From d39348d7bd1dbe13a2954835466c2242acf46014 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 9 Aug 2020 11:49:04 -0400 Subject: [PATCH 01/71] Initial changes to support multiple versions for code system. --- .../context/support/IValidationSupport.java | 31 +++++ .../ca/uhn/fhir/jpa/api/config/DaoConfig.java | 35 +++++ .../api/dao/IFhirResourceDaoCodeSystem.java | 5 + .../jpa/dao/FhirResourceDaoValueSetDstu2.java | 12 ++ .../dao/data/ITermCodeSystemVersionDao.java | 10 ++ .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 22 +++- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 22 +++- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 18 ++- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 22 +++- .../jpa/entity/TermCodeSystemVersion.java | 8 +- ...aseJpaResourceProviderCodeSystemDstu3.java | 3 +- .../BaseJpaResourceProviderCodeSystemR4.java | 3 +- .../BaseJpaResourceProviderCodeSystemR5.java | 3 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 122 +++++++++++------- .../term/TermCodeSystemStorageSvcImpl.java | 64 +++++---- .../uhn/fhir/jpa/term/TermLoaderSvcImpl.java | 4 + .../uhn/fhir/jpa/term/TermReadSvcDstu3.java | 7 +- .../ca/uhn/fhir/jpa/term/TermReadSvcR4.java | 7 +- .../uhn/fhir/jpa/term/api/ITermReadSvc.java | 4 + .../term/loinc/LoincUploadPropertiesEnum.java | 3 + .../jpa/term/loinc/loincupload.properties | 4 + .../FhirResourceDaoDstu3TerminologyTest.java | 4 +- .../r4/FhirResourceDaoR4TerminologyTest.java | 2 +- .../r4/ResourceProviderR4ValueSetTest.java | 46 +++++++ .../term/TermCodeSystemStorageSvcTest.java | 27 +++- .../term/TerminologyLoaderSvcLoincTest.java | 30 ++++- .../jpa/term/TerminologySvcDeltaR4Test.java | 2 +- .../jpa/term/TerminologySvcImplR4Test.java | 114 ++++++++++++++++ .../loinc/v267_loincupload.properties | 87 +++++++++++++ .../loinc/v268_loincupload.properties | 87 +++++++++++++ .../support/BaseValidationSupportWrapper.java | 5 + .../support/CachingValidationSupport.java | 6 + .../CommonCodeSystemsTerminologyService.java | 6 + ...oryTerminologyServerValidationSupport.java | 9 +- .../support/ValidationSupportChain.java | 10 ++ 35 files changed, 742 insertions(+), 102 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index 49739e62b1e..a1efd061a7c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -183,6 +183,24 @@ public interface IValidationSupport { return null; } + /** + * Validates that the given code exists and if possible returns a display + * name. This method is called to check codes which are found in "example" + * binding fields (e.g. Observation.code in the default profile. + * + * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to + * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. + * @param theOptions Provides options controlling the validation + * @param theCodeSystem The code system, e.g. "http://loinc.org" + * @param theCode The code, e.g. "1234-5" + * @param theDisplay The display name, if it should also be validated + * @param theSystemVersion The code system version. + * @return Returns a validation result object + */ + default CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, String theSystemVersion) { + return null; + } + /** * Validates that the given code exists and if possible returns a display * name. This method is called to check codes which are found in "example" @@ -212,6 +230,19 @@ public interface IValidationSupport { return null; } + /** + * Look up a code using the system, system version and code value + * + * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to + * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. + * @param theSystem The CodeSystem URL + * @param theCode The code + * @param theSystemVersion The CodeSystem version + */ + default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theSystemVersion) { + return null; + } + /** * Returns true if the given valueset can be validated by the given * validation support module diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java index 98f49f6ff2d..85b6bb6b8ca 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java @@ -227,6 +227,11 @@ public class DaoConfig { */ private boolean myPreloadBlobFromInputStream = false; + /** + * @since 5.1.0 + */ + private boolean myMultipleCodeSystemVersionsEnabled = false; + /** * Constructor */ @@ -2126,4 +2131,34 @@ public class DaoConfig { myPreloadBlobFromInputStream = thePreloadBlobFromInputStream; } + /** + *

+ * This determines whether multiple code system versions will be enabled. If not enabled, existing code systems will be + * deleted when a new version is uploaded. + *

+ *

+ * The default value for this setting is {@code false}. + *

+ * + * @since 5.1.0 + */ + public boolean isMultipleCodeSystemVersionsEnabled() { + return myMultipleCodeSystemVersionsEnabled; + } + + /** + *

+ * This determines whether multiple code system versions will be enabled. If not enabled, existing code systems will be + * deleted when a new version is uploaded. + *

+ *

+ * The default value for this setting is {@code false}. + *

+ * + * @since 5.1.0 + */ + public void setMultipleCodeSystemVersionsEnabled(Boolean theMultipleCodeSystemVersionsEnabled) { + myMultipleCodeSystemVersionsEnabled = theMultipleCodeSystemVersionsEnabled; + } + } diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index ba957e00542..2c00c792b49 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -42,6 +42,11 @@ public interface IFhirResourceDaoCodeSystem ext SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, RequestDetails theRequestDetails); + @Nonnull + IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CD theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails); + + SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails); + class SubsumesResult { private final ConceptSubsumptionOutcome myOutcome; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index ec2b7bd27e6..c43d3e3d9c2 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 @@ -48,6 +48,7 @@ import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -235,6 +236,12 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, CodingDt theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); + } + @Override public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CodingDt theCoding, RequestDetails theRequest) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); @@ -280,6 +287,11 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CodingDt theCodingA, CodingDt theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); + } + @Override @PostConstruct public void postConstruct() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java index 92af07eaef9..fc55636836f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java @@ -35,9 +35,19 @@ public interface ITermCodeSystemVersionDao extends JpaRepository findByCodeSystemPid(@Param("codesystem_pid") Long theCodeSystemPid); + @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemPid = :codesystem_pid AND cs.myCodeSystemVersionId = :codesystem_version_id") + TermCodeSystemVersion findByCodeSystemPidAndVersion(@Param("codesystem_pid") Long theCodeSystemPid, @Param("codesystem_version_id") String theCodeSystemVersionId); + + @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemPid = :codesystem_pid AND cs.myCodeSystemVersionId IS NULL") + TermCodeSystemVersion findByCodeSystemPidVersionIsNull(@Param("codesystem_pid") Long theCodeSystemPid); + @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myResourcePid = :resource_id") List findByCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); 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 0fbd43ac94f..6d2cd77aa99 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 @@ -44,6 +44,7 @@ import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; @@ -86,9 +87,15 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { + return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); + } + + @Nonnull + @Override + public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -109,12 +116,16 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { + return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion); + } + @Override public SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); 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 f2c7f221e30..a7f91e3c1b0 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 @@ -44,6 +44,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; @@ -81,9 +82,15 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { + return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); + } + + @Nonnull + @Override + public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -104,13 +111,17 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { + return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion); + } + @Override public SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java index c2170f8ac7a..7a104f1ba97 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java @@ -113,14 +113,17 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao ValueSet source = new ValueSet(); source.setUrl(theUri); - source.getCompose().addInclude().addValueSet(theUri); +// source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { - ConceptSetComponent include = source.getCompose().addInclude(); - ConceptSetFilterComponent filter = include.addFilter(); +// ConceptSetComponent include = source.getCompose().addInclude(); +// ConceptSetFilterComponent filter = include.addFilter(); + ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter(); filter.setProperty("display"); filter.setOp(FilterOperator.EQUAL); filter.setValue(theFilter); + } else { + source.getCompose().addInclude().addValueSet(theUri); } ValueSet retVal = doExpand(source); @@ -148,14 +151,17 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao ValueSet source = new ValueSet(); source.setUrl(theUri); - source.getCompose().addInclude().addValueSet(theUri); +// source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { - ConceptSetComponent include = source.getCompose().addInclude(); - ConceptSetFilterComponent filter = include.addFilter(); +// ConceptSetComponent include = source.getCompose().addInclude(); +// ConceptSetFilterComponent filter = include.addFilter(); + ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter(); filter.setProperty("display"); filter.setOp(FilterOperator.EQUAL); filter.setValue(theFilter); + } else { + source.getCompose().addInclude().addValueSet(theUri); } ValueSet retVal = doExpand(source, theOffset, theCount); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index 0cc481ae32c..329cf00eb40 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -45,6 +45,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; @@ -83,9 +84,15 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { + return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); + } + + @Nonnull + @Override + public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -106,13 +113,17 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { + return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion); + } + @Override public SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index 5b16f5811e9..04444c47f3f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -40,17 +40,21 @@ import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.SequenceGenerator; import javax.persistence.Table; +import javax.persistence.UniqueConstraint; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import static org.apache.commons.lang3.StringUtils.length; -@Table(name = "TRM_CODESYSTEM_VER" +@Table(name = "TRM_CODESYSTEM_VER", // Note, we used to have a constraint named IDX_CSV_RESOURCEPID_AND_VER (don't reuse this) -) + uniqueConstraints = { + @UniqueConstraint(name = TermCodeSystemVersion.IDX_CODESYSTEM_AND_VER, columnNames = {"CODESYSTEM_PID", "CS_VERSION_ID"}) +}) @Entity() public class TermCodeSystemVersion implements Serializable { + public static final String IDX_CODESYSTEM_AND_VER = "IDX_CODESYSTEM_AND_VER"; public static final int MAX_VERSION_LENGTH = 200; private static final long serialVersionUID = 1L; @OneToMany(fetch = FetchType.LAZY, mappedBy = "myCodeSystem") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 2fcee77131e..a6d061c3e06 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -53,6 +53,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, @OperationParam(name = "coding", min = 0, max = 1) Coding theCoding, + @OperationParam(name="version", min=0, max=1) StringType theVersion, @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, RequestDetails theRequestDetails ) { @@ -60,7 +61,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); result.throwNotFoundIfAppropriate(); return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } catch (FHIRException e) { 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 2c69bdbf54c..dec2d1adbcc 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 @@ -55,6 +55,7 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 theProperties, RequestDetails theRequestDetails ) { @@ -62,7 +63,7 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); result.throwNotFoundIfAppropriate(); return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } finally { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 38545e7d5d7..45d80b0228f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -55,6 +55,7 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 theProperties, RequestDetails theRequestDetails ) { @@ -62,7 +63,7 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); result.throwNotFoundIfAppropriate(); return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } finally { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index b03dc4aec20..69e1113fb09 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -244,7 +244,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { - TermCodeSystemVersion cs = getCurrentCodeSystemVersion(theSystem); + TermCodeSystemVersion cs = getCurrentCodeSystemVersionForVersion(theSystem,null); return cs != null; } @@ -616,6 +616,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { private Boolean expandValueSetHandleIncludeOrExclude(@Nullable ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull) { String system = theIncludeOrExclude.getSystem(); + String systemVersion = theIncludeOrExclude.getVersion(); boolean hasSystem = isNotBlank(system); boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0; @@ -630,7 +631,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); if (cs != null) { - return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs); + return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs, systemVersion); } else { @@ -671,7 +672,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { nextSystem = system; } - LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode()); + LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode(), systemVersion); if (lookup != null && lookup.isFound()) { addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, nextSystem, next.getCode(), lookup.getCodeDisplay()); foundCount++; @@ -765,7 +766,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nonnull - private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs) { + private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs, String theSystemVersion) { TermCodeSystemVersion csv = theCs.getCurrentVersion(); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -793,7 +794,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Filters */ - handleFilters(bool, theSystem, qb, theIncludeOrExclude); + handleFilters(bool, theSystem, qb, theIncludeOrExclude, theSystemVersion); Query luceneQuery = bool.createQuery(); @@ -902,15 +903,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void handleFilters(BooleanJunction theBool, String theSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { + private void handleFilters(BooleanJunction theBool, String theSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude, String theSystemVersion) { if (theIncludeOrExclude.getFilter().size() > 0) { for (ValueSet.ConceptSetFilterComponent nextFilter : theIncludeOrExclude.getFilter()) { - handleFilter(theSystem, theQb, theBool, nextFilter); + handleFilter(theSystem, theQb, theBool, nextFilter, theSystemVersion); } } } - private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { if (isBlank(theFilter.getValue()) && theFilter.getOp() == null && isBlank(theFilter.getProperty())) { return; } @@ -926,7 +927,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "concept": case "code": - handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter); + handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter, theSystemVersion); break; case "parent": case "child": @@ -935,11 +936,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "ancestor": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestor(theSystem, theQb, theBool, theFilter); + handleFilterLoincAncestor(theSystem, theQb, theBool, theFilter, theSystemVersion); break; case "descendant": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincDescendant(theSystem, theQb, theBool, theFilter); + handleFilterLoincDescendant(theSystem, theQb, theBool, theFilter, theSystemVersion); break; case "copyright": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); @@ -990,8 +991,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { bool.must(textQuery); } - private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - TermConcept code = findCode(theSystem, theFilter.getValue()) + private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + TermConcept code = findCode(theSystem, theFilter.getValue(), theSystemVersion) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theFilter.getValue())); if (theFilter.getOp() == ValueSet.FilterOperator.ISA) { @@ -1036,41 +1037,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincAncestor(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincAncestor(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter); + addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter, theSystemVersion); break; case IN: - addLoincFilterAncestorIn(theSystem, theQb, theBool, theFilter); + addLoincFilterAncestorIn(theSystem, theQb, theBool, theFilter, theSystemVersion); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter.getProperty(), theFilter.getValue()); + private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter.getProperty(), theFilter.getValue(), theSystemVersion); } - private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, String theProperty, String theValue) { - List terms = getAncestorTerms(theSystem, theProperty, theValue); + private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, String theProperty, String theValue, String theSystemVersion) { + List terms = getAncestorTerms(theSystem, theProperty, theValue, theSystemVersion); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value)); + terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value, theSystemVersion)); } theBool.must(new TermsQuery(terms)); } - private List getAncestorTerms(String theSystem, String theProperty, String theValue) { + private List getAncestorTerms(String theSystem, String theProperty, String theValue, String theSystemVersion) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue) + TermConcept code = findCode(theSystem, theValue, theSystemVersion) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); retVal.add(new Term("myParentPids", "" + code.getId())); @@ -1080,41 +1081,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterDescendantEqual(theSystem, theBool, theFilter); + addLoincFilterDescendantEqual(theSystem, theBool, theFilter, theSystemVersion); break; case IN: - addLoincFilterDescendantIn(theSystem, theQb, theBool, theFilter); + addLoincFilterDescendantIn(theSystem, theQb, theBool, theFilter, theSystemVersion); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue(), theSystemVersion); } - private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { - List terms = getDescendantTerms(theSystem, theProperty, theValue); + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue, String theSystemVersion) { + List terms = getDescendantTerms(theSystem, theProperty, theValue, theSystemVersion); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterDescendantIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterDescendantIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value)); + terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value, theSystemVersion)); } theBool.must(new TermsQuery(terms)); } - private List getDescendantTerms(String theSystem, String theProperty, String theValue) { + private List getDescendantTerms(String theSystem, String theProperty, String theValue, String theSystemVersion) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue) + TermConcept code = findCode(theSystem, theValue, theSystemVersion) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); String[] parentPids = code.getParentPidsAsString().split(" "); @@ -1363,6 +1364,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override public Optional findCode(String theCodeSystem, String theCode) { + return findCode(theCodeSystem, theCode, null); + + } + + @Override + public Optional findCode(String theCodeSystem, String theCode, String theVersion) { /* * Loading concepts without a transaction causes issues later on some * platforms (e.g. PSQL) so this transactiontemplate is here to make @@ -1371,7 +1378,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY); return txTemplate.execute(t -> { - TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem); + TermCodeSystemVersion csv = getCurrentCodeSystemVersionForVersion(theCodeSystem, theVersion); if (csv == null) { return null; } @@ -1380,12 +1387,20 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nullable - private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri) { - TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(theUri, uri -> myTxTemplate.execute(tx -> { + private TermCodeSystemVersion getCurrentCodeSystemVersionForVersion(String theUri, String theVersion) { + StringBuffer key = new StringBuffer(theUri); + if (theVersion != null) { + key.append("_").append(theVersion); + } + TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), uri -> myTxTemplate.execute(tx -> { TermCodeSystemVersion csv = null; TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(uri); - if (cs != null && cs.getCurrentVersion() != null) { - csv = cs.getCurrentVersion(); + if (cs != null) { + if (theVersion != null) { + csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(cs.getPid(), theVersion); + } else if (cs.getCurrentVersion() != null) { + csv = cs.getCurrentVersion(); + } } if (csv != null) { return csv; @@ -1804,19 +1819,28 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - @Transactional public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { + return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null); + } + + @Override + @Transactional + public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion) { VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); + String systemVersion = null; + if (theSystemVersion != null) { + systemVersion = theSystemVersion.getValue(); + } if (!StringUtils.equals(conceptA.getSystem(), conceptB.getSystem())) { throw new InvalidRequestException("Unable to test subsumption across different code systems"); } - TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode()) + TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), systemVersion) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptA)); - TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode()) + TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), systemVersion) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptB)); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -1835,10 +1859,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { protected abstract ValueSet toCanonicalValueSet(IBaseResource theValueSet); - protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode) { + protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode, String theVersion) { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); return txTemplate.execute(t -> { - Optional codeOpt = findCode(theSystem, theCode); + Optional codeOpt = findCode(theSystem, theCode, theVersion); if (codeOpt.isPresent()) { TermConcept code = codeOpt.get(); @@ -2107,10 +2131,14 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return null; } - @CoverageIgnore @Override public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { + return validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl, null); + } + + @Override + public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, String theSystemVersion) { invokeRunnableForUnitTest(); if (isNotBlank(theValueSetUrl)) { @@ -2119,7 +2147,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - Optional codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode()))); + Optional codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode, theSystemVersion).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode()))); if (codeOpt != null && codeOpt.isPresent()) { VersionIndependentConcept code = codeOpt.get(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 6775fd6f2f0..13683722d6b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -284,16 +284,19 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { ResourcePersistentId codeSystemResourcePid = getCodeSystemResourcePid(theCodeSystem.getIdElement()); /* - * If this is a not-present codesystem, we don't want to store a new version if one - * already exists, since that will wipe out the existing concepts. We do create or update - * the TermCodeSystem table though, since that allows the DB to reject changes - * that would result in duplicate CodeSysten.url values. + * If this is a not-present codesystem and codesystem version already exists, we don't want to + * overwrite the existing version since that will wipe out the existing concepts. We do create + * or update the TermCodeSystem table though, since that allows the DB to reject changes that would + * result in duplicate CodeSystem.url values. */ if (theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) { - TermCodeSystem codeSystem = myCodeSystemDao.findByCodeSystemUri(theCodeSystem.getUrl()); - if (codeSystem != null) { - getOrCreateTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theResourceEntity); - return; + TermCodeSystem termCodeSystem = myCodeSystemDao.findByCodeSystemUri(theCodeSystem.getUrl()); + if (termCodeSystem != null) { + TermCodeSystemVersion codeSystemVersion = getExistingTermCodeSystemVersion(termCodeSystem.getPid(), theCodeSystem.getVersion()); + if (codeSystemVersion != null) { + getOrCreateTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theResourceEntity); + return; + } } } @@ -338,22 +341,23 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied"); ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystemUri, "No system URI supplied"); - // Grab the existing versions so we can delete them later - List existing = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourcePid.getIdAsLong()); - - /* - * For now we always delete old versions. At some point it would be nice to allow configuration to keep old versions. - */ - - for (TermCodeSystemVersion next : existing) { - ourLog.info("Deleting old code system version {}", next.getPid()); - Long codeSystemVersionPid = next.getPid(); - deleteCodeSystemVersion(codeSystemVersionPid); + // Grab the existing version so we can delete it + TermCodeSystem existingCodeSystem = myCodeSystemDao.findByCodeSystemUri(theSystemUri); + TermCodeSystemVersion existing = null; + if (existingCodeSystem != null) { + existing = getExistingTermCodeSystemVersion(existingCodeSystem.getPid(), theSystemVersionId); } - ourLog.debug("Flushing..."); - myConceptDao.flush(); - ourLog.debug("Done flushing"); + /* + * Delete version being replaced. + */ + + if(existing != null) { + ourLog.info("Deleting old code system version {}", existing.getPid()); + Long codeSystemVersionPid = existing.getPid(); + deleteCodeSystemVersion(codeSystemVersionPid); + + } /* * Do the upload @@ -408,6 +412,17 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { } } + private TermCodeSystemVersion getExistingTermCodeSystemVersion(Long theCodeSystemVersionPid, String theCodeSystemVersion) { + TermCodeSystemVersion existing; + if (theCodeSystemVersion == null) { + existing = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystemVersionPid); + } else { + existing = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystemVersionPid, theCodeSystemVersion); + } + + return existing; + } + private void deleteCodeSystemVersion(final Long theCodeSystemVersionPid) { ourLog.info(" * Deleting code system version {}", theCodeSystemVersionPid); @@ -457,8 +472,11 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { myCodeSystemDao.save(codeSystem); } + myConceptDao.flush(); + ourLog.info(" * Deleting code system version"); - myCodeSystemVersionDao.deleteById(theCodeSystemVersionPid); + myCodeSystemVersionDao.delete(theCodeSystemVersionPid); + myCodeSystemVersionDao.flush(); }); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index 11401f845a1..3fe945458a5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -378,6 +378,10 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { try { String loincCsString = IOUtils.toString(BaseTermReadSvcImpl.class.getResourceAsStream("/ca/uhn/fhir/jpa/term/loinc/loinc.xml"), Charsets.UTF_8); loincCs = FhirContext.forR4().newXmlParser().parseResource(CodeSystem.class, loincCsString); + String codeSystemVersionId = theUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (codeSystemVersionId != null) { + loincCs.setVersion(codeSystemVersionId); + } } catch (IOException e) { throw new InternalErrorException("Failed to load loinc.xml", e); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java index 6b266ae52f3..0056824d586 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java @@ -149,7 +149,12 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { - return super.lookupCode(theSystem, theCode); + return super.lookupCode(theSystem, theCode, null); + } + + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + return super.lookupCode(theSystem, theCode, theVersion); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java index 2075725dc2f..96243af965a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java @@ -89,9 +89,14 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4 return (CodeSystem) theCodeSystem; } + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + return super.lookupCode(theSystem, theCode, theVersion); + } + @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { - return super.lookupCode(theSystem, theCode); + return super.lookupCode(theSystem, theCode, null); } @Override 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..9e6e340e5bd 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 @@ -73,6 +73,8 @@ public interface ITermReadSvc extends IValidationSupport { List expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet); + Optional findCode(String theCodeSystem, String theCode, String theVersion); + Optional findCode(String theCodeSystem, String theCode); Set findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode); @@ -103,6 +105,8 @@ public interface ITermReadSvc extends IValidationSupport { IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB); + IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion); + void preExpandDeferredValueSetsToTerminologyTables(); /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java index d1d1295c9e2..225601034b7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java @@ -96,6 +96,9 @@ public enum LoincUploadPropertiesEnum { /* * OPTIONAL */ + // This is the version identifier for the answer list file + LOINC_CODESYSTEM_VERSION("loinc.codesystem.version"), + // This is the version identifier for the answer list file LOINC_ANSWERLIST_VERSION("loinc.answerlist.version"), diff --git a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties index 986d2afdaa0..cb7d1eb0345 100644 --- a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties +++ b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties @@ -61,6 +61,10 @@ loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersVa ### OPTIONAL ### ################ +# This is the version identifier for the LOINC code system +## Key may be omitted if only a single version of LOINC is being kept. +loinc.codesystem.version=2.68 + # This is the version identifier for the answer list file ## Key may be omitted loinc.answerlist.version=Beta.1 diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index f8e15870a2f..d34393b7b61 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -521,7 +521,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { myTerminologyDeferredStorageSvc.saveDeferred(); myTerminologyDeferredStorageSvc.saveDeferred(); - IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM),null, mySrd); + IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM), null,null, mySrd); assertEquals(true, lookupResults.isFound()); ValueSet vs = new ValueSet(); @@ -711,7 +711,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); - IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); + IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null,null, mySrd); assertEquals(true, outcome.isFound()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index b73ced860d1..63b88299e1b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -828,7 +828,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); - IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); + IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null,null, mySrd); assertEquals(true, outcome.isFound()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 7d6767f1aa4..6196d2d226f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -16,6 +16,7 @@ import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -415,6 +416,51 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } + @Test + public void testExpandByValueSetWithFilter() throws IOException { + loadAndPersistCodeSystem(); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("blood")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithFilter() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test public void testExpandByValueSetWithPreExpansion() throws IOException { myDaoConfig.setPreExpandValueSets(true); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java index 8c6f1fc8805..cd7ddf2265f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java @@ -13,7 +13,7 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; @Test - public void testStoreNewCodeSystemVersionForExistingCodeSystem() { + public void testStoreNewCodeSystemVersionForExistingCodeSystemNoVersionId() { CodeSystem upload = createCodeSystemWithMoreThan100Concepts(); // Create CodeSystem resource @@ -34,15 +34,38 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { assertEquals(125, myTermConceptDao.count()); } + @Test + public void testStoreNewCodeSystemVersionForExistingCodeSystemVersionId() { + CodeSystem upload = createCodeSystemWithMoreThan100Concepts(); + upload.setVersion("1"); + + // Create CodeSystem resource + ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(upload, mySrd).getEntity(); + + // Update the CodeSystem resource + runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity)); + + /* + Because there are more than 100 concepts in the code system, the first 100 will be persisted immediately and + the remaining 25 concepts will be queued up for "deferred save". + + As the CodeSystem was persisted twice, the extra 25 term concepts will be queued twice, each with a different + CodeSystem version PID. Only one set of the term concepts should be persisted (i.e. 125 term concepts in total). + */ + myTerminologyDeferredStorageSvc.setProcessDeferred(true); + myTerminologyDeferredStorageSvc.saveDeferred(); + assertEquals(125, myTermConceptDao.count()); + } + private CodeSystem createCodeSystemWithMoreThan100Concepts() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); for (int i = 0; i < 125; i++) { codeSystem.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeA " + i))); } + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); return codeSystem; } 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 c41a29eddb6..df83c976ec7 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 @@ -17,12 +17,10 @@ import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsUsHandler; import ca.uhn.fhir.jpa.term.loinc.LoincUniversalOrderSetHandler; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.ValueSet; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -342,6 +340,28 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { assertEquals("13006-2", vs.getCompose().getInclude().get(0).getConcept().get(1).getCode()); } + @Test + public void testLoadLoincMultipleVersions() throws IOException { + + // Load LOINC marked as version 2.67 + addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v267_loincupload.properties"); + mySvc.loadLoinc(myFiles.getFiles(), mySrd); + + verify(myTermCodeSystemStorageSvc, times(1)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); + CodeSystem loincCS = mySystemCaptor.getValue(); + assertEquals("2.67", loincCS.getVersion()); + + // Load LOINC marked as version 2.68 + myFiles = new ZipCollectionBuilder(); + addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v268_loincupload.properties"); + mySvc.loadLoinc(myFiles.getFiles(), mySrd); + + verify(myTermCodeSystemStorageSvc, times(2)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); + loincCS = mySystemCaptor.getValue(); + assertEquals("2.68", loincCS.getVersion()); + + } + @Test @Disabled public void testLoadLoincMandatoryFilesOnly() throws IOException { @@ -382,7 +402,11 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { } public static void addLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles) throws IOException { - theFiles.addFileZip("/loinc/", LOINC_UPLOAD_PROPERTIES_FILE.getCode()); + addLoincMandatoryFilesWithPropertiesFileToZip(theFiles, LOINC_UPLOAD_PROPERTIES_FILE.getCode()); + } + + public static void addLoincMandatoryFilesWithPropertiesFileToZip(ZipCollectionBuilder theFiles, String thePropertiesFile) throws IOException { + theFiles.addFileZip("/loinc/", thePropertiesFile); theFiles.addFileZip("/loinc/", LOINC_GROUP_FILE_DEFAULT.getCode()); theFiles.addFileZip("/loinc/", LOINC_GROUP_TERMS_FILE_DEFAULT.getCode()); theFiles.addFileZip("/loinc/", LOINC_PARENT_GROUP_FILE_DEFAULT.getCode()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index 265e2064761..db601c7a330 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -184,7 +184,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { assertEquals(2, outcome.getUpdatedConceptCount()); runInTransaction(() -> { - TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA").orElseThrow(() -> new IllegalStateException()); + TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA", null).orElseThrow(() -> new IllegalStateException()); assertEquals(2, concept.getParents().size()); assertThat(concept.getParentPidsAsString(), matchesPattern("^[0-9]+ [0-9]+$")); }); 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 d0ff1cebb37..51b817bbb34 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 @@ -4,11 +4,15 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.model.TranslationRequest; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptMap; import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.model.dstu2.valueset.ContentTypeEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CanonicalType; @@ -30,9 +34,14 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -1880,4 +1889,109 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { assertTrue(result.isOk()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); } + + @Test + public void testCreateCodeSystemTwoVersions() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(CS_URL); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A"); + codeSystem + .addConcept().setCode("B").setDisplay("Code A"); + + codeSystem.setVersion("1"); + + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + Set codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A"); + assertThat(toCodes(codes), containsInAnyOrder("A")); + + codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "B"); + assertThat(toCodes(codes), containsInAnyOrder("B")); + + runInTransaction(() -> { + List termCodeSystemVersions = myTermCodeSystemVersionDao.findAll(); + assertEquals(termCodeSystemVersions.size(), 1); + TermCodeSystemVersion termCodeSystemVersion_1 = termCodeSystemVersions.get(0); + assertEquals(termCodeSystemVersion_1.getConcepts().size(), 2); + Set termConcepts = new HashSet<>(termCodeSystemVersion_1.getConcepts()); + assertThat(toCodes(termConcepts), containsInAnyOrder("A", "B")); + + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); + assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); + + }); + + codeSystem.setVersion("2"); + codeSystem + .addConcept().setCode("C").setDisplay("Code C"); + + myCodeSystemDao.update(codeSystem, mySrd).getId().toUnqualified(); + codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "C"); + assertThat(toCodes(codes), containsInAnyOrder("C")); + + runInTransaction(() -> { + List termCodeSystemVersions_updated = myTermCodeSystemVersionDao.findAll(); + assertEquals(termCodeSystemVersions_updated.size(), 2); + TermCodeSystemVersion termCodeSystemVersion_2 = termCodeSystemVersions_updated.get(1); + assertEquals(termCodeSystemVersion_2.getConcepts().size(), 3); + Set termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts()); + assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B", "C")); + + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); + assertEquals("2", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); + }); + } + + @Test + public void testUpdateCodeSystemContentModeNotPresent() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(CS_URL); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A"); + codeSystem + .addConcept().setCode("B").setDisplay("Code A"); + + codeSystem.setVersion("1"); + + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + Set codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A"); + assertThat(toCodes(codes), containsInAnyOrder("A")); + + codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "B"); + assertThat(toCodes(codes), containsInAnyOrder("B")); + + runInTransaction(() -> { + List termCodeSystemVersions = myTermCodeSystemVersionDao.findAll(); + assertEquals(termCodeSystemVersions.size(), 1); + TermCodeSystemVersion termCodeSystemVersion_1 = termCodeSystemVersions.get(0); + assertEquals(termCodeSystemVersion_1.getConcepts().size(), 2); + Set termConcepts = new HashSet<>(termCodeSystemVersion_1.getConcepts()); + assertThat(toCodes(termConcepts), containsInAnyOrder("A", "B")); + + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); + assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); + + }); + + // Remove concepts and changed ContentMode to NOTPRESENT + codeSystem.setConcept(new ArrayList<>()); + codeSystem.setContent((CodeSystem.CodeSystemContentMode.NOTPRESENT)); + + myCodeSystemDao.update(codeSystem, mySrd).getId().toUnqualified(); + runInTransaction(() -> { + List termCodeSystemVersions_updated = myTermCodeSystemVersionDao.findAll(); + assertEquals(termCodeSystemVersions_updated.size(), 1); + TermCodeSystemVersion termCodeSystemVersion_2 = termCodeSystemVersions_updated.get(0); + assertEquals(termCodeSystemVersion_2.getConcepts().size(), 2); + Set termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts()); + assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B")); + + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); + assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); + }); + } } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties new file mode 100644 index 00000000000..f9a049fdf06 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties @@ -0,0 +1,87 @@ +################# +### MANDATORY ### +################# + +# Answer lists (ValueSets of potential answers/values for LOINC "questions") +## File must be present +loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv +# Answer list links (connects LOINC observation codes to answer list codes) +## File must be present +loinc.answerlist.link.file=AccessoryFiles/AnswerFile/LoincAnswerListLink.csv + +# Document ontology +## File must be present +loinc.document.ontology.file=AccessoryFiles/DocumentOntology/DocumentOntology.csv + +# LOINC codes +## File must be present +loinc.file=LoincTable/Loinc.csv + +# LOINC hierarchy +## File must be present +loinc.hierarchy.file=AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv + +# IEEE medical device codes +## File must be present +loinc.ieee.medical.device.code.mapping.table.file=AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv + +# Imaging document codes +## File must be present +loinc.imaging.document.codes.file=AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv + +# Part +## File must be present +loinc.part.file=AccessoryFiles/PartFile/Part.csv + +# Part link +## File must be present +loinc.part.link.primary.file=AccessoryFiles/PartFile/LoincPartLink_Primary.csv +loinc.part.link.supplementary.file=AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv + +# Part related code mapping +## File must be present +loinc.part.related.code.mapping.file=AccessoryFiles/PartFile/PartRelatedCodeMapping.csv + +# RSNA playbook +## File must be present +loinc.rsna.playbook.file=AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv + +# Top 2000 codes - SI +## File must be present +loinc.top2000.common.lab.results.si.file=AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv +# Top 2000 codes - US +## File must be present +loinc.top2000.common.lab.results.us.file=AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv + +# Universal lab order ValueSet +## File must be present +loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv + +################ +### OPTIONAL ### +################ + +# This is the version identifier for the LOINC code system +## Key may be omitted if only a single version of LOINC is being kept. +loinc.codesystem.version=2.67 + +# This is the version identifier for the answer list file +## Key may be omitted +loinc.answerlist.version=Beta.1 + +# This is the version identifier for uploaded ConceptMap resources +## Key may be omitted +loinc.conceptmap.version=Beta.1 + +# Group +## Default value if key not provided: AccessoryFiles/GroupFile/Group.csv +## File may be omitted +loinc.group.file=AccessoryFiles/GroupFile/Group.csv +# Group terms +## Default value if key not provided: AccessoryFiles/GroupFile/GroupLoincTerms.csv +## File may be omitted +loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv +# Parent group +## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv +## File may be omitted +loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties new file mode 100644 index 00000000000..7f4854293de --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties @@ -0,0 +1,87 @@ +################# +### MANDATORY ### +################# + +# This is the version identifier for the LOINC code system +## Key may be omitted if only a single version of LOINC is being kept. +loinc.codesystem.version=2.68 + +# Answer lists (ValueSets of potential answers/values for LOINC "questions") +## File must be present +loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv +# Answer list links (connects LOINC observation codes to answer list codes) +## File must be present +loinc.answerlist.link.file=AccessoryFiles/AnswerFile/LoincAnswerListLink.csv + +# Document ontology +## File must be present +loinc.document.ontology.file=AccessoryFiles/DocumentOntology/DocumentOntology.csv + +# LOINC codes +## File must be present +loinc.file=LoincTable/Loinc.csv + +# LOINC hierarchy +## File must be present +loinc.hierarchy.file=AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv + +# IEEE medical device codes +## File must be present +loinc.ieee.medical.device.code.mapping.table.file=AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv + +# Imaging document codes +## File must be present +loinc.imaging.document.codes.file=AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv + +# Part +## File must be present +loinc.part.file=AccessoryFiles/PartFile/Part.csv + +# Part link +## File must be present +loinc.part.link.primary.file=AccessoryFiles/PartFile/LoincPartLink_Primary.csv +loinc.part.link.supplementary.file=AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv + +# Part related code mapping +## File must be present +loinc.part.related.code.mapping.file=AccessoryFiles/PartFile/PartRelatedCodeMapping.csv + +# RSNA playbook +## File must be present +loinc.rsna.playbook.file=AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv + +# Top 2000 codes - SI +## File must be present +loinc.top2000.common.lab.results.si.file=AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv +# Top 2000 codes - US +## File must be present +loinc.top2000.common.lab.results.us.file=AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv + +# Universal lab order ValueSet +## File must be present +loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv + +################ +### OPTIONAL ### +################ + +# This is the version identifier for the answer list file +## Key may be omitted +loinc.answerlist.version=Beta.1 + +# This is the version identifier for uploaded ConceptMap resources +## Key may be omitted +loinc.conceptmap.version=Beta.1 + +# Group +## Default value if key not provided: AccessoryFiles/GroupFile/Group.csv +## File may be omitted +loinc.group.file=AccessoryFiles/GroupFile/Group.csv +# Group terms +## Default value if key not provided: AccessoryFiles/GroupFile/GroupLoincTerms.csv +## File may be omitted +loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv +# Parent group +## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv +## File may be omitted +loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java index cc04a447d10..3c6c10f9fb3 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java @@ -58,6 +58,11 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport { return myWrap.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl); } + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion); + } + @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java index 829b9977ad7..53c15009f11 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java @@ -83,6 +83,12 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple return loadFromCache(myValidateCodeCache, key, t -> super.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl)); } + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + String key = "lookupCode " + theSystem + " " + theCode + " " + theVersion; + return loadFromCache(myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion)); + } + @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { String key = "lookupCode " + theSystem + " " + theCode; 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 b447c39e160..51dfe5dd86b 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 @@ -145,6 +145,12 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { } + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + // For now Common Code Systems will not be versioned. + return lookupCode(theValidationSupportContext, theSystem, theCode); + } + @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 3fca6d034ac..dc1c3576c14 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -308,6 +308,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu .setMessage(message); } + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + // TODO: Add support for validating versioned codes as well. + return lookupCode(theValidationSupportContext, theSystem, theCode); + } + @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { return validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode); @@ -470,6 +476,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu List nextCodeList = new ArrayList<>(); String system = nextInclude.getSystem(); + String systemVersion = nextInclude.getVersion(); if (isNotBlank(system)) { if (theWantSystem != null && !theWantSystem.equals(system)) { @@ -492,7 +499,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu if (theWantCode != null) { if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, system)) { - LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode); + LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode, systemVersion); if (lookup != null && lookup.isFound()) { CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() .addConcept() diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index 841038fd4b7..ac7da03cd07 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -257,6 +257,16 @@ public class ValidationSupportChain implements IValidationSupport { return null; } + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { + for (IValidationSupport next : myChain) { + if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) { + return next.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion); + } + } + return null; + } + @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { for (IValidationSupport next : myChain) { From 4db00fa604579abe1b4b64695c6ffda6a1da25c4 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Mon, 17 Aug 2020 18:14:37 -0400 Subject: [PATCH 02/71] Fix broken test and cleanup. --- .../ca/uhn/fhir/jpa/api/config/DaoConfig.java | 35 ------------------- .../jpa/dao/FhirResourceDaoValueSetDstu2.java | 3 -- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 5 ++- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 5 ++- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 5 ++- .../uhn/fhir/jpa/term/TermLoaderSvcImpl.java | 2 +- .../FhirInstanceValidatorR4Test.java | 2 +- 7 files changed, 8 insertions(+), 49 deletions(-) diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java index 85b6bb6b8ca..98f49f6ff2d 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java @@ -227,11 +227,6 @@ public class DaoConfig { */ private boolean myPreloadBlobFromInputStream = false; - /** - * @since 5.1.0 - */ - private boolean myMultipleCodeSystemVersionsEnabled = false; - /** * Constructor */ @@ -2131,34 +2126,4 @@ public class DaoConfig { myPreloadBlobFromInputStream = thePreloadBlobFromInputStream; } - /** - *

- * This determines whether multiple code system versions will be enabled. If not enabled, existing code systems will be - * deleted when a new version is uploaded. - *

- *

- * The default value for this setting is {@code false}. - *

- * - * @since 5.1.0 - */ - public boolean isMultipleCodeSystemVersionsEnabled() { - return myMultipleCodeSystemVersionsEnabled; - } - - /** - *

- * This determines whether multiple code system versions will be enabled. If not enabled, existing code systems will be - * deleted when a new version is uploaded. - *

- *

- * The default value for this setting is {@code false}. - *

- * - * @since 5.1.0 - */ - public void setMultipleCodeSystemVersionsEnabled(Boolean theMultipleCodeSystemVersionsEnabled) { - myMultipleCodeSystemVersionsEnabled = theMultipleCodeSystemVersionsEnabled; - } - } 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 c43d3e3d9c2..12cbd8fa2f5 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 @@ -43,12 +43,10 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -236,7 +234,6 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, CodingDt theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { throw new UnsupportedOperationException(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 6d2cd77aa99..6a680ed97b8 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 @@ -44,7 +44,6 @@ import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; @@ -87,7 +86,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); @@ -121,7 +120,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); @@ -116,7 +115,7 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index 3fe945458a5..0068ba1ee11 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -504,7 +504,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { retVal.setId("loinc-all"); retVal.setUrl("http://loinc.org/vs"); retVal.setVersion("1.0.0"); - retVal.setName("All LOINC codes"); + retVal.setName("Ø"); retVal.setStatus(Enumerations.PublicationStatus.ACTIVE); retVal.setDate(new Date()); retVal.setPublisher("Regenstrief Institute, Inc."); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index 48d5cb31eb7..d5055423b05 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -254,7 +254,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.lookupCode(any(), any(), any())).thenAnswer(t -> { + when(mockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> { String system = t.getArgument(1, String.class); String code = t.getArgument(2, String.class); if (myValidConcepts.contains(system + "___" + code)) { From c1dcf0442c409b521bafd4664771ca11383dcb7f Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Mon, 17 Aug 2020 21:52:16 -0400 Subject: [PATCH 03/71] Fix broken tests. --- .../provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java | 3 ++- .../jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java | 3 ++- .../jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java | 3 ++- .../src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index a6d061c3e06..7cc793e5371 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -85,13 +85,14 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, @OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA, @OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB, + @OperationParam(name="version", min=0, max=1) org.hl7.fhir.r4.model.StringType theVersion, RequestDetails theRequestDetails ) { startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); 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 dec2d1adbcc..9b78c83991b 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 @@ -85,13 +85,14 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 45d80b0228f..bcd7445766f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -85,13 +85,14 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index 0068ba1ee11..3fe945458a5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -504,7 +504,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { retVal.setId("loinc-all"); retVal.setUrl("http://loinc.org/vs"); retVal.setVersion("1.0.0"); - retVal.setName("Ø"); + retVal.setName("All LOINC codes"); retVal.setStatus(Enumerations.PublicationStatus.ACTIVE); retVal.setDate(new Date()); retVal.setPublisher("Regenstrief Institute, Inc."); From dc786eaff920b70c62500d30fb561e287c9e8bba Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Fri, 21 Aug 2020 10:16:10 -0400 Subject: [PATCH 04/71] Refine model for storing multiple code versions. --- .../ca/uhn/fhir/i18n/hapi-messages.properties | 1 + .../term/TermCodeSystemStorageSvcImpl.java | 41 ++++++++++++++----- .../jpa/term/TerminologySvcImplR4Test.java | 2 +- 3 files changed, 32 insertions(+), 12 deletions(-) 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 aecaba0f495..c8b2f25d067 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 @@ -141,6 +141,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.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! diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 13683722d6b..486d1da3fbb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -294,7 +294,8 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { if (termCodeSystem != null) { TermCodeSystemVersion codeSystemVersion = getExistingTermCodeSystemVersion(termCodeSystem.getPid(), theCodeSystem.getVersion()); if (codeSystemVersion != null) { - getOrCreateTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theResourceEntity); + TermCodeSystem myCodeSystemEntity = getOrCreateTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theResourceEntity); + validateCodeSystemVersionUpdate(myCodeSystemEntity,theCodeSystem.getUrl(), theCodeSystem.getVersion(), theResourceEntity); return; } } @@ -348,6 +349,12 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { existing = getExistingTermCodeSystemVersion(existingCodeSystem.getPid(), theSystemVersionId); } + /* + * Get CodeSystem and validate CodeSystemVersion + */ + TermCodeSystem codeSystem = getOrCreateTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theCodeSystemResourceTable); + validateCodeSystemVersionUpdate(codeSystem, theSystemUri, theSystemVersionId, theCodeSystemResourceTable); + /* * Delete version being replaced. */ @@ -363,8 +370,6 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { * Do the upload */ - TermCodeSystem codeSystem = getOrCreateTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theCodeSystemResourceTable); - theCodeSystemVersion.setCodeSystem(codeSystem); theCodeSystemVersion.setCodeSystemDisplayName(theSystemName); @@ -470,10 +475,9 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { ourLog.info(" * Removing code system version {} as current version of code system {}", theCodeSystemVersionPid, codeSystem.getPid()); codeSystem.setCurrentVersion(null); myCodeSystemDao.save(codeSystem); + myCodeSystemDao.flush(); } - myConceptDao.flush(); - ourLog.info(" * Deleting code system version"); myCodeSystemVersionDao.delete(theCodeSystemVersionPid); myCodeSystemVersionDao.flush(); @@ -677,12 +681,6 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { codeSystem = new TermCodeSystem(); } codeSystem.setResource(theCodeSystemResourceTable); - } else { - if (!ObjectUtil.equals(codeSystem.getResource().getId(), theCodeSystemResourceTable.getId())) { - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, - codeSystem.getResource().getIdDt().toUnqualifiedVersionless().getValue()); - throw new UnprocessableEntityException(msg); - } } codeSystem.setCodeSystemUri(theSystemUri); @@ -691,6 +689,27 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { return codeSystem; } + private void validateCodeSystemVersionUpdate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) { + // Check if CodeSystemVersion entity already exists. + TermCodeSystemVersion codeSystemVersionEntity; + String msg; + if (theSystemVersionId == null) { + codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid()); + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, + theCodeSystem.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + } else { + codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId); + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, + theSystemVersionId, theCodeSystem.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + } + // Throw exception if the CodeSystemVersion is being duplicated. + if (codeSystemVersionEntity != null) { + if (!ObjectUtil.equals(codeSystemVersionEntity.getResource().getId(), theCodeSystemResourceTable.getId())) { + throw new UnprocessableEntityException(msg); + } + } + } + private void populateCodeSystemVersionProperties(TermCodeSystemVersion theCodeSystemVersion, CodeSystem theCodeSystemResource, ResourceTable theResourceTable) { theCodeSystemVersion.setResource(theResourceTable); theCodeSystemVersion.setCodeSystemDisplayName(theCodeSystemResource.getName()); 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 51b817bbb34..57e543a57e5 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 @@ -1927,7 +1927,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { codeSystem .addConcept().setCode("C").setDisplay("Code C"); - myCodeSystemDao.update(codeSystem, mySrd).getId().toUnqualified(); + myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "C"); assertThat(toCodes(codes), containsInAnyOrder("C")); From 749c2bc4be43b23b06c6755c6d08ab6d01bc2445 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 25 Aug 2020 12:06:07 -0400 Subject: [PATCH 05/71] Changes to enable request delete of specific Code System version. --- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 14 +- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 14 +- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 18 +- .../uhn/fhir/jpa/entity/TermCodeSystem.java | 2 +- .../term/TermCodeSystemStorageSvcImpl.java | 24 ++- .../jpa/term/TermDeferredStorageSvcImpl.java | 16 +- .../term/api/ITermCodeSystemStorageSvc.java | 2 + .../jpa/term/api/ITermDeferredStorageSvc.java | 3 + .../term/TermCodeSystemStorageSvcTest.java | 195 +++++++++++++++--- .../term/TerminologyLoaderSvcLoincTest.java | 11 +- .../jpa/term/TerminologySvcImplR4Test.java | 12 +- 11 files changed, 242 insertions(+), 69 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 6a680ed97b8..d10c33c105c 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 @@ -25,7 +25,9 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; +import ca.uhn.fhir.jpa.dao.HapiFhirResourceDaoCodeSystemUtil; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; +import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; @@ -67,6 +69,8 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao myDefferedCodeSystemsDeletions = Collections.synchronizedList(new ArrayList<>()); + private List myDefferedCodeSystemVersionsDeletions = Collections.synchronizedList(new ArrayList<>()); private List myDeferredConcepts = Collections.synchronizedList(new ArrayList<>()); private List myDeferredValueSets = Collections.synchronizedList(new ArrayList<>()); private List myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>()); @@ -115,6 +117,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { myDefferedCodeSystemsDeletions.add(theCodeSystem); } + @Override + @Transactional + public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { + myDefferedCodeSystemVersionsDeletions.add(theCodeSystemVersion); + } + @Override public void saveAllDeferred() { while (!isStorageQueueEmpty()) { @@ -266,6 +274,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { } private void processDeferredCodeSystemDeletions() { + + for (TermCodeSystemVersion next : myDefferedCodeSystemVersionsDeletions) { + myCodeSystemStorageSvc.deleteCodeSystemVersion(next); + } + + myDefferedCodeSystemVersionsDeletions.clear(); for (TermCodeSystem next : myDefferedCodeSystemsDeletions) { myCodeSystemStorageSvc.deleteCodeSystem(next); } @@ -300,7 +314,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { } private boolean isDeferredCodeSystemDeletions() { - return !myDefferedCodeSystemsDeletions.isEmpty(); + return !myDefferedCodeSystemsDeletions.isEmpty() || !myDefferedCodeSystemVersionsDeletions.isEmpty(); } private boolean isDeferredConcepts() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermCodeSystemStorageSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermCodeSystemStorageSvc.java index 7718bcbab7a..ab3434aa6df 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermCodeSystemStorageSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermCodeSystemStorageSvc.java @@ -41,6 +41,8 @@ public interface ITermCodeSystemStorageSvc { void deleteCodeSystem(TermCodeSystem theCodeSystem); + void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion); + void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable); /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java index 34eac05a92e..9fddda117f1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term.api; */ import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import org.hl7.fhir.r4.model.ConceptMap; @@ -54,6 +55,8 @@ public interface ITermDeferredStorageSvc { void deleteCodeSystem(TermCodeSystem theCodeSystem); + void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion); + /** * This is mostly here for unit tests - Saves any and all deferred concepts and links */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java index cd7ddf2265f..3845452a093 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java @@ -1,11 +1,19 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeType; import org.junit.jupiter.api.Test; +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.fail; + + import static org.junit.jupiter.api.Assertions.assertEquals; public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { @@ -14,47 +22,123 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { @Test public void testStoreNewCodeSystemVersionForExistingCodeSystemNoVersionId() { - CodeSystem upload = createCodeSystemWithMoreThan100Concepts(); + CodeSystem firstUpload = createCodeSystemWithMoreThan100Concepts(); + CodeSystem duplicateUpload = createCodeSystemWithMoreThan100Concepts(); - // Create CodeSystem resource - ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(upload, mySrd).getEntity(); + testCreatingAndUpdatingCodeSystemEntity(firstUpload, duplicateUpload, 125, "Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/"); - // Update the CodeSystem resource - runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity)); + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM); - /* - Because there are more than 100 concepts in the code system, the first 100 will be persisted immediately and - the remaining 25 concepts will be queued up for "deferred save". - - As the CodeSystem was persisted twice, the extra 25 term concepts will be queued twice, each with a different - CodeSystem version PID. Only one set of the term concepts should be persisted (i.e. 125 term concepts in total). - */ - myTerminologyDeferredStorageSvc.setProcessDeferred(true); - myTerminologyDeferredStorageSvc.saveDeferred(); - assertEquals(125, myTermConceptDao.count()); + TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidVersionIsNull( myTermCodeSystem.getPid()); + assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid()); + assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId()); + }); } + @Test public void testStoreNewCodeSystemVersionForExistingCodeSystemVersionId() { - CodeSystem upload = createCodeSystemWithMoreThan100Concepts(); - upload.setVersion("1"); + CodeSystem firstUpload = createCodeSystemWithMoreThan100Concepts(); + firstUpload.setVersion("1"); - // Create CodeSystem resource - ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(upload, mySrd).getEntity(); + CodeSystem duplicateUpload = createCodeSystemWithMoreThan100Concepts(); + duplicateUpload.setVersion("1"); - // Update the CodeSystem resource - runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(upload, codeSystemResourceEntity)); + testCreatingAndUpdatingCodeSystemEntity(firstUpload, duplicateUpload, 125,"Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\" and CodeSystem.version \"1\", already have one with resource ID: CodeSystem/"); - /* - Because there are more than 100 concepts in the code system, the first 100 will be persisted immediately and - the remaining 25 concepts will be queued up for "deferred save". + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM); + + TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion( myTermCodeSystem.getPid(), "1"); + assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid()); + assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId()); + }); + + // Now add a second version + firstUpload = createCodeSystemWithMoreThan100Concepts(); + firstUpload.setVersion("2"); + + duplicateUpload = createCodeSystemWithMoreThan100Concepts(); + duplicateUpload.setVersion("2"); + + testCreatingAndUpdatingCodeSystemEntity(firstUpload, duplicateUpload, 251,"Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\" and CodeSystem.version \"2\", already have one with resource ID: CodeSystem/"); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(2, myTermCodeSystemVersionDao.count()); + TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri(URL_MY_CODE_SYSTEM); + + TermCodeSystemVersion mySecondTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2"); + assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), mySecondTermCodeSystemVersion.getPid()); + assertEquals(myTermCodeSystem.getResource().getId(), mySecondTermCodeSystemVersion.getResource().getId()); + }); + + } + + @Test + public void testDeleteCodeSystem() { + CodeSystem codeSystemToDelete = createSmallCodeSystem(); + + // Create CodeSystem resource and entity + ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(codeSystemToDelete, mySrd).getEntity(); + validateCodeSystemUpdates(10); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + }); + + // Attempt to delete + myCodeSystemDao.delete(codeSystemResourceEntity.getIdDt(), mySrd); + validateCodeSystemUpdates(0); + + runInTransaction(() -> { + assertEquals(0, myTermCodeSystemDao.count()); + assertEquals(0, myTermCodeSystemVersionDao.count()); + }); + + } + + @Test + public void testDeleteCodeSystemVersion() { + CodeSystem firstCodeSystemVersion = createSmallCodeSystem(); + firstCodeSystemVersion.setVersion("1"); + + // Create first CodeSystem resource and entity + ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(firstCodeSystemVersion, mySrd).getEntity(); + validateCodeSystemUpdates(10); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + }); + + CodeSystem secondCodeSystemVersion = createSmallCodeSystem(); + secondCodeSystemVersion.setVersion("2"); + + // Create CodeSystem resource and entity + myCodeSystemDao.create(secondCodeSystemVersion, mySrd); + validateCodeSystemUpdates(20); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(2, myTermCodeSystemVersionDao.count()); + }); + + // Attempt to delete first version + myCodeSystemDao.delete(codeSystemResourceEntity.getIdDt(), mySrd); + validateCodeSystemUpdates(10); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + }); - As the CodeSystem was persisted twice, the extra 25 term concepts will be queued twice, each with a different - CodeSystem version PID. Only one set of the term concepts should be persisted (i.e. 125 term concepts in total). - */ - myTerminologyDeferredStorageSvc.setProcessDeferred(true); - myTerminologyDeferredStorageSvc.saveDeferred(); - assertEquals(125, myTermConceptDao.count()); } private CodeSystem createCodeSystemWithMoreThan100Concepts() { @@ -70,5 +154,56 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { } + private CodeSystem createSmallCodeSystem() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + + for (int i = 0; i < 10; i++) { + codeSystem.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeA " + i))); + } + + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + return codeSystem; + + } + + private void testCreatingAndUpdatingCodeSystemEntity(CodeSystem theUpload, CodeSystem theDuplicate, int expectedCnt, String theDuplicateErrorBaseMsg) { + + // Create CodeSystem resource + ResourceTable codeSystemResourceEntity = (ResourceTable) myCodeSystemDao.create(theUpload, mySrd).getEntity(); + + // Create the CodeSystem and CodeSystemVersion entities + validateCodeSystemUpdates(expectedCnt); + + // Update the CodeSystem + theUpload.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeB"))); + // Update the CodeSystem and CodeSystemVersion entities + runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(theUpload, codeSystemResourceEntity)); + validateCodeSystemUpdates(expectedCnt+1); + + // Try duplicating the CodeSystem + Long originalResId = codeSystemResourceEntity.getId(); + try { + myCodeSystemDao.create(theDuplicate, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals(theDuplicateErrorBaseMsg + originalResId, e.getMessage()); + } + + // Try updating code system when content mode is NOT PRESENT + theUpload.setConcept(new ArrayList<>()); + theUpload.setContent((CodeSystem.CodeSystemContentMode.NOTPRESENT)); + runInTransaction(() -> myTermCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(theUpload, codeSystemResourceEntity)); + validateCodeSystemUpdates(expectedCnt+1); + + } + + private void validateCodeSystemUpdates(int theExpectedConceptCount) { + myTerminologyDeferredStorageSvc.setProcessDeferred(true); + myTerminologyDeferredStorageSvc.saveDeferred(); + myTerminologyDeferredStorageSvc.setProcessDeferred(false); + assertEquals(theExpectedConceptCount, myTermConceptDao.count()); + + } } 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 df83c976ec7..288dcc85a29 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 @@ -351,12 +351,21 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { CodeSystem loincCS = mySystemCaptor.getValue(); assertEquals("2.67", loincCS.getVersion()); + // Update LOINC marked as version 2.67 + myFiles = new ZipCollectionBuilder(); + addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v267_loincupload.properties"); + mySvc.loadLoinc(myFiles.getFiles(), mySrd); + + verify(myTermCodeSystemStorageSvc, times(2)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); + loincCS = mySystemCaptor.getValue(); + assertEquals("2.67", loincCS.getVersion()); + // Load LOINC marked as version 2.68 myFiles = new ZipCollectionBuilder(); addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v268_loincupload.properties"); mySvc.loadLoinc(myFiles.getFiles(), mySrd); - verify(myTermCodeSystemStorageSvc, times(2)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); + verify(myTermCodeSystemStorageSvc, times(3)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); loincCS = mySystemCaptor.getValue(); assertEquals("2.68", loincCS.getVersion()); 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 57e543a57e5..79f2c37836c 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 @@ -12,7 +12,6 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.entity.TermValueSet; -import ca.uhn.fhir.model.dstu2.valueset.ContentTypeEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CanonicalType; @@ -25,7 +24,6 @@ import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.codesystems.HttpVerb; -import org.hl7.fhir.utilities.validation.ValidationOptions; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -1787,10 +1785,10 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { createCodeSystem(); IValidationSupport.CodeValidationResult validation = myTermSvc.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), CS_URL, "ParentWithNoChildrenA", null, null); - assertEquals(true, validation.isOk()); + assertTrue(validation.isOk()); validation = myTermSvc.validateCode(new ValidationSupportContext(myValidationSupport), new ConceptValidationOptions(), CS_URL, "ZZZZZZZ", null, null); - assertEquals(false, validation.isOk()); + assertFalse(validation.isOk()); } @Test @@ -1927,8 +1925,8 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { codeSystem .addConcept().setCode("C").setDisplay("Code C"); - myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "C"); + IIdType id_v2 = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + codes = myTermSvc.findCodesBelow(id_v2.getIdPartAsLong(), id_v2.getVersionIdPartAsLong(), "C"); assertThat(toCodes(codes), containsInAnyOrder("C")); runInTransaction(() -> { @@ -1939,7 +1937,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { Set termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts()); assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B", "C")); - TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id_v2.getIdPartAsLong()); assertEquals("2", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); }); } From 5a0bce915584881d0075cc3a71acf1549995e1b2 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 25 Aug 2020 12:45:53 -0400 Subject: [PATCH 06/71] Changes to enable request delete of specific Code System version. --- .../HapiFhirResourceDaoCodeSystemUtil.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java new file mode 100644 index 00000000000..72cf8ac39ab --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java @@ -0,0 +1,28 @@ +package ca.uhn.fhir.jpa.dao; + +import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; +import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class HapiFhirResourceDaoCodeSystemUtil { + + static public void deleteCodeSystemEntities(ITermCodeSystemDao theCsDao, ITermCodeSystemVersionDao theCsvDao, + ITermDeferredStorageSvc theTermDeferredStorageSvc, String theCodeSystemUrl, + String theCodeSystemVersion) { + if (isNotBlank(theCodeSystemUrl)) { + TermCodeSystem persCs = theCsDao.findByCodeSystemUri(theCodeSystemUrl); + if (persCs != null) { + if (theCodeSystemVersion != null) { + TermCodeSystemVersion persCsVersion = theCsvDao.findByCodeSystemPidAndVersion(persCs.getPid(), theCodeSystemVersion); + theTermDeferredStorageSvc.deleteCodeSystemVersion(persCsVersion); + } else { + theTermDeferredStorageSvc.deleteCodeSystem(persCs); + } + } + } + } +} From 92313cd7703cb71c5e735fa0898b33677578fa8d Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 26 Aug 2020 16:23:17 -0400 Subject: [PATCH 07/71] Tests and fixes to support versioned CodeSystem lookup and subsume operations --- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 13 +- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 13 +- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 13 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 55 +- .../term/TermCodeSystemStorageSvcImpl.java | 14 +- .../uhn/fhir/jpa/term/api/ITermReadSvc.java | 2 +- .../r4/FhirResourceDaoR4CodeSystemTest.java | 130 ++- .../r4/ResourceProviderR4CodeSystemTest.java | 3 + ...urceProviderR4CodeSystemVersionedTest.java | 769 ++++++++++++++++++ .../r4/ResourceProviderR4ValueSetTest.java | 34 + .../term/TermCodeSystemStorageSvcTest.java | 76 -- .../resources/extensional-case-3-cs-v1.xml | 101 +++ .../resources/extensional-case-3-cs-v2.xml | 101 +++ 13 files changed, 1205 insertions(+), 119 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml 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 d10c33c105c..621d83d5e9a 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 @@ -112,16 +112,17 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion); + String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null; + String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null; + return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion); } @Override 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 1b95f658efe..d421f52354c 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 @@ -107,16 +107,17 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion); + String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null; + String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null; + return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index 8a28cdeeeb0..29a2f295eb9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -109,16 +109,17 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion); + String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null; + String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null; + return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 69e1113fb09..7f0a90be65a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1380,7 +1380,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return txTemplate.execute(t -> { TermCodeSystemVersion csv = getCurrentCodeSystemVersionForVersion(theCodeSystem, theVersion); if (csv == null) { - return null; + return Optional.empty(); } return myConceptDao.findByCodeSystemAndCode(csv, theCode); }); @@ -1392,9 +1392,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { if (theVersion != null) { key.append("_").append(theVersion); } - TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), uri -> myTxTemplate.execute(tx -> { + TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), t -> myTxTemplate.execute(tx -> { TermCodeSystemVersion csv = null; - TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(uri); + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theUri); if (cs != null) { if (theVersion != null) { csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(cs.getPid(), theVersion); @@ -1819,28 +1819,32 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { - return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null); + public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, + IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { + return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null, null, null); } @Override @Transactional - public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion) { - VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); - VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); - String systemVersion = null; - if (theSystemVersion != null) { - systemVersion = theSystemVersion.getValue(); - } + public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, + IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, + IPrimitiveType theSystemVersion, String theCodingAVersion, + String theCodingBVersion) { + VersionIndependentConceptWithSystemVersion conceptA = toConcept(theCodeA, theSystem, theCodingA, theSystemVersion, theCodingAVersion); + VersionIndependentConceptWithSystemVersion conceptB = toConcept(theCodeB, theSystem, theCodingB, theSystemVersion, theCodingBVersion); if (!StringUtils.equals(conceptA.getSystem(), conceptB.getSystem())) { throw new InvalidRequestException("Unable to test subsumption across different code systems"); } - TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), systemVersion) + if (!StringUtils.equals(conceptA.getCodeSystemVersion(), conceptB.getCodeSystemVersion())) { + throw new InvalidRequestException("Unable to test subsumption across different code system versions"); + } + + TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), conceptA.getCodeSystemVersion()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptA)); - TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), systemVersion) + TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), conceptB.getCodeSystemVersion()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptB)); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -2411,14 +2415,33 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @NotNull - private static VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType) { + private VersionIndependentConceptWithSystemVersion toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, + IBaseCoding theCodingType, IPrimitiveType theSystemVersionType, + String theCodingVersionType) { String code = theCodeType != null ? theCodeType.getValueAsString() : null; String system = theSystemType != null ? theSystemType.getValueAsString() : null; + String systemVersion = theSystemVersionType != null ? theSystemVersionType.getValueAsString() : null; if (theCodingType != null) { code = theCodingType.getCode(); system = theCodingType.getSystem(); + systemVersion = theCodingVersionType; } - return new VersionIndependentConcept(system, code); + return new VersionIndependentConceptWithSystemVersion(system, code, systemVersion); + } + + private static class VersionIndependentConceptWithSystemVersion extends VersionIndependentConcept { + + String myCodeSystemVersion; + + public VersionIndependentConceptWithSystemVersion(String theSystem, String theCode, String theSystemVersion) { + super(theSystem, theCode); + myCodeSystemVersion = theSystemVersion; + } + + public String getCodeSystemVersion() { + return myCodeSystemVersion; + } + } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index d9d7189534b..6230a94cc35 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -234,9 +234,19 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { @Override @Transactional(propagation = Propagation.NEVER) public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { - ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getPid()); - + TermCodeSystem termCodeSystem = theCodeSystemVersion.getCodeSystem(); + ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getCodeSystemVersionId()); deleteCodeSystemVersion(theCodeSystemVersion.getPid()); + + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + txTemplate.executeWithoutResult(t -> { + if (myCodeSystemVersionDao.findByCodeSystemPid(termCodeSystem.getPid()).size() == 0) { + ourLog.info(" * Deleting code system {}", termCodeSystem.getPid()); + deleteCodeSystem(termCodeSystem); + } + }); + } /** 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 9e6e340e5bd..124cf2d6450 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 @@ -105,7 +105,7 @@ public interface ITermReadSvc extends IValidationSupport { IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB); - IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion); + IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion, String theCodingAVersion, String theCodingBVersion); void preExpandDeferredValueSetsToTerminologyTables(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java index df2905456c4..4328183f249 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.dao.r4; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; import org.apache.commons.io.IOUtils; import org.hl7.fhir.instance.model.api.IIdType; @@ -8,11 +9,14 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { @@ -35,15 +39,7 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { @Test public void testDeleteLargeCompleteCodeSystem() { - CodeSystem cs = new CodeSystem(); - cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); - cs.setUrl("http://foo"); - for (int i = 0; i < 222; i++) { - cs.addConcept().setCode("CODE" + i); - } - IIdType id = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless(); - myTerminologyDeferredStorageSvc.saveDeferred(); - myTerminologyDeferredStorageSvc.saveDeferred(); + IIdType id = createLargeCodeSystem(null); runInTransaction(() -> { assertEquals(1, myTermCodeSystemDao.count()); @@ -72,6 +68,122 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { } + @Test + public void testDeleteCodeSystemVersion() { + + // Create code system with two versions. + IIdType id_first = createLargeCodeSystem("1"); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(222, myTermConceptDao.count()); + assertEquals(1, resourceList.size()); + assertNull(resourceList.get(0).getDeleted()); + }); + + IIdType id_second = createLargeCodeSystem("2"); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(2, myTermCodeSystemVersionDao.count()); + assertEquals(444, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(2, active); + }); + + // Attempt to delete first version + myCodeSystemDao.delete(id_first, mySrd); + + // Only the resource will be deleted initially + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(2, myTermCodeSystemVersionDao.count()); + assertEquals(444, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(1, active); + }); + + // Now the background scheduler will do its thing + myTerminologyDeferredStorageSvc.saveDeferred(); + + // Entities for first resource should be gone now. + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(222, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(1, active); + }); + + // Attempt to delete second version + myCodeSystemDao.delete(id_second, mySrd); + + // Only the resource will be deleted initially + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(222, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(0, active); + }); + + // Now the background scheduler will do its thing + myTerminologyDeferredStorageSvc.saveDeferred(); + + // The remaining versions and Code System entities should be gone now. + runInTransaction(() -> { + assertEquals(0, myTermCodeSystemDao.count()); + assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(0, myTermCodeSystemVersionDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(0, active); + }); + + } + + private IIdType createLargeCodeSystem(String theVersion) { + CodeSystem cs = new CodeSystem(); + cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo"); + if (theVersion != null) { + cs.setVersion(theVersion); + } + for (int i = 0; i < 222; i++) { + cs.addConcept().setCode("CODE" + i); + } + IIdType id = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless(); + myTerminologyDeferredStorageSvc.saveDeferred(); + myTerminologyDeferredStorageSvc.saveDeferred(); + return id; + } + @AfterAll public static void afterClassClearContext() { TermReindexingSvcImpl.setForceSaveDeferredAlwaysForUnitTest(false); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index 8fc41cbb4ab..0ed780b76e4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -325,6 +325,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("FOO")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) .execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Unknown code: [http://parentchild|FOO]", e.getMessage()); } @@ -340,6 +341,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("FOO")) .execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Unknown code: [http://parentchild|FOO]", e.getMessage()); } @@ -355,6 +357,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD + "A").setCode("ChildAA")) .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD + "B").setCode("ParentA")) .execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Unable to test subsumption across different code systems", e.getMessage()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java new file mode 100644 index 00000000000..d6cce634524 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java @@ -0,0 +1,769 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; +import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProviderR4Test { + + private static final String SYSTEM_PARENTCHILD = "http://parentchild"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemVersionedTest.class); + + @BeforeEach + @Transactional + public void before02() throws IOException { + CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v1.xml"); + myCodeSystemDao.create(cs, mySrd); + + cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v2.xml"); + myCodeSystemDao.create(cs, mySrd); + + ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless(); + + CodeSystem parentChildCs = new CodeSystem(); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setVersion("1"); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + CodeSystem.ConceptDefinitionComponent parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); + parentA.addConcept().setCode("ParentC").setDisplay("Parent C"); + parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B"); + + myCodeSystemDao.create(parentChildCs); + + parentChildCs = new CodeSystem(); + parentChildCs.setVersion("2"); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A v2"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA v2"); + parentA.addConcept().setCode("ParentB").setDisplay("Parent B v2"); + parentChildCs.addConcept().setCode("ParentC").setDisplay("Parent C v2"); + + myCodeSystemDao.create(parentChildCs); + + } + + @Test + public void testLookupOnExternalCodeMultiVersion() { + ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "1"); + ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "2"); + + // First test with no version specified (should return from last version created) + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // With HTTP GET + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM)) + .useHttpGet() + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 1 specified. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A1", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // With HTTP GET + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("1")) + .useHttpGet() + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("1"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A1", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 2 specified. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // With HTTP GET + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("2")) + .useHttpGet() + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemBuiltInCode() { + // First test with no version specified (should return the one and only version defined). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Repeat with version specified. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203")) + .andParameter("version", new StringType("2.9")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemBuiltInNonexistentVersion() { + try { + myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://hl7.org/fhir/v2/0203")) + .andParameter("version", new StringType("2.8")) + .execute(); + fail(); + } catch (ResourceNotFoundException e) { + ourLog.info("Lookup failed as expected"); + } + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedCode() { + // First test with no version specified (should return from last version created) + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 1 specified. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 2 specified + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedNonExistentVersion() { + try { + myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("3")) + .execute(); + fail(); + } catch (ResourceNotFoundException e) { + ourLog.info("Lookup failed as expected"); + } + } + + @Test + public void testLookupOperationByCoding() { + // First test with no version specified (should return from last version created) + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version set to 1 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version set to 2 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodes_Subsumes() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentB")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentC")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentB")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodes_Subsumedby() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentB")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentC")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentB")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + } + + @Test + public void testSubsumesOnCodes_Disjoint() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentC")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentB")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentC")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodings_MismatchedCsVersions() { + try { + myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Unable to test subsumption across different code system versions", e.getMessage()); + } + } + + + @Test + public void testSubsumesOnCodings_Subsumes() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + + @Test + public void testSubsumesOnCodings_Subsumedby() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMEDBY.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodings_Disjoint() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 6196d2d226f..0d2c8d18407 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -1135,4 +1135,38 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { return codeSystem; } + public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + codeSystem.setVersion(theCodeSystemVersion); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A" + theCodeSystemVersion); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA" + theCodeSystemVersion); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA" + theCodeSystemVersion); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB" + theCodeSystemVersion); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB" + theCodeSystemVersion); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B" + theCodeSystemVersion); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", theCodeSystemVersion, cs, table); + return codeSystem; + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java index 3845452a093..42ace0e1efc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcTest.java @@ -12,8 +12,6 @@ import org.junit.jupiter.api.Test; import java.util.ArrayList; import static org.junit.jupiter.api.Assertions.fail; - - import static org.junit.jupiter.api.Assertions.assertEquals; public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { @@ -80,67 +78,6 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { } - @Test - public void testDeleteCodeSystem() { - CodeSystem codeSystemToDelete = createSmallCodeSystem(); - - // Create CodeSystem resource and entity - ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(codeSystemToDelete, mySrd).getEntity(); - validateCodeSystemUpdates(10); - - runInTransaction(() -> { - assertEquals(1, myTermCodeSystemDao.count()); - assertEquals(1, myTermCodeSystemVersionDao.count()); - }); - - // Attempt to delete - myCodeSystemDao.delete(codeSystemResourceEntity.getIdDt(), mySrd); - validateCodeSystemUpdates(0); - - runInTransaction(() -> { - assertEquals(0, myTermCodeSystemDao.count()); - assertEquals(0, myTermCodeSystemVersionDao.count()); - }); - - } - - @Test - public void testDeleteCodeSystemVersion() { - CodeSystem firstCodeSystemVersion = createSmallCodeSystem(); - firstCodeSystemVersion.setVersion("1"); - - // Create first CodeSystem resource and entity - ResourceTable codeSystemResourceEntity = (ResourceTable)myCodeSystemDao.create(firstCodeSystemVersion, mySrd).getEntity(); - validateCodeSystemUpdates(10); - - runInTransaction(() -> { - assertEquals(1, myTermCodeSystemDao.count()); - assertEquals(1, myTermCodeSystemVersionDao.count()); - }); - - CodeSystem secondCodeSystemVersion = createSmallCodeSystem(); - secondCodeSystemVersion.setVersion("2"); - - // Create CodeSystem resource and entity - myCodeSystemDao.create(secondCodeSystemVersion, mySrd); - validateCodeSystemUpdates(20); - - runInTransaction(() -> { - assertEquals(1, myTermCodeSystemDao.count()); - assertEquals(2, myTermCodeSystemVersionDao.count()); - }); - - // Attempt to delete first version - myCodeSystemDao.delete(codeSystemResourceEntity.getIdDt(), mySrd); - validateCodeSystemUpdates(10); - - runInTransaction(() -> { - assertEquals(1, myTermCodeSystemDao.count()); - assertEquals(1, myTermCodeSystemVersionDao.count()); - }); - - } - private CodeSystem createCodeSystemWithMoreThan100Concepts() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); @@ -154,19 +91,6 @@ public class TermCodeSystemStorageSvcTest extends BaseJpaR4Test { } - private CodeSystem createSmallCodeSystem() { - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl(URL_MY_CODE_SYSTEM); - - for (int i = 0; i < 10; i++) { - codeSystem.addConcept(new CodeSystem.ConceptDefinitionComponent(new CodeType("codeA " + i))); - } - - codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); - return codeSystem; - - } - private void testCreatingAndUpdatingCodeSystemEntity(CodeSystem theUpload, CodeSystem theDuplicate, int expectedCnt, String theDuplicateErrorBaseMsg) { // Create CodeSystem resource diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml new file mode 100644 index 00000000000..dc023fe3811 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml new file mode 100644 index 00000000000..a9868b624da --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3fef8f7befd51e01c8991c4667f2d7a5e6bb929a Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 30 Aug 2020 15:29:57 -0400 Subject: [PATCH 08/71] Back out changes relating to validate operations. Cleanup of code --- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 2 +- ...aseJpaResourceProviderCodeSystemDstu3.java | 2 +- .../BaseJpaResourceProviderCodeSystemR5.java | 2 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 115 ++++++++---------- .../term/TermCodeSystemStorageSvcImpl.java | 15 +-- .../r4/ResourceProviderR4ValueSetTest.java | 45 ------- 6 files changed, 60 insertions(+), 121 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index 29a2f295eb9..9b5fe473c96 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -88,7 +88,7 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 7cc793e5371..6b5c7cd7c77 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -85,7 +85,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, @OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA, @OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB, - @OperationParam(name="version", min=0, max=1) org.hl7.fhir.r4.model.StringType theVersion, + @OperationParam(name="version", min=0, max=1) StringType theVersion, RequestDetails theRequestDetails ) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index bcd7445766f..47c880a6a10 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -85,7 +85,7 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull) { String system = theIncludeOrExclude.getSystem(); - String systemVersion = theIncludeOrExclude.getVersion(); boolean hasSystem = isNotBlank(system); boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0; @@ -631,7 +625,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); if (cs != null) { - return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs, systemVersion); + return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs); } else { @@ -672,7 +666,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { nextSystem = system; } - LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode(), systemVersion); + LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode()); if (lookup != null && lookup.isFound()) { addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, nextSystem, next.getCode(), lookup.getCodeDisplay()); foundCount++; @@ -766,7 +760,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nonnull - private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs, String theSystemVersion) { + private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs) { TermCodeSystemVersion csv = theCs.getCurrentVersion(); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -794,7 +788,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Filters */ - handleFilters(bool, theSystem, qb, theIncludeOrExclude, theSystemVersion); + handleFilters(bool, theSystem, qb, theIncludeOrExclude); Query luceneQuery = bool.createQuery(); @@ -903,15 +897,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void handleFilters(BooleanJunction theBool, String theSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude, String theSystemVersion) { + private void handleFilters(BooleanJunction theBool, String theSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { if (theIncludeOrExclude.getFilter().size() > 0) { for (ValueSet.ConceptSetFilterComponent nextFilter : theIncludeOrExclude.getFilter()) { - handleFilter(theSystem, theQb, theBool, nextFilter, theSystemVersion); + handleFilter(theSystem, theQb, theBool, nextFilter); } } } - private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { if (isBlank(theFilter.getValue()) && theFilter.getOp() == null && isBlank(theFilter.getProperty())) { return; } @@ -927,24 +921,24 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "concept": case "code": - handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter, theSystemVersion); + handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter); break; case "parent": case "child": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincParentChild(theQb, theBool, theFilter); + handleFilterLoincParentChild(theBool, theFilter); break; case "ancestor": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestor(theSystem, theQb, theBool, theFilter, theSystemVersion); + handleFilterLoincAncestor(theSystem, theBool, theFilter); break; case "descendant": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincDescendant(theSystem, theQb, theBool, theFilter, theSystemVersion); + handleFilterLoincDescendant(theSystem, theBool, theFilter); break; case "copyright": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincCopyright(theQb, theBool, theFilter); + handleFilterLoincCopyright(theBool, theFilter); break; default: handleFilterRegex(theBool, theFilter); @@ -952,11 +946,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private boolean isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) { + private void isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) { if (!isCodeSystemLoinc(theSystem)) { throw new InvalidRequestException("Invalid filter, property " + theProperty + " is LOINC-specific and cannot be used with system: " + theSystem); } - return true; } private boolean isCodeSystemLoinc(String theSystem) { @@ -991,8 +984,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { bool.must(textQuery); } - private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { - TermConcept code = findCode(theSystem, theFilter.getValue(), theSystemVersion) + private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + TermConcept code = findCode(theSystem, theFilter.getValue()) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theFilter.getValue())); if (theFilter.getOp() == ValueSet.FilterOperator.ISA) { @@ -1004,7 +997,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincParentChild(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincParentChild(BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: addLoincFilterParentChildEqual(theBool, theFilter.getProperty(), theFilter.getValue()); @@ -1037,41 +1030,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincAncestor(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + private void handleFilterLoincAncestor(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter, theSystemVersion); + addLoincFilterAncestorEqual(theSystem, theBool, theFilter); break; case IN: - addLoincFilterAncestorIn(theSystem, theQb, theBool, theFilter, theSystemVersion); + addLoincFilterAncestorIn(theSystem, theBool, theFilter); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { - addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter.getProperty(), theFilter.getValue(), theSystemVersion); + private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterAncestorEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); } - private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, String theProperty, String theValue, String theSystemVersion) { - List terms = getAncestorTerms(theSystem, theProperty, theValue, theSystemVersion); + private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getAncestorTerms(theSystem, theProperty, theValue); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + private void addLoincFilterAncestorIn(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value, theSystemVersion)); + terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value)); } theBool.must(new TermsQuery(terms)); } - private List getAncestorTerms(String theSystem, String theProperty, String theValue, String theSystemVersion) { + private List getAncestorTerms(String theSystem, String theProperty, String theValue) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue, theSystemVersion) + TermConcept code = findCode(theSystem, theValue) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); retVal.add(new Term("myParentPids", "" + code.getId())); @@ -1081,41 +1074,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + private void handleFilterLoincDescendant(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterDescendantEqual(theSystem, theBool, theFilter, theSystemVersion); + addLoincFilterDescendantEqual(theSystem, theBool, theFilter); break; case IN: - addLoincFilterDescendantIn(theSystem, theQb, theBool, theFilter, theSystemVersion); + addLoincFilterDescendantIn(theSystem, theBool, theFilter); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { - addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue(), theSystemVersion); + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); } - private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue, String theSystemVersion) { - List terms = getDescendantTerms(theSystem, theProperty, theValue, theSystemVersion); + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getDescendantTerms(theSystem, theProperty, theValue); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterDescendantIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter, String theSystemVersion) { + private void addLoincFilterDescendantIn(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value, theSystemVersion)); + terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value)); } theBool.must(new TermsQuery(terms)); } - private List getDescendantTerms(String theSystem, String theProperty, String theValue, String theSystemVersion) { + private List getDescendantTerms(String theSystem, String theProperty, String theValue) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue, theSystemVersion) + TermConcept code = findCode(theSystem, theValue) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); String[] parentPids = code.getParentPidsAsString().split(" "); @@ -1127,7 +1120,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return retVal; } - private void handleFilterLoincCopyright(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincCopyright(BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { String copyrightFilterValue = defaultString(theFilter.getValue()).toLowerCase(); @@ -1365,7 +1358,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override public Optional findCode(String theCodeSystem, String theCode) { return findCode(theCodeSystem, theCode, null); - } @Override @@ -1378,7 +1370,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY); return txTemplate.execute(t -> { - TermCodeSystemVersion csv = getCurrentCodeSystemVersionForVersion(theCodeSystem, theVersion); + TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem, theVersion); if (csv == null) { return Optional.empty(); } @@ -1387,8 +1379,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nullable - private TermCodeSystemVersion getCurrentCodeSystemVersionForVersion(String theUri, String theVersion) { - StringBuffer key = new StringBuffer(theUri); + private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri, String theVersion) { + StringBuilder key = new StringBuilder(theUri); if (theVersion != null) { key.append("_").append(theVersion); } @@ -1820,16 +1812,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, - IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { + IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null, null, null); } @Override @Transactional - public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, - IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, - IPrimitiveType theSystemVersion, String theCodingAVersion, - String theCodingBVersion) { + public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion, String theCodingAVersion, String theCodingBVersion) { VersionIndependentConceptWithSystemVersion conceptA = toConcept(theCodeA, theSystem, theCodingA, theSystemVersion, theCodingAVersion); VersionIndependentConceptWithSystemVersion conceptB = toConcept(theCodeB, theSystem, theCodingB, theSystemVersion, theCodingBVersion); @@ -2085,6 +2074,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { while (scrollableResultsIterator.hasNext()) { TermConceptMapGroupElement nextElement = scrollableResultsIterator.next(); + // TODO: The invocation of the size() below does not seem to be necessary but for some reason, removing it causes tests in TerminologySvcImplR4Test to fail. nextElement.getConceptMapGroupElementTargets().size(); myEntityManager.detach(nextElement); @@ -2138,11 +2128,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @CoverageIgnore @Override public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { - return validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl, null); - } - - @Override - public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, String theSystemVersion) { invokeRunnableForUnitTest(); if (isNotBlank(theValueSetUrl)) { @@ -2151,7 +2136,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - Optional codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode, theSystemVersion).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode()))); + Optional codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode()))); if (codeOpt != null && codeOpt.isPresent()) { VersionIndependentConcept code = codeOpt.get(); @@ -2182,7 +2167,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - CodeValidationResult retVal = null; + CodeValidationResult retVal; if (valueSet != null) { retVal = new InMemoryTerminologyServerValidationSupport(myContext).validateCodeInValueSet(theValidationSupportContext, theValidationOptions, theCodeSystem, theCode, theDisplay, valueSet); } else { @@ -2415,9 +2400,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @NotNull - private VersionIndependentConceptWithSystemVersion toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, - IBaseCoding theCodingType, IPrimitiveType theSystemVersionType, - String theCodingVersionType) { + private VersionIndependentConceptWithSystemVersion toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType, IPrimitiveType theSystemVersionType, String theCodingVersionType) { String code = theCodeType != null ? theCodeType.getValueAsString() : null; String system = theSystemType != null ? theSystemType.getValueAsString() : null; String systemVersion = theSystemVersionType != null ? theSystemVersionType.getValueAsString() : null; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 6230a94cc35..598c5eccdd4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -234,10 +234,12 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { @Override @Transactional(propagation = Propagation.NEVER) public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { - TermCodeSystem termCodeSystem = theCodeSystemVersion.getCodeSystem(); + // Delete TermCodeSystemVersion ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getCodeSystemVersionId()); deleteCodeSystemVersion(theCodeSystemVersion.getPid()); + // Check if the version deleted is the current version. If so, delete TermCodeSystem as well. + TermCodeSystem termCodeSystem = theCodeSystemVersion.getCodeSystem(); TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txTemplate.executeWithoutResult(t -> { @@ -312,8 +314,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { if (termCodeSystem != null) { TermCodeSystemVersion codeSystemVersion = getExistingTermCodeSystemVersion(termCodeSystem.getPid(), theCodeSystem.getVersion()); if (codeSystemVersion != null) { - TermCodeSystem myCodeSystemEntity = getOrCreateTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theResourceEntity); - validateCodeSystemVersionUpdate(myCodeSystemEntity,theCodeSystem.getUrl(), theCodeSystem.getVersion(), theResourceEntity); + TermCodeSystem myCodeSystemEntity = getOrCreateDistinctTermCodeSystem(codeSystemResourcePid, theCodeSystem.getUrl(), theCodeSystem.getUrl(), theCodeSystem.getVersion(), theResourceEntity); return; } } @@ -370,8 +371,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { /* * Get CodeSystem and validate CodeSystemVersion */ - TermCodeSystem codeSystem = getOrCreateTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theCodeSystemResourceTable); - validateCodeSystemVersionUpdate(codeSystem, theSystemUri, theSystemVersionId, theCodeSystemResourceTable); + TermCodeSystem codeSystem = getOrCreateDistinctTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theSystemVersionId, theCodeSystemResourceTable); /* * Delete version being replaced. @@ -691,7 +691,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { } @Nonnull - private TermCodeSystem getOrCreateTermCodeSystem(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, ResourceTable theCodeSystemResourceTable) { + private TermCodeSystem getOrCreateDistinctTermCodeSystem(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) { TermCodeSystem codeSystem = myCodeSystemDao.findByCodeSystemUri(theSystemUri); if (codeSystem == null) { codeSystem = myCodeSystemDao.findByResourcePid(theCodeSystemResourcePid.getIdAsLong()); @@ -704,10 +704,11 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { codeSystem.setCodeSystemUri(theSystemUri); codeSystem.setName(theSystemName); codeSystem = myCodeSystemDao.save(codeSystem); + checkForCodeSystemVersionDuplicate(codeSystem,theSystemUri, theSystemVersionId, theCodeSystemResourceTable); return codeSystem; } - private void validateCodeSystemVersionUpdate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) { + private void checkForCodeSystemVersionDuplicate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) { // Check if CodeSystemVersion entity already exists. TermCodeSystemVersion codeSystemVersionEntity; String msg = null; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 0d2c8d18407..1785024a411 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -416,51 +416,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } - @Test - public void testExpandByValueSetWithFilter() throws IOException { - loadAndPersistCodeSystem(); - - ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); - - Parameters respParam = myClient - .operation() - .onType(ValueSet.class) - .named("expand") - .withParameter(Parameters.class, "valueSet", toExpand) - .andParameter("filter", new StringType("blood")) - .execute(); - ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); - ourLog.info(resp); - assertThat(resp, stringContainsInOrder( - "", - "")); - - } - - @Test - public void testExpandByUrlWithFilter() throws Exception { - loadAndPersistCodeSystemAndValueSet(); - - Parameters respParam = myClient - .operation() - .onType(ValueSet.class) - .named("expand") - .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) - .andParameter("filter", new StringType("first")) - .execute(); - ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); - ourLog.info(resp); - assertThat(resp, stringContainsInOrder( - "", - "")); - - } - - @Test public void testExpandByValueSetWithPreExpansion() throws IOException { myDaoConfig.setPreExpandValueSets(true); From 1c55d87b103778321766562bd43f44c64a1ad5e1 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 30 Aug 2020 17:11:48 -0400 Subject: [PATCH 09/71] Problem found in LOINC loader. --- .../ca/uhn/fhir/i18n/hapi-messages.properties | 1 + .../dao/data/ITermCodeSystemVersionDao.java | 2 +- .../term/TermCodeSystemStorageSvcImpl.java | 12 ++- ...rminologyLoaderSvcLoincIntegratedTest.java | 79 +++++++++++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java 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 c8b2f25d067..b263903392d 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 @@ -142,6 +142,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.cannotUpdateVersionForExistingCodeSystemVersion=Updated CodeSystem has wrong version id. Existing CodeSystem resource has 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.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! diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java index fc55636836f..8b56e223d30 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java @@ -49,7 +49,7 @@ public interface ITermCodeSystemVersionDao extends JpaRepository findByCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); + TermCodeSystemVersion findByCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemHavingThisVersionAsCurrentVersionIfAny.myResource.myId = :resource_id") TermCodeSystemVersion findCurrentVersionForCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 598c5eccdd4..b37f98235b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -715,14 +715,18 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { if (theSystemVersionId == null) { codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid()); if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, - codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } } else { codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId); if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, - theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + } else { + codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourceTable.getId()); + if (codeSystemVersionEntity != null) { + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + throw new UnprocessableEntityException(msg); + } } } // Throw exception if the CodeSystemVersion is being duplicated. diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java new file mode 100644 index 00000000000..5543102a7dc --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java @@ -0,0 +1,79 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import org.eclipse.jetty.http.HttpStatus; +import org.hl7.fhir.r4.model.CodeSystem; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TerminologyLoaderSvcLoincIntegratedTest extends BaseJpaR4Test { + private TermLoaderSvcImpl mySvc; + + private ZipCollectionBuilder myFiles; + + @BeforeEach + public void before() { + mySvc = new TermLoaderSvcImpl(); + mySvc.setTermCodeSystemStorageSvcForUnitTests(myTermCodeSystemStorageSvc); + mySvc.setTermDeferredStorageSvc(myTerminologyDeferredStorageSvc); + + myFiles = new ZipCollectionBuilder(); + } + + @Test + public void testLoadLoincMultipleVersions() throws IOException { + + // Load LOINC marked as version 2.67 + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v267_loincupload.properties"); + mySvc.loadLoinc(myFiles.getFiles(), mySrd); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(1, myResourceTableDao.count()); + TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri("http://loinc.org"); + + TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2.67"); + assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid()); + assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId()); + }); + + // Update LOINC marked as version 2.67 + myFiles = new ZipCollectionBuilder(); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v267_loincupload.properties"); + mySvc.loadLoinc(myFiles.getFiles(), mySrd); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri("http://loinc.org"); + + TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2.67"); + assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid()); + assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId()); + }); + + + // Load LOINC marked as version 2.68 + myFiles = new ZipCollectionBuilder(); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesWithPropertiesFileToZip(myFiles, "v268_loincupload.properties"); + mySvc.loadLoinc(myFiles.getFiles(), mySrd); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(2, myTermCodeSystemVersionDao.count()); + TermCodeSystem myTermCodeSystem = myTermCodeSystemDao.findByCodeSystemUri("http://loinc.org"); + + TermCodeSystemVersion mySecondTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2.68"); + assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), mySecondTermCodeSystemVersion.getPid()); + assertEquals(myTermCodeSystem.getResource().getId(), mySecondTermCodeSystemVersion.getResource().getId()); + }); + + } + +} From 94440c849fb1e61a193cae8a2d00b067eff450e4 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 30 Aug 2020 21:53:38 -0400 Subject: [PATCH 10/71] Ensure that LOINC loader assigns a distinct ID for each version. --- .../main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java | 1 + .../jpa/provider/r4/ResourceProviderR4ValueSetTest.java | 2 ++ .../jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java | 6 ++++++ 3 files changed, 9 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index 3fe945458a5..5e452989081 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -381,6 +381,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { String codeSystemVersionId = theUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); if (codeSystemVersionId != null) { loincCs.setVersion(codeSystemVersionId); + loincCs.setId(loincCs.getId() + "-" + codeSystemVersionId); } } catch (IOException e) { throw new InternalErrorException("Failed to load loinc.xml", e); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 1785024a411..90b325c159b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -1060,6 +1060,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails) { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); @@ -1093,6 +1094,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setVersion(theCodeSystemVersion); IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java index 5543102a7dc..b6703763dbe 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java @@ -41,6 +41,8 @@ public class TerminologyLoaderSvcLoincIntegratedTest extends BaseJpaR4Test { TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2.67"); assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid()); assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId()); + + assertEquals(1, myResourceTableDao.count()); }); // Update LOINC marked as version 2.67 @@ -56,6 +58,8 @@ public class TerminologyLoaderSvcLoincIntegratedTest extends BaseJpaR4Test { TermCodeSystemVersion myTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2.67"); assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), myTermCodeSystemVersion.getPid()); assertEquals(myTermCodeSystem.getResource().getId(), myTermCodeSystemVersion.getResource().getId()); + + assertEquals(1, myResourceTableDao.count()); }); @@ -72,6 +76,8 @@ public class TerminologyLoaderSvcLoincIntegratedTest extends BaseJpaR4Test { TermCodeSystemVersion mySecondTermCodeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(myTermCodeSystem.getPid(), "2.68"); assertEquals(myTermCodeSystem.getCurrentVersion().getPid(), mySecondTermCodeSystemVersion.getPid()); assertEquals(myTermCodeSystem.getResource().getId(), mySecondTermCodeSystemVersion.getResource().getId()); + + assertEquals(2, myResourceTableDao.count()); }); } From 5f005120c9990f281abdaddd7939317d6262c5fd Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Thu, 3 Sep 2020 22:36:15 -0400 Subject: [PATCH 11/71] Initial changes to support multi-version ValueSets --- .../ca/uhn/fhir/i18n/hapi-messages.properties | 4 +- .../HapiFhirResourceDaoCodeSystemUtil.java | 28 -- .../fhir/jpa/dao/data/ITermValueSetDao.java | 6 +- .../jpa/dao/data/ITermValueSetVersionDao.java | 52 ++++ .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 11 +- .../dao/expunge/ExpungeEverythingService.java | 6 + .../dao/r4/FhirResourceDaoCodeSystemR4.java | 11 +- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 11 +- .../ca/uhn/fhir/jpa/entity/TermValueSet.java | 15 ++ .../fhir/jpa/entity/TermValueSetVersion.java | 249 ++++++++++++++++++ .../fhir/jpa/term/BaseTermReadSvcImpl.java | 152 ++++++++--- .../term/TermCodeSystemStorageSvcImpl.java | 19 +- .../jpa/term/TermDeferredStorageSvcImpl.java | 12 +- .../jpa/term/ValueSetConceptAccumulator.java | 1 + .../jpa/term/api/ITermDeferredStorageSvc.java | 3 +- .../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 3 + .../FhirResourceDaoDstu3ValueSetTest.java | 8 +- .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 3 + .../r4/FhirResourceDaoR4CodeSystemTest.java | 4 +- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 13 +- .../ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java | 3 + .../ResourceProviderDstu3ValueSetTest.java | 2 +- .../r4/ResourceProviderR4ValueSetTest.java | 19 +- .../r5/ResourceProviderR5ValueSetTest.java | 19 +- .../jpa/term/TerminologySvcImplR4Test.java | 11 +- .../term/ValueSetConceptAccumulatorTest.java | 12 +- .../jpa/term/ValueSetExpansionR4Test.java | 73 ++--- 27 files changed, 568 insertions(+), 182 deletions(-) delete mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java 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 b263903392d..faa358c87eb 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 @@ -142,10 +142,12 @@ 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.cannotUpdateVersionForExistingCodeSystemVersion=Updated CodeSystem has wrong version id. Existing CodeSystem resource has CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2} +ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotUpdateUrlOrVersionForCodeSystemResource=Cannot update URL or version for CodeSystem resource. Existing CodeSystem resource with resource ID {0} found with CodeSystem.url "{1}" and CodeSystem.version "{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.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 CodeSystem resources with CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2} +ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotUpdateUrlOrVersionForValueSetResource=Cannot update URL or version for ValueSet resource. Existing ValueSet resource with resource ID {0} found with ValueSet.url "{1}" and ValueSet.version "{2}" ca.uhn.fhir.jpa.patch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java deleted file mode 100644 index 72cf8ac39ab..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HapiFhirResourceDaoCodeSystemUtil.java +++ /dev/null @@ -1,28 +0,0 @@ -package ca.uhn.fhir.jpa.dao; - -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; -import ca.uhn.fhir.jpa.entity.TermCodeSystem; -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; -import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -public class HapiFhirResourceDaoCodeSystemUtil { - - static public void deleteCodeSystemEntities(ITermCodeSystemDao theCsDao, ITermCodeSystemVersionDao theCsvDao, - ITermDeferredStorageSvc theTermDeferredStorageSvc, String theCodeSystemUrl, - String theCodeSystemVersion) { - if (isNotBlank(theCodeSystemUrl)) { - TermCodeSystem persCs = theCsDao.findByCodeSystemUri(theCodeSystemUrl); - if (persCs != null) { - if (theCodeSystemVersion != null) { - TermCodeSystemVersion persCsVersion = theCsvDao.findByCodeSystemPidAndVersion(persCs.getPid(), theCodeSystemVersion); - theTermDeferredStorageSvc.deleteCodeSystemVersion(persCsVersion); - } else { - theTermDeferredStorageSvc.deleteCodeSystem(persCs); - } - } - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java index f3a841f49e6..40dbc15027e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.data; * #L% */ +import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import org.springframework.data.domain.Pageable; @@ -38,7 +39,8 @@ public interface ITermValueSetDao extends JpaRepository { @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url") Optional findByUrl(@Param("url") String theUrl); - @Query("SELECT vs FROM TermValueSet vs WHERE vs.myExpansionStatus = :expansion_status") - Slice findByExpansionStatus(Pageable pageable, @Param("expansion_status") TermValueSetPreExpansionStatusEnum theExpansionStatus); + @Query("SELECT vs FROM TermValueSet vs WHERE vs.myCurrentVersion.myId = :vsv_pid") + Optional findWithCodeSystemVersionAsCurrentVersion(@Param("vsv_pid") Long theValueSetVersionPid); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java new file mode 100644 index 00000000000..b81d4a69914 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java @@ -0,0 +1,52 @@ +package ca.uhn.fhir.jpa.dao.data; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2020 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Optional; + +public interface ITermValueSetVersionDao extends JpaRepository { + + @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myResourcePid = :resource_pid") + Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); + + @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myValueSetPid = :valueset_pid AND vsv.myValueSetVersionId = :version" ) + TermValueSetVersion findByValueSetPidAndVersion(@Param("valueset_pid") Long theValueSetPid, @Param("version") String theVersion); + + @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myValueSetPid = :valueset_pid AND vsv.myValueSetVersionId IS NULL" ) + TermValueSetVersion findByValueSetPidAndNullVersion(@Param("valueset_pid") Long theValueSetPid); + + @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myExpansionStatus = :expansion_status") + Slice findByExpansionStatus(Pageable pageable, @Param("expansion_status") TermValueSetPreExpansionStatusEnum theExpansionStatus); + + @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myValueSetPid = :valueset_pid") + List findByValueSetPid(@Param("valueset_pid") Long theValueSetPid); + +} 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 621d83d5e9a..c42f4125ce1 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 @@ -25,10 +25,6 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.dao.HapiFhirResourceDaoCodeSystemUtil; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; -import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; @@ -67,10 +63,6 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao { + counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermValueSet.class.getSimpleName() + " d SET d.myCurrentVersion = null")); + return null; + }); counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class)); counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class)); counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class)); @@ -135,6 +140,7 @@ public class ExpungeEverythingService { counter.addAndGet(expungeEverythingByType(SearchInclude.class)); counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class)); counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class)); + counter.addAndGet(expungeEverythingByType(TermValueSetVersion.class)); counter.addAndGet(expungeEverythingByType(TermValueSet.class)); counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class)); counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class)); 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 d421f52354c..996eba93af4 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 @@ -25,10 +25,6 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.dao.HapiFhirResourceDaoCodeSystemUtil; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; -import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; @@ -60,10 +56,6 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao getConcepts() { if (myConcepts == null) { myConcepts = new ArrayList<>(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java new file mode 100644 index 00000000000..3b9bb8280f2 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java @@ -0,0 +1,249 @@ +package ca.uhn.fhir.jpa.entity; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2020 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.util.ValidateUtil; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.ColumnDefault; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import static org.apache.commons.lang3.StringUtils.length; + +@Table(name = "TRM_VALUESET_VER", uniqueConstraints = { + @UniqueConstraint(name = "IDX_VALUESET_URL_AND_VER", columnNames = {"VALUESET_PID", "VS_VERSION_ID"}) +}) +@Entity() +public class TermValueSetVersion implements Serializable { + private static final long serialVersionUID = 1L; + + public static final int MAX_EXPANSION_STATUS_LENGTH = 50; + public static final int MAX_NAME_LENGTH = 200; + public static final int MAX_VERSION_LENGTH = 200; + + @Id() + @SequenceGenerator(name = "SEQ_VALUESETVER_PID", sequenceName = "SEQ_VALUESETVER_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESETVER_PID") + @Column(name = "PID") + private Long myId; + + @OneToOne() + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMVALUESETVER_RES")) + private ResourceTable myResource; + + @Column(name = "RES_ID", insertable = false, updatable = false) + private Long myResourcePid; + + @OneToMany(mappedBy = "myValueSet", fetch = FetchType.LAZY) + private List myConcepts; + + @Column(name = "TOTAL_CONCEPTS", nullable = false) + @ColumnDefault("0") + private Long myTotalConcepts; + + @Column(name = "TOTAL_CONCEPT_DESIGNATIONS", nullable = false) + @ColumnDefault("0") + private Long myTotalConceptDesignations; + + @Enumerated(EnumType.STRING) + @Column(name = "EXPANSION_STATUS", nullable = false, length = MAX_EXPANSION_STATUS_LENGTH) + private TermValueSetPreExpansionStatusEnum myExpansionStatus; + + @Column(name = "VS_VERSION_ID", nullable = true, updatable = false, length = MAX_VERSION_LENGTH) + private String myValueSetVersionId; + + /** + * This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration + * issued. It should be made non-nullable at some point. + */ + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "VALUESET_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_VALSETVER_VS_ID")) + private TermValueSet myValueSet; + + @Column(name = "VALUESET_PID", insertable = false, updatable = false) + private Long myValueSetPid; + + @SuppressWarnings("unused") + @OneToOne(mappedBy = "myCurrentVersion", optional = true, fetch = FetchType.LAZY) + private TermValueSet myValueSetHavingThisVersionAsCurrentVersionIfAny; + + public TermValueSetVersion() { + super(); + myExpansionStatus = TermValueSetPreExpansionStatusEnum.NOT_EXPANDED; + myTotalConcepts = 0L; + myTotalConceptDesignations = 0L; + } + + public Long getId() { + return myId; + } + + public ResourceTable getResource() { + return myResource; + } + + public TermValueSetVersion setResource(ResourceTable theResource) { + myResource = theResource; + return this; + } + + public List getConcepts() { + if (myConcepts == null) { + myConcepts = new ArrayList<>(); + } + + return myConcepts; + } + + public Long getTotalConcepts() { + return myTotalConcepts; + } + + public TermValueSetVersion setTotalConcepts(Long theTotalConcepts) { + myTotalConcepts = theTotalConcepts; + return this; + } + + public TermValueSetVersion decrementTotalConcepts() { + if (myTotalConcepts > 0) { + myTotalConcepts--; + } + return this; + } + + public TermValueSetVersion incrementTotalConcepts() { + myTotalConcepts++; + return this; + } + + public Long getTotalConceptDesignations() { + return myTotalConceptDesignations; + } + + public TermValueSetVersion setTotalConceptDesignations(Long theTotalConceptDesignations) { + myTotalConceptDesignations = theTotalConceptDesignations; + return this; + } + + public TermValueSetVersion decrementTotalConceptDesignations() { + if (myTotalConceptDesignations > 0) { + myTotalConceptDesignations--; + } + return this; + } + + public TermValueSetVersion incrementTotalConceptDesignations() { + myTotalConceptDesignations++; + return this; + } + + public TermValueSetPreExpansionStatusEnum getExpansionStatus() { + return myExpansionStatus; + } + + public void setExpansionStatus(TermValueSetPreExpansionStatusEnum theExpansionStatus) { + myExpansionStatus = theExpansionStatus; + } + + public String getValueSetVersionId() { + return myValueSetVersionId; + } + + public TermValueSetVersion setValueSetVersionId(String theValueSetVersionId) { + ValidateUtil.isNotTooLongOrThrowIllegalArgument( + theValueSetVersionId, MAX_VERSION_LENGTH, + "Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theValueSetVersionId)); + myValueSetVersionId = theValueSetVersionId; + return this; + } + + public TermValueSet getValueSet() { + return myValueSet; + } + + public TermValueSetVersion setValueSet(TermValueSet theValueSet) { + myValueSet = theValueSet; + return this; + } + + public Long getValueSetPid() { + return myValueSetPid; + } + + @Override + public boolean equals(Object theO) { + if (this == theO) return true; + + if (!(theO instanceof TermValueSetVersion)) return false; + + TermValueSetVersion that = (TermValueSetVersion) theO; + + return new EqualsBuilder() + .append(myValueSetVersionId, that.myValueSetVersionId) + .append(myValueSetPid, that.myValueSetPid) + .isEquals(); + } + + @Override + public int hashCode() { + HashCodeBuilder b = new HashCodeBuilder(17, 37); + b.append(myValueSetVersionId); + b.append(myValueSetPid); + return b.toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("myId", myId) + .append(myResource != null ? ("myResource=" + myResource.toString()) : ("myResource=(null)")) + .append("myResourcePid", myResourcePid) + .append("valueSetPid", myValueSetPid) + .append("valueSetVersionId", myValueSetVersionId) + .append(myConcepts != null ? ("myConcepts - size=" + myConcepts.size()) : ("myConcepts=(null)")) + .append("myTotalConcepts", myTotalConcepts) + .append("myTotalConceptDesignations", myTotalConceptDesignations) + .append("myExpansionStatus", myExpansionStatus) + .toString(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 2be7dae56c0..3b9aa34bdd1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -81,6 +81,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.CoverageIgnore; +import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; @@ -331,25 +332,47 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { deleteConceptMap(theResourceTable); } - public void deleteValueSet(ResourceTable theResourceTable) { + public void deleteValueSetForResource(ResourceTable theResourceTable) { // Get existing entity so it can be deleted. - Optional optionalExistingTermValueSetById = myValueSetDao.findByResourcePid(theResourceTable.getId()); + Optional optionalExistingTermValueSetVersionById = myValueSetVersionDao.findByResourcePid(theResourceTable.getId()); - if (optionalExistingTermValueSetById.isPresent()) { - TermValueSet existingTermValueSet = optionalExistingTermValueSetById.get(); + if (optionalExistingTermValueSetVersionById.isPresent()) { + TermValueSetVersion existingTermValueSetVersion = optionalExistingTermValueSetVersionById.get(); + + ourLog.info("Deleting existing TermValueSetVersion[{}] and its children...", existingTermValueSetVersion.getId()); + myValueSetConceptDesignationDao.deleteByTermValueSetId(existingTermValueSetVersion.getId()); + myValueSetConceptDao.deleteByTermValueSetId(existingTermValueSetVersion.getId()); + + // Check if this is the current version. If so, clear the current version from TermValueSet. + TermValueSet termValueSet = existingTermValueSetVersion.getValueSet(); + TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + Boolean versionIsCurrent = termValueSet.getCurrentVersion().getId() == existingTermValueSetVersion.getId(); + if (versionIsCurrent) { + txTemplate.executeWithoutResult(t -> { + termValueSet.setCurrentVersion(null); + myValueSetDao.save(termValueSet); + }); + } + + myValueSetVersionDao.deleteById(existingTermValueSetVersion.getId()); + ourLog.info("Done deleting existing TermValueSetVersion[{}] and its children.", existingTermValueSetVersion.getId()); + + // If this is the current version, then delete the TermValueSet master as well. + if (versionIsCurrent) { + txTemplate.executeWithoutResult(t -> { + ourLog.info(" * Deleting value set {}", termValueSet.getId()); + myValueSetDao.deleteById(termValueSet.getId()); + }); + } - ourLog.info("Deleting existing TermValueSet[{}] and its children...", existingTermValueSet.getId()); - myValueSetConceptDesignationDao.deleteByTermValueSetId(existingTermValueSet.getId()); - myValueSetConceptDao.deleteByTermValueSetId(existingTermValueSet.getId()); - myValueSetDao.deleteById(existingTermValueSet.getId()); - ourLog.info("Done deleting existing TermValueSet[{}] and its children.", existingTermValueSet.getId()); } } @Override @Transactional public void deleteValueSetAndChildren(ResourceTable theResourceTable) { - deleteValueSet(theResourceTable); + deleteValueSetForResource(theResourceTable); } private ValueSet expandValueSetInMemory(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, VersionIndependentConcept theWantConceptOrNull) { @@ -1674,17 +1697,18 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { // We have a ValueSet to pre-expand. try { ValueSet valueSet = txTemplate.execute(t -> { - TermValueSet refreshedValueSetToExpand = myValueSetDao.findById(valueSetToExpand.getId()).get(); - return getValueSetFromResourceTable(refreshedValueSetToExpand.getResource()); + TermValueSetVersion refreshedValueSetVersionToExpand = myValueSetVersionDao.findById(valueSetToExpand.getId()).get(); + return getValueSetFromResourceTable(refreshedValueSetVersionToExpand.getResource()); }); - ValueSetConceptAccumulator accumulator = new ValueSetConceptAccumulator(valueSetToExpand, myValueSetDao, myValueSetConceptDao, myValueSetConceptDesignationDao); + ValueSetConceptAccumulator accumulator = new ValueSetConceptAccumulator(valueSetToExpand, myValueSetVersionDao, myValueSetDao, myValueSetConceptDao, myValueSetConceptDesignationDao); expandValueSet(null, valueSet, accumulator); // We are done with this ValueSet. txTemplate.execute(t -> { - valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANDED); - myValueSetDao.saveAndFlush(valueSetToExpand); + TermValueSetVersion finalValueSetToExpand = myValueSetVersionDao.findById(valueSetToExpand.getId()).get(); + finalValueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANDED); + myValueSetVersionDao.saveAndFlush(finalValueSetToExpand); return null; }); @@ -1778,38 +1802,92 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied"); ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theValueSet.getUrl(), "ValueSet has no value for ValueSet.url"); - TermValueSet termValueSet = new TermValueSet(); - termValueSet.setResource(theResourceTable); - termValueSet.setUrl(theValueSet.getUrl()); - termValueSet.setName(theValueSet.hasName() ? theValueSet.getName() : null); + /* + * Get CodeSystem and validate CodeSystemVersion + */ + TermValueSet termValueSet = getOrCreateDistinctTermValueSet(theResourceTable.getPersistentId(), theValueSet.getUrl(), theValueSet.getName(), theValueSet.getVersion(), theResourceTable); - // We delete old versions; we don't support versioned ValueSets. - deleteValueSet(theResourceTable); + // Delete version being replaced + deleteValueSetForResource(theResourceTable); /* * Do the upload. */ - String url = termValueSet.getUrl(); - Optional optionalExistingTermValueSetByUrl = myValueSetDao.findByUrl(url); - if (!optionalExistingTermValueSetByUrl.isPresent()) { - - termValueSet = myValueSetDao.save(termValueSet); - - } else { - TermValueSet existingTermValueSet = optionalExistingTermValueSetByUrl.get(); - - String msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, - "cannotCreateDuplicateValueSetUrl", - url, - existingTermValueSet.getResource().getIdDt().toUnqualifiedVersionless().getValue()); - - throw new UnprocessableEntityException(msg); + TermValueSetVersion termValueSetVersion = termValueSet.getCurrentVersion(); + if (termValueSetVersion == null) { + termValueSetVersion = new TermValueSetVersion(); } + termValueSetVersion.setResource(theResourceTable); + termValueSetVersion.setValueSetVersionId(theValueSet.getVersion()); + termValueSetVersion.setValueSet(termValueSet); + + termValueSetVersion = myValueSetVersionDao.saveAndFlush(termValueSetVersion); + + termValueSet.setCurrentVersion(termValueSetVersion); + termValueSet = myValueSetDao.saveAndFlush(termValueSet); + ourLog.info("Done storing TermValueSet[{}] for {}", termValueSet.getId(), theValueSet.getIdElement().toVersionless().getValueAsString()); } + + @Nonnull + private TermValueSet getOrCreateDistinctTermValueSet(ResourcePersistentId theValueSetResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, ResourceTable theValueSetResourceTable) { + Optional termValueSetOptional = myValueSetDao.findByUrl(theSystemUri); + TermValueSet termValueSet; + if (!termValueSetOptional.isPresent()) { + termValueSetOptional = myValueSetDao.findByResourcePid(theValueSetResourcePid.getIdAsLong()); + if (!termValueSetOptional.isPresent()) { + termValueSet = new TermValueSet(); + } else { + termValueSet = termValueSetOptional.get(); + } + } else { + termValueSet = termValueSetOptional.get(); + } + + termValueSet.setResource(theValueSetResourceTable); + termValueSet.setUrl(theSystemUri); + termValueSet.setName(theSystemName); + termValueSet = myValueSetDao.save(termValueSet); + checkForValueSetVersionDuplicate(termValueSet,theSystemUri, theSystemVersionId, theValueSetResourceTable); + return termValueSet; + } + + + private void checkForValueSetVersionDuplicate(TermValueSet theValueSet, String theSystemUri, String theSystemVersionId, ResourceTable theValueSetResourceTable) { + // Check if TermValueSetVersion entity already exists. + TermValueSetVersion valueSetVersionEntity; + String msg = null; + if (theSystemVersionId == null) { + // Check if a non-versioned TermValueSetVersion entity already exists for this TermValueSet. + valueSetVersionEntity = myValueSetVersionDao.findByValueSetPidAndNullVersion(theValueSet.getId()); + if (valueSetVersionEntity != null) { + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateValueSetUrl", theSystemUri, valueSetVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + } + } else { + // Check if a TermValueSetVersion entity already exists for this TermValueSet and version. + valueSetVersionEntity = myValueSetVersionDao.findByValueSetPidAndVersion(theValueSet.getId(), theSystemVersionId); + if (valueSetVersionEntity != null) { + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateValueSetUrlAndVersion", theSystemUri, theSystemVersionId, valueSetVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + } else { + // Check if a TermValueSetVersion entity already exists for this ValueSet resource (i.e. with a different version or URL) + Optional valueSetVersionEntityOptional = myValueSetVersionDao.findByResourcePid(theValueSetResourceTable.getId()); + if (valueSetVersionEntityOptional.isPresent()) { + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotUpdateUrlOrVersionForValueSetResource", theSystemUri, theSystemVersionId, valueSetVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + throw new UnprocessableEntityException(msg); + } + } + } + // Throw exception if the TermValueSet version is being duplicated. + if (valueSetVersionEntity != null) { + if (!ObjectUtil.equals(valueSetVersionEntity.getResource().getId(), theValueSetResourceTable.getId())) { + throw new UnprocessableEntityException(msg); + } + } + } + + @Override public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index b37f98235b9..2098fbbce5f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -238,17 +238,6 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getCodeSystemVersionId()); deleteCodeSystemVersion(theCodeSystemVersion.getPid()); - // Check if the version deleted is the current version. If so, delete TermCodeSystem as well. - TermCodeSystem termCodeSystem = theCodeSystemVersion.getCodeSystem(); - TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); - txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - txTemplate.executeWithoutResult(t -> { - if (myCodeSystemVersionDao.findByCodeSystemPid(termCodeSystem.getPid()).size() == 0) { - ourLog.info(" * Deleting code system {}", termCodeSystem.getPid()); - deleteCodeSystem(termCodeSystem); - } - }); - } /** @@ -709,27 +698,29 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { } private void checkForCodeSystemVersionDuplicate(TermCodeSystem theCodeSystem, String theSystemUri, String theSystemVersionId, ResourceTable theCodeSystemResourceTable) { - // Check if CodeSystemVersion entity already exists. TermCodeSystemVersion codeSystemVersionEntity; String msg = null; if (theSystemVersionId == null) { + // Check if a non-versioned TermCodeSystemVersion entity already exists for this TermCodeSystem. codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidVersionIsNull(theCodeSystem.getPid()); if (codeSystemVersionEntity != null) { msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } } else { + // Check if a TermCodeSystemVersion entity already exists for this TermCodeSystem and version. codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId); if (codeSystemVersionEntity != null) { msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } else { + // Check if a TermCodeSystemVersion entity already exists for this CodeSystem resource (i.e. with a different version or URL) codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourceTable.getId()); if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotUpdateUrlOrVersionForCodeSystemResource", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); throw new UnprocessableEntityException(msg); } } } - // Throw exception if the CodeSystemVersion is being duplicated. + // Throw exception if the TermCodeSystemVersion is being duplicated. if (codeSystemVersionEntity != null) { if (!ObjectUtil.equals(codeSystemVersionEntity.getResource().getId(), theCodeSystemResourceTable.getId())) { throw new UnprocessableEntityException(msg); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java index 3a4e3f47139..5c5fe0c5a07 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; @@ -119,8 +120,15 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { @Override @Transactional - public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { - myDefferedCodeSystemVersionsDeletions.add(theCodeSystemVersion); + public void deleteCodeSystemForResource(ResourceTable theCodeSystemToDelete) { + TermCodeSystemVersion codeSystemVersionToDelete = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemToDelete.getResourceId()); + if (codeSystemVersionToDelete != null) { + myDefferedCodeSystemVersionsDeletions.add(codeSystemVersionToDelete); + } + TermCodeSystem codeSystemToDelete = myCodeSystemDao.findByResourcePid(theCodeSystemToDelete.getResourceId()); + if (codeSystemToDelete != null) { + deleteCodeSystem(codeSystemToDelete); + } } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java index 869d8ba1b72..07af5fbbbd5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java @@ -113,6 +113,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystem, "ValueSet contains a concept with no system value"); ValidateUtil.isNotBlankOrThrowInvalidRequest(theCode, "ValueSet contains a concept with no code value"); + myTermValueSetVersion = myValueSetVersionDao.findById(myTermValueSetVersion.getId()).get(); TermValueSetConcept concept = new TermValueSetConcept(); concept.setValueSet(myTermValueSet); concept.setOrder(myConceptsSaved); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java index 9fddda117f1..eada0db384b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermDeferredStorageSvc.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; @@ -55,7 +56,7 @@ public interface ITermDeferredStorageSvc { void deleteCodeSystem(TermCodeSystem theCodeSystem); - void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion); + void deleteCodeSystemForResource(ResourceTable theCodeSystemResourceToDelete); /** * This is mostly here for unit tests - Saves any and all deferred concepts and links diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index 720a4bd0a3d..0484f6e1ae5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.model.entity.ModelConfig; @@ -343,6 +344,8 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { private IBulkDataExportSvc myBulkDataExportSvc; @Autowired protected ITermValueSetDao myTermValueSetDao; + @Autowired + protected ITermValueSetVersionDao myTermValueSetVersionDao; @AfterEach() public void afterCleanupDao() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java index eeacca3ea22..ca5e887afad 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeType; @@ -58,7 +59,9 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { runInTransaction(() -> { TermValueSet vsEntity = myTermValueSetDao.findByUrl("http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.2--20171231000000").orElseThrow(() -> new IllegalStateException()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsEntity.getExpansionStatus()); + Long valueSetId = vsEntity.getId(); + TermValueSetVersion vsvEntity = myTermValueSetVersionDao.findByValueSetPid(valueSetId).get(0); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsvEntity.getExpansionStatus()); }); IValidationSupport.CodeValidationResult validationOutcome; @@ -80,7 +83,8 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { runInTransaction(() -> { TermValueSet vsEntity = myTermValueSetDao.findByUrl("http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.2--20171231000000").orElseThrow(() -> new IllegalStateException()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsEntity.getExpansionStatus()); + TermValueSetVersion vsvEntity = myTermValueSetVersionDao.findByValueSetPid(vsEntity.getId()).get(0); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsvEntity.getExpansionStatus()); }); // Validate good diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 6f82419b86c..5fe669b636a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -47,6 +47,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.entity.TermCodeSystem; @@ -438,6 +439,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired protected ITermValueSetDao myTermValueSetDao; @Autowired + protected ITermValueSetVersionDao myTermValueSetVersionDao; + @Autowired protected ITermValueSetConceptDao myTermValueSetConceptDao; @Autowired protected ITermValueSetConceptDesignationDao myTermValueSetConceptDesignationDao; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java index 4328183f249..9d917a7af8a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java @@ -136,10 +136,10 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { // Attempt to delete second version myCodeSystemDao.delete(id_second, mySrd); - // Only the resource will be deleted initially + // Only the resource will be deleted initially, but the URL for the TermCodeSystem will be cleared and not searchable. runInTransaction(() -> { assertEquals(1, myTermCodeSystemDao.count()); - assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); assertEquals(1, myTermCodeSystemVersionDao.count()); assertEquals(222, myTermConceptDao.count()); List resourceList = myResourceTableDao.findAll(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 9a6804f4020..0cf1bc2d874 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; import ca.uhn.fhir.jpa.term.TerminologyLoaderSvcLoincTest; @@ -212,7 +213,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { runInTransaction(() -> { TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); + TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); }); OperationOutcome outcome; @@ -241,14 +243,16 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { // Before, the VS wasn't pre-expanded. Try again with it pre-expanded runInTransaction(() -> { TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); + TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); }); myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables(); runInTransaction(() -> { TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vs.getExpansionStatus()); + TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsv.getExpansionStatus()); }); // Use a code that's in the ValueSet @@ -282,7 +286,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { runInTransaction(() -> { TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); + TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); }); OperationOutcome outcome; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index da959cba3c6..e3967c118b4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -44,6 +44,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor; import ca.uhn.fhir.jpa.model.entity.ModelConfig; @@ -385,6 +386,8 @@ public abstract class BaseJpaR5Test extends BaseJpaTest { @Autowired protected ITermValueSetDao myTermValueSetDao; @Autowired + protected ITermValueSetVersionDao myTermValueSetVersionDao; + @Autowired protected ITermValueSetConceptDao myTermValueSetConceptDao; @Autowired protected ITermValueSetConceptDesignationDao myTermValueSetConceptDesignationDao; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 9c85d3f9afb..0468e5176b8 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -823,7 +823,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setName("ACME Codes"); - codeSystem.setVersion("1.2.3.4"); + codeSystem.setVersion("SYSTEM VERSION"); IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 90b325c159b..5780bc2c54f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; @@ -808,8 +809,9 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - assertEquals(0, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(0, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); }); } @@ -826,10 +828,11 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); - TermValueSetConcept concept = termValueSet.getConcepts().get(0); + TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -851,7 +854,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSet.getConcepts().get(1); + concept = termValueSetVersion.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -861,7 +864,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { // ... - concept = termValueSet.getConcepts().get(22); + concept = termValueSetVersion.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -876,7 +879,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSet.getConcepts().get(23); + concept = termValueSetVersion.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index 919c3b8e8b9..f94754d8d6f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; @@ -1119,8 +1120,9 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - assertEquals(0, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(0, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); }); } @@ -1137,10 +1139,11 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); - TermValueSetConcept concept = termValueSet.getConcepts().get(0); + TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1162,7 +1165,7 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSet.getConcepts().get(1); + concept = termValueSetVersion.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1172,7 +1175,7 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { // ... - concept = termValueSet.getConcepts().get(22); + concept = termValueSetVersion.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1187,7 +1190,7 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSet.getConcepts().get(23); + concept = termValueSetVersion.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); 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 79f2c37836c..9029075718f 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 @@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CanonicalType; @@ -194,9 +195,10 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); Long termValueSetId = termValueSet.getId(); assertEquals(3, myTermValueSetConceptDesignationDao.countByTermValueSetId(termValueSetId).intValue()); - assertEquals(3, termValueSet.getTotalConceptDesignations().intValue()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); assertEquals(24, myTermValueSetConceptDao.countByTermValueSetId(termValueSetId).intValue()); - assertEquals(24, termValueSet.getTotalConcepts().intValue()); + assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override @@ -231,9 +233,10 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); Long termValueSetId = termValueSet.getId(); assertEquals(3, myTermValueSetConceptDesignationDao.countByTermValueSetId(termValueSetId).intValue()); - assertEquals(3, termValueSet.getTotalConceptDesignations().intValue()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); assertEquals(24, myTermValueSetConceptDao.countByTermValueSetId(termValueSetId).intValue()); - assertEquals(24, termValueSet.getTotalConcepts().intValue()); + assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java index 16d3dc3d930..e0480afcfa7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java @@ -2,10 +2,10 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; -import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,9 +25,9 @@ import static org.mockito.Mockito.when; public class ValueSetConceptAccumulatorTest { private ValueSetConceptAccumulator myAccumulator; - private TermValueSet myValueSet; + private TermValueSetVersion myValueSetVersion; @Mock - private ITermValueSetDao myValueSetDao; + private ITermValueSetVersionDao myValueSetVersionDao; @Mock private ITermValueSetConceptDesignationDao myValueSetDesignationDao; @Mock @@ -35,8 +35,8 @@ public class ValueSetConceptAccumulatorTest { @BeforeEach public void before() { - myValueSet = new TermValueSet(); - myAccumulator = new ValueSetConceptAccumulator(myValueSet, myValueSetDao, myValueSetConceptDao, myValueSetDesignationDao); + myValueSetVersion = new TermValueSetVersion(); + myAccumulator = new ValueSetConceptAccumulator(myValueSetVersion, myValueSetVersionDao, null, myValueSetConceptDao, myValueSetDesignationDao); } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index 40850b23eaf..5dd8ac22d4f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; @@ -1165,8 +1166,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(0, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(0, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1183,10 +1185,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(codeSystem.getConcept().size(), termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); - TermValueSetConcept concept = termValueSet.getConcepts().get(0); + TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1208,7 +1211,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSet.getConcepts().get(1); + concept = termValueSetVersion.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1218,7 +1221,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSet.getConcepts().get(22); + concept = termValueSetVersion.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1233,7 +1236,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSet.getConcepts().get(23); + concept = termValueSetVersion.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1267,8 +1270,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(0, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(0, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1285,10 +1289,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(codeSystem.getConcept().size(), termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); - TermValueSetConcept concept = termValueSet.getConcepts().get(0); + TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1310,7 +1315,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSet.getConcepts().get(1); + concept = termValueSetVersion.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1320,7 +1325,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSet.getConcepts().get(22); + concept = termValueSetVersion.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1335,7 +1340,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSet.getConcepts().get(23); + concept = termValueSetVersion.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1369,8 +1374,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(0, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(0, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1387,10 +1393,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(codeSystem.getConcept().size() - 2, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); - TermValueSetConcept concept = termValueSet.getConcepts().get(0); + TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1412,7 +1419,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSet.getConcepts().get(1); + concept = termValueSetVersion.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1422,7 +1429,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSet.getConcepts().get(20); + concept = termValueSetVersion.getConcepts().get(20); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1437,7 +1444,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSet.getConcepts().get(21); + concept = termValueSetVersion.getConcepts().get(21); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1471,8 +1478,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(0, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(0, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1489,10 +1497,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - assertEquals(codeSystem.getConcept().size() - 2, termValueSet.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); - TermValueSetConcept concept = termValueSet.getConcepts().get(0); + TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1514,7 +1523,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSet.getConcepts().get(1); + concept = termValueSetVersion.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1524,7 +1533,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSet.getConcepts().get(20); + concept = termValueSetVersion.getConcepts().get(20); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1539,7 +1548,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSet.getConcepts().get(21); + concept = termValueSetVersion.getConcepts().get(21); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); From 5b707f97a9e5b0f2ae0fd386136dad98c48ef81c Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Fri, 4 Sep 2020 09:25:35 -0400 Subject: [PATCH 12/71] Missing commits --- .../ca/uhn/fhir/jpa/entity/TermValueSet.java | 83 ------------------- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 76 ++++++++++------- .../jpa/term/ValueSetConceptAccumulator.java | 54 ++++++------ 3 files changed, 76 insertions(+), 137 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java index f057efd2d88..c137c3d4643 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java @@ -25,13 +25,10 @@ import ca.uhn.fhir.util.ValidateUtil; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hibernate.annotations.ColumnDefault; import javax.annotation.Nonnull; import javax.persistence.*; import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; @@ -72,29 +69,11 @@ public class TermValueSet implements Serializable { @Column(name = "VSNAME", nullable = true, length = MAX_NAME_LENGTH) private String myName; - @OneToMany(mappedBy = "myValueSet", fetch = FetchType.LAZY) - private List myConcepts; - - @Column(name = "TOTAL_CONCEPTS", nullable = false) - @ColumnDefault("0") - private Long myTotalConcepts; - - @Column(name = "TOTAL_CONCEPT_DESIGNATIONS", nullable = false) - @ColumnDefault("0") - private Long myTotalConceptDesignations; - - @Enumerated(EnumType.STRING) - @Column(name = "EXPANSION_STATUS", nullable = false, length = MAX_EXPANSION_STATUS_LENGTH) - private TermValueSetPreExpansionStatusEnum myExpansionStatus; - @Transient private transient Integer myHashCode; public TermValueSet() { super(); - myExpansionStatus = TermValueSetPreExpansionStatusEnum.NOT_EXPANDED; - myTotalConcepts = 0L; - myTotalConceptDesignations = 0L; } public Long getId() { @@ -140,64 +119,6 @@ public class TermValueSet implements Serializable { return this; } - public List getConcepts() { - if (myConcepts == null) { - myConcepts = new ArrayList<>(); - } - - return myConcepts; - } - - public Long getTotalConcepts() { - return myTotalConcepts; - } - - public TermValueSet setTotalConcepts(Long theTotalConcepts) { - myTotalConcepts = theTotalConcepts; - return this; - } - - public TermValueSet decrementTotalConcepts() { - if (myTotalConcepts > 0) { - myTotalConcepts--; - } - return this; - } - - public TermValueSet incrementTotalConcepts() { - myTotalConcepts++; - return this; - } - - public Long getTotalConceptDesignations() { - return myTotalConceptDesignations; - } - - public TermValueSet setTotalConceptDesignations(Long theTotalConceptDesignations) { - myTotalConceptDesignations = theTotalConceptDesignations; - return this; - } - - public TermValueSet decrementTotalConceptDesignations() { - if (myTotalConceptDesignations > 0) { - myTotalConceptDesignations--; - } - return this; - } - - public TermValueSet incrementTotalConceptDesignations() { - myTotalConceptDesignations++; - return this; - } - - public TermValueSetPreExpansionStatusEnum getExpansionStatus() { - return myExpansionStatus; - } - - public void setExpansionStatus(TermValueSetPreExpansionStatusEnum theExpansionStatus) { - myExpansionStatus = theExpansionStatus; - } - @Override public boolean equals(Object theO) { if (this == theO) return true; @@ -227,10 +148,6 @@ public class TermValueSet implements Serializable { .append(myResource != null ? ("myResource=" + myResource.toString()) : ("myResource=(null)")) .append("myResourcePid", myResourcePid) .append("myName", myName) - .append(myConcepts != null ? ("myConcepts - size=" + myConcepts.size()) : ("myConcepts=(null)")) - .append("myTotalConcepts", myTotalConcepts) - .append("myTotalConceptDesignations", myTotalConceptDesignations) - .append("myExpansionStatus", myExpansionStatus) .toString(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 3b9aa34bdd1..06576adb74f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -45,6 +45,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptViewDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; @@ -62,6 +63,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptView; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; @@ -205,6 +207,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Autowired protected ITermValueSetDao myValueSetDao; @Autowired + protected ITermValueSetVersionDao myValueSetVersionDao; + @Autowired protected ITermValueSetConceptDao myValueSetConceptDao; @Autowired protected ITermValueSetConceptDesignationDao myValueSetConceptDesignationDao; @@ -425,10 +429,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } TermValueSet termValueSet = optionalTermValueSet.get(); + TermValueSetVersion termValueSetVersion; + String valueSetVersion = theValueSetToExpand.getVersion(); + if (valueSetVersion != null) { + termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndVersion(termValueSet.getId(),theValueSetToExpand.getVersion()); + } else { + termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + } - if (termValueSet.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { + if (termValueSetVersion.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { ourLog.warn("{} is present in terminology tables but not ready for persistence-backed invocation of operation $expand. Will perform in-memory expansion without parameters. Current status: {} | {}", - getValueSetInfo(theValueSetToExpand), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); + getValueSetInfo(theValueSetToExpand), termValueSetVersion.getExpansionStatus().name(), termValueSetVersion.getExpansionStatus().getDescription()); return expandValueSetInMemory(theExpansionOptions, theValueSetToExpand, null); // In-memory expansion. } @@ -439,7 +450,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions); int offset = expansionOptions.getOffset(); int count = expansionOptions.getCount(); - populateExpansionComponent(expansionComponent, termValueSet, offset, count); + populateExpansionComponent(expansionComponent, termValueSetVersion, offset, count, termValueSet.getUrl()); ValueSet valueSet = new ValueSet(); valueSet.setStatus(Enumerations.PublicationStatus.ACTIVE); @@ -448,8 +459,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return valueSet; } - private void populateExpansionComponent(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount) { - int total = theTermValueSet.getTotalConcepts().intValue(); + private void populateExpansionComponent(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSetVersion theTermValueSetVersion, int theOffset, int theCount, String theValueSetUrl) { + int total = theTermValueSetVersion.getTotalConcepts().intValue(); theExpansionComponent.setTotal(total); theExpansionComponent.setOffset(theOffset); theExpansionComponent.addParameter().setName("offset").setValue(new IntegerType(theOffset)); @@ -459,17 +470,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return; } - expandConcepts(theExpansionComponent, theTermValueSet, theOffset, theCount); + expandConcepts(theExpansionComponent, theTermValueSetVersion, theOffset, theCount, theValueSetUrl); } - private void expandConcepts(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount) { + private void expandConcepts(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSetVersion theTermValueSetVersion, int theOffset, int theCount, String theValueSetUrl) { int conceptsExpanded = 0; int designationsExpanded = 0; int toIndex = theOffset + theCount; - Collection conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theOffset, toIndex, theTermValueSet.getId()); + Collection conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theOffset, toIndex, theTermValueSetVersion.getId()); if (conceptViews.isEmpty()) { - logConceptsExpanded("No concepts to expand. ", theTermValueSet, conceptsExpanded); + logConceptsExpanded("No concepts to expand. ", theValueSetUrl, conceptsExpanded); return; } @@ -500,28 +511,28 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { designationComponent.setValue(conceptView.getDesignationVal()); if (++designationsExpanded % 250 == 0) { - logDesignationsExpanded("Expansion of designations in progress. ", theTermValueSet, designationsExpanded); + logDesignationsExpanded("Expansion of designations in progress. ", theValueSetUrl, designationsExpanded); } } if (++conceptsExpanded % 250 == 0) { - logConceptsExpanded("Expansion of concepts in progress. ", theTermValueSet, conceptsExpanded); + logConceptsExpanded("Expansion of concepts in progress. ", theValueSetUrl, conceptsExpanded); } } - logDesignationsExpanded("Finished expanding designations. ", theTermValueSet, designationsExpanded); - logConceptsExpanded("Finished expanding concepts. ", theTermValueSet, conceptsExpanded); + logDesignationsExpanded("Finished expanding designations. ", theValueSetUrl, designationsExpanded); + logConceptsExpanded("Finished expanding concepts. ", theValueSetUrl, conceptsExpanded); } - private void logConceptsExpanded(String theLogDescriptionPrefix, TermValueSet theTermValueSet, int theConceptsExpanded) { + private void logConceptsExpanded(String theLogDescriptionPrefix, String theValueSetUrl, int theConceptsExpanded) { if (theConceptsExpanded > 0) { - ourLog.debug("{}Have expanded {} concepts in ValueSet[{}]", theLogDescriptionPrefix, theConceptsExpanded, theTermValueSet.getUrl()); + ourLog.debug("{}Have expanded {} concepts in ValueSet[{}]", theLogDescriptionPrefix, theConceptsExpanded, theValueSetUrl); } } - private void logDesignationsExpanded(String theLogDescriptionPrefix, TermValueSet theTermValueSet, int theDesignationsExpanded) { + private void logDesignationsExpanded(String theLogDescriptionPrefix, String theValueSetUrl, int theDesignationsExpanded) { if (theDesignationsExpanded > 0) { - ourLog.debug("{}Have expanded {} designations in ValueSet[{}]", theLogDescriptionPrefix, theDesignationsExpanded, theTermValueSet.getUrl()); + ourLog.debug("{}Have expanded {} designations in ValueSet[{}]", theLogDescriptionPrefix, theDesignationsExpanded, theValueSetUrl); } } @@ -1262,10 +1273,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } TermValueSet termValueSet = optionalTermValueSet.get(); + TermValueSetVersion termValueSetVersion; + String valueSetVersion = theValueSet.getVersion(); + if (valueSetVersion != null) { + termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndVersion(termValueSet.getId(), valueSetVersion); + } else { + termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); + } - if (termValueSet.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { + if (termValueSetVersion.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { ourLog.warn("{} is present in terminology tables but not ready for persistence-backed invocation of operation $validation-code. Will perform in-memory code validation. Current status: {} | {}", - getValueSetInfo(theValueSet), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); + getValueSetInfo(theValueSet), termValueSetVersion.getExpansionStatus().name(), termValueSetVersion.getExpansionStatus().getDescription()); return false; } @@ -1680,15 +1698,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { while (true) { StopWatch sw = new StopWatch(); - TermValueSet valueSetToExpand = txTemplate.execute(t -> { - Optional optionalTermValueSet = getNextTermValueSetNotExpanded(); - if (!optionalTermValueSet.isPresent()) { + TermValueSetVersion valueSetToExpand = txTemplate.execute(t -> { + Optional optionalTermValueSetVersion = getNextTermValueSetNotExpanded(); + if (!optionalTermValueSetVersion.isPresent()) { return null; } - TermValueSet termValueSet = optionalTermValueSet.get(); - termValueSet.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANSION_IN_PROGRESS); - return myValueSetDao.saveAndFlush(termValueSet); + TermValueSetVersion termValueSetVersion = optionalTermValueSetVersion.get(); + termValueSetVersion.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANSION_IN_PROGRESS); + return myValueSetVersionDao.saveAndFlush(termValueSetVersion); }); if (valueSetToExpand == null) { return; @@ -1718,7 +1736,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ourLog.error("Failed to pre-expand ValueSet: " + e.getMessage(), e); txTemplate.execute(t -> { valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND); - myValueSetDao.saveAndFlush(valueSetToExpand); + myValueSetVersionDao.saveAndFlush(valueSetToExpand); return null; }); } @@ -1783,9 +1801,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { protected abstract ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable); - private Optional getNextTermValueSetNotExpanded() { - Optional retVal = Optional.empty(); - Slice page = myValueSetDao.findByExpansionStatus(PageRequest.of(0, 1), TermValueSetPreExpansionStatusEnum.NOT_EXPANDED); + private Optional getNextTermValueSetNotExpanded() { + Optional retVal = Optional.empty(); + Slice page = myValueSetVersionDao.findByExpansionStatus(PageRequest.of(0, 1), TermValueSetPreExpansionStatusEnum.NOT_EXPANDED); if (!page.getContent().isEmpty()) { retVal = Optional.of(page.getContent().get(0)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java index 07af5fbbbd5..5b444ee6a5a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java @@ -23,10 +23,12 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.entity.TermConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.util.ValidateUtil; import javax.annotation.Nonnull; @@ -41,17 +43,18 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValueSetConceptAccumulator.class); - private TermValueSet myTermValueSet; - private ITermValueSetDao myValueSetDao; - private ITermValueSetConceptDao myValueSetConceptDao; - private ITermValueSetConceptDesignationDao myValueSetConceptDesignationDao; + private TermValueSetVersion myTermValueSetVersion; + final private ITermValueSetVersionDao myValueSetVersionDao; + final private ITermValueSetDao myValueSetDao; + final private ITermValueSetConceptDao myValueSetConceptDao; + final private ITermValueSetConceptDesignationDao myValueSetConceptDesignationDao; private int myConceptsSaved; private int myDesignationsSaved; private int myConceptsExcluded; - private int myCount; - public ValueSetConceptAccumulator(@Nonnull TermValueSet theTermValueSet, @Nonnull ITermValueSetDao theValueSetDao, @Nonnull ITermValueSetConceptDao theValueSetConceptDao, @Nonnull ITermValueSetConceptDesignationDao theValueSetConceptDesignationDao) { - myTermValueSet = theTermValueSet; + public ValueSetConceptAccumulator(@Nonnull TermValueSetVersion theTermValueSetVersion, @Nonnull ITermValueSetVersionDao theValueSetVersionDao, @Nonnull ITermValueSetDao theValueSetDao, @Nonnull ITermValueSetConceptDao theValueSetConceptDao, @Nonnull ITermValueSetConceptDesignationDao theValueSetConceptDesignationDao) { + myTermValueSetVersion = theTermValueSetVersion; + myValueSetVersionDao = theValueSetVersionDao; myValueSetDao = theValueSetDao; myValueSetConceptDao = theValueSetConceptDao; myValueSetConceptDesignationDao = theValueSetConceptDesignationDao; @@ -88,23 +91,23 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } // Get existing entity so it can be deleted. - Optional optionalConcept = myValueSetConceptDao.findByTermValueSetIdSystemAndCode(myTermValueSet.getId(), theSystem, theCode); + Optional optionalConcept = myValueSetConceptDao.findByTermValueSetIdSystemAndCode(myTermValueSetVersion.getId(), theSystem, theCode); if (optionalConcept.isPresent()) { TermValueSetConcept concept = optionalConcept.get(); - ourLog.debug("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl()); + ourLog.debug("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSetVersion.getValueSet().getUrl()); for (TermValueSetConceptDesignation designation : concept.getDesignations()) { myValueSetConceptDesignationDao.deleteById(designation.getId()); - myTermValueSet.decrementTotalConceptDesignations(); + myTermValueSetVersion.decrementTotalConceptDesignations(); } myValueSetConceptDao.deleteById(concept.getId()); - myTermValueSet.decrementTotalConcepts(); - myValueSetDao.save(myTermValueSet); - ourLog.debug("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl()); + myTermValueSetVersion.decrementTotalConcepts(); + myValueSetVersionDao.save(myTermValueSetVersion); + ourLog.debug("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSetVersion.getValueSet().getUrl()); if (++myConceptsExcluded % 250 == 0) { - ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSet.getUrl()); + ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSetVersion.getValueSet().getUrl()); } } } @@ -115,7 +118,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { myTermValueSetVersion = myValueSetVersionDao.findById(myTermValueSetVersion.getId()).get(); TermValueSetConcept concept = new TermValueSetConcept(); - concept.setValueSet(myTermValueSet); + concept.setValueSet(myTermValueSetVersion.getValueSet()); concept.setOrder(myConceptsSaved); concept.setSystem(theSystem); concept.setCode(theCode); @@ -123,21 +126,23 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { concept.setDisplay(theDisplay); } myValueSetConceptDao.save(concept); - myValueSetDao.save(myTermValueSet.incrementTotalConcepts()); + myTermValueSetVersion.incrementTotalConcepts(); + myValueSetVersionDao.save(myTermValueSetVersion); if (++myConceptsSaved % 250 == 0) { - ourLog.info("Have pre-expanded {} concepts in ValueSet[{}]", myConceptsSaved, myTermValueSet.getUrl()); + ourLog.info("Have pre-expanded {} concepts in ValueSet[{}]", myConceptsSaved, myTermValueSetVersion.getValueSet().getUrl()); } return concept; } - private TermValueSetConceptDesignation saveConceptDesignation(TermValueSetConcept theConcept, TermConceptDesignation theDesignation) { + private void saveConceptDesignation(TermValueSetConcept theConcept, TermConceptDesignation theDesignation) { ValidateUtil.isNotBlankOrThrowInvalidRequest(theDesignation.getValue(), "ValueSet contains a concept designation with no value"); TermValueSetConceptDesignation designation = new TermValueSetConceptDesignation(); designation.setConcept(theConcept); - designation.setValueSet(myTermValueSet); + myTermValueSetVersion = myValueSetVersionDao.findById(myTermValueSetVersion.getId()).get(); + designation.setValueSet(myTermValueSetVersion.getValueSet()); designation.setLanguage(theDesignation.getLanguage()); if (isNoneBlank(theDesignation.getUseSystem(), theDesignation.getUseCode())) { designation.setUseSystem(theDesignation.getUseSystem()); @@ -148,13 +153,12 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } designation.setValue(theDesignation.getValue()); myValueSetConceptDesignationDao.save(designation); - myValueSetDao.save(myTermValueSet.incrementTotalConceptDesignations()); + myValueSetVersionDao.save(myTermValueSetVersion.incrementTotalConceptDesignations()); if (++myDesignationsSaved % 250 == 0) { - ourLog.debug("Have pre-expanded {} designations for Concept[{}|{}] in ValueSet[{}]", myDesignationsSaved, theConcept.getSystem(), theConcept.getCode(), myTermValueSet.getUrl()); + ourLog.debug("Have pre-expanded {} designations for Concept[{}|{}] in ValueSet[{}]", myDesignationsSaved, theConcept.getSystem(), theConcept.getCode(), myTermValueSetVersion.getValueSet().getUrl()); } - return designation; } public Boolean removeGapsFromConceptOrder() { @@ -162,13 +166,13 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { return false; } - ourLog.info("Removing gaps from concept order for ValueSet[{}]", myTermValueSet.getUrl()); + ourLog.info("Removing gaps from concept order for ValueSet[{}]", myTermValueSetVersion.getValueSet().getUrl()); int order = 0; - List conceptIds = myValueSetConceptDao.findIdsByTermValueSetId(myTermValueSet.getId()); + List conceptIds = myValueSetConceptDao.findIdsByTermValueSetId(myTermValueSetVersion.getId()); for (Long conceptId : conceptIds) { myValueSetConceptDao.updateOrderById(conceptId, order++); } - ourLog.info("Have removed gaps from concept order for {} concepts in ValueSet[{}]", conceptIds.size(), myTermValueSet.getUrl()); + ourLog.info("Have removed gaps from concept order for {} concepts in ValueSet[{}]", conceptIds.size(), myTermValueSetVersion.getValueSet().getUrl()); return true; } From ed2801dbaf48a5502868283774ac8a35e35364b0 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sun, 6 Sep 2020 11:33:55 -0400 Subject: [PATCH 13/71] ConceptMap multi-version supported --- .../fhir/jpa/dao/data/ITermConceptMapDao.java | 15 ++- .../uhn/fhir/jpa/entity/TermConceptMap.java | 18 +++- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 9 +- .../FhirResourceDaoDstu3ConceptMapTest.java | 91 ++++++++++++++++-- .../r4/FhirResourceDaoR4ConceptMapTest.java | 95 +++++++++++++++++-- .../jpa/term/TerminologySvcImplR4Test.java | 16 ++-- 6 files changed, 216 insertions(+), 28 deletions(-) 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..7e7d1933939 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,14 @@ public interface ITermConceptMapDao extends JpaRepository @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid") Optional findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid); + //-- Replaced with next method. Should it be removed? + @Deprecated @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url") Optional findTermConceptMapByUrl(@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 findTermConceptMapByUrl(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/entity/TermConceptMap.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java index 0e3f1af6c79..02a0937a4ef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java @@ -35,12 +35,13 @@ import static org.apache.commons.lang3.StringUtils.length; @Entity @Table(name = "TRM_CONCEPT_MAP", uniqueConstraints = { - @UniqueConstraint(name = "IDX_CONCEPT_MAP_URL", columnNames = {"URL"}) + @UniqueConstraint(name = "IDX_CONCEPT_MAP_URL", columnNames = {"URL", "VER"}) }) public class TermConceptMap implements Serializable { private static final long serialVersionUID = 1L; static final int MAX_URL_LENGTH = 200; + static final int MAX_VER_LENGTH = 200; @Id() @SequenceGenerator(name = "SEQ_CONCEPT_MAP_PID", sequenceName = "SEQ_CONCEPT_MAP_PID") @@ -64,6 +65,9 @@ public class TermConceptMap implements Serializable { @Column(name = "URL", nullable = false, length = MAX_URL_LENGTH) private String myUrl; + @Column(name = "VER", nullable = true, length = MAX_VER_LENGTH) + private String myVersion; + @OneToMany(mappedBy = "myConceptMap") private List 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, + "URL exceeds maximum length (" + MAX_URL_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/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index b03dc4aec20..b8cde329e0e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -131,6 +131,7 @@ import org.quartz.JobExecutionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -1514,6 +1515,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TermConceptMap termConceptMap = new TermConceptMap(); termConceptMap.setResource(theResourceTable); termConceptMap.setUrl(theConceptMap.getUrl()); + termConceptMap.setVersion(theConceptMap.getVersion()); String source = theConceptMap.hasSourceUriType() ? theConceptMap.getSourceUriType().getValueAsString() : null; String target = theConceptMap.hasTargetUriType() ? theConceptMap.getTargetUriType().getValueAsString() : null; @@ -1547,7 +1549,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { * Do the upload. */ String conceptMapUrl = termConceptMap.getUrl(); - Optional optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl); + String conceptMapVersion = termConceptMap.getVersion(); + Optional optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); if (!optionalExistingTermConceptMapByUrl.isPresent()) { try { if (isNotBlank(source)) { @@ -1630,8 +1633,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { String msg = myContext.getLocalizer().getMessage( BaseTermReadSvcImpl.class, - "cannotCreateDuplicateConceptMapUrl", - conceptMapUrl, + "cannotCreateDuplicateConceptMapUrlAndVersion", + conceptMapUrl, conceptMapVersion, existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); throw new UnprocessableEntityException(msg); 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..271ef8f7c2e 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.findTermConceptMapByUrl(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.findTermConceptMapByUrl(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.findTermConceptMapByUrl(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..6caf695c8d0 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.findTermConceptMapByUrl(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.findTermConceptMapByUrl(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.findTermConceptMapByUrl(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/term/TerminologySvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java index d0ff1cebb37..bdcbfb101ed 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 @@ -25,6 +25,8 @@ import org.hl7.fhir.utilities.validation.ValidationOptions; 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; @@ -292,10 +294,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.findTermConceptMapByUrl(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); - TermConceptMap conceptMap = optionalConceptMap.get(); + TermConceptMap conceptMap = optionalConceptMap.get(0); ourLog.info("ConceptMap:\n" + conceptMap.toString()); @@ -470,10 +473,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.findTermConceptMapByUrl(page, CM_URL); + assertEquals(1, optionalConceptMap.size()); - TermConceptMap conceptMap = optionalConceptMap.get(); + TermConceptMap conceptMap = optionalConceptMap.get(0); ourLog.info("ConceptMap:\n" + conceptMap.toString()); From 45b88de3b67e6536aad82fc6f119334ec83a6e28 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sun, 6 Sep 2020 11:47:07 -0400 Subject: [PATCH 14/71] Fixed typo --- .../src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java index 02a0937a4ef..7c6e6e14c1d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java @@ -141,7 +141,7 @@ public class TermConceptMap implements Serializable { public TermConceptMap setVersion(String theVersion) { ValidateUtil.isNotTooLongOrThrowIllegalArgument(theVersion, MAX_VER_LENGTH, - "URL exceeds maximum length (" + MAX_URL_LENGTH + "): " + length(theVersion)); + "Version exceeds maximum length (" + MAX_VER_LENGTH + "): " + length(theVersion)); myVersion = theVersion; return this; } From 1ecec0abc923bedf44872324a2c3a5e4f878391c Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sun, 6 Sep 2020 15:23:26 -0400 Subject: [PATCH 15/71] Fixed duplicate ConceptMap URL only --- .../ca/uhn/fhir/i18n/hapi-messages.properties | 1 + .../fhir/jpa/dao/data/ITermConceptMapDao.java | 4 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 46 +++++++++++++++---- 3 files changed, 40 insertions(+), 11 deletions(-) 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 aecaba0f495..71aee0c17e2 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 @@ -142,6 +142,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.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! 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 7e7d1933939..f3b609faa6e 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 @@ -39,9 +39,7 @@ public interface ITermConceptMapDao extends JpaRepository @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid") Optional findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid); - //-- Replaced with next method. Should it be removed? - @Deprecated - @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url") + @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url and cm.myVersion is null") Optional findTermConceptMapByUrl(@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") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index b8cde329e0e..99f7617ebcd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1550,7 +1550,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { */ String conceptMapUrl = termConceptMap.getUrl(); String conceptMapVersion = termConceptMap.getVersion(); - Optional optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); + Optional optionalExistingTermConceptMapByUrl = null; + if (isBlank(conceptMapVersion)) { + optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl); + } else { + optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); + } if (!optionalExistingTermConceptMapByUrl.isPresent()) { try { if (isNotBlank(source)) { @@ -1631,13 +1636,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } else { TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapByUrl.get(); - String msg = myContext.getLocalizer().getMessage( - BaseTermReadSvcImpl.class, - "cannotCreateDuplicateConceptMapUrlAndVersion", - conceptMapUrl, conceptMapVersion, - existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); - - throw new UnprocessableEntityException(msg); + if (isBlank(conceptMapVersion)) { + String msg = myContext.getLocalizer().getMessage( + BaseTermReadSvcImpl.class, + "cannotCreateDuplicateConceptMapUrl", + conceptMapUrl, + existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + 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()); @@ -1949,6 +1963,14 @@ 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()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVerion"), translationQuery.getConceptMapVersion().getValueAsString())); + } + if (translationQuery.hasSource()) { predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString())); } @@ -2032,6 +2054,14 @@ 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()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVerion"), translationQuery.getConceptMapVersion().getValueAsString())); + } + if (translationQuery.hasTargetSystem()) { predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem().getValueAsString())); } From 81cd73f820b6da861207c5eb3bcc630fd86f2c77 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sun, 6 Sep 2020 15:27:29 -0400 Subject: [PATCH 16/71] Added test cases to for duplicate url and version of ConceptMap --- .../jpa/term/TerminologySvcImplR4Test.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 bdcbfb101ed..c0849888342 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 @@ -54,6 +54,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) { @@ -268,6 +275,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); From d019dee786b9c741a4d10cc348d9b320591f56b4 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sun, 6 Sep 2020 16:11:09 -0400 Subject: [PATCH 17/71] Added url/conceptMapVersion for ConceptMap to support translate --- .../fhir/jpa/api/model/TranslationQuery.java | 33 ++++++++++++++ .../jpa/api/model/TranslationRequest.java | 43 +++++++++++++++++-- 2 files changed, 73 insertions(+), 3 deletions(-) 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(); } From 54100426434b5feb9d99f41965ad8c5fbe6b91e7 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Mon, 7 Sep 2020 10:43:58 -0400 Subject: [PATCH 18/71] Multi-version supported for ConceptMap $translate operation --- .../ca/uhn/fhir/i18n/hapi-messages.properties | 2 + ...aseJpaResourceProviderConceptMapDstu3.java | 15 ++ .../BaseJpaResourceProviderConceptMapR4.java | 14 ++ .../BaseJpaResourceProviderConceptMapR5.java | 14 ++ .../fhir/jpa/term/BaseTermReadSvcImpl.java | 4 +- .../ResourceProviderDstu3ConceptMapTest.java | 103 +++++++++- .../r4/ResourceProviderR4ConceptMapTest.java | 188 +++++++++++++++++- .../r5/ResourceProviderR5ConceptMapTest.java | 109 ++++++++++ 8 files changed, 439 insertions(+), 10 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ConceptMapTest.java 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 71aee0c17e2..ea6f163b24e 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 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/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 Date: Mon, 7 Sep 2020 18:09:01 -0400 Subject: [PATCH 19/71] Fixed NullPointerException for reverse $translate operation. --- .../dao/r4/FhirResourceDaoConceptMapR4.java | 9 ++- .../dao/r5/FhirResourceDaoConceptMapR5.java | 38 +++++---- .../ResourceProviderDstu3ConceptMapTest.java | 79 +++++++++++++++++++ .../r5/ResourceProviderR5ConceptMapTest.java | 79 +++++++++++++++++++ 4 files changed, 188 insertions(+), 17 deletions(-) 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 IFhirResourceDaoConceptMap { @Autowired @@ -153,7 +155,11 @@ public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao Date: Mon, 7 Sep 2020 20:54:35 -0400 Subject: [PATCH 20/71] Updated R5 reverse $translate test case for more coverage --- .../provider/r5/ResourceProviderR5ConceptMapTest.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 index fac01a68c10..04b55803ce4 100644 --- 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 @@ -13,6 +13,7 @@ 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; @@ -121,7 +122,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test element1.setCode("12222").setDisplay("Source Code 12222"); TargetElementComponent target1 = element1.addTarget(); - target1.setCode("11111").setDisplay("11111"); + target1.setCode("11111").setDisplay("11111").setRelationship(ConceptMapRelationship.EQUIVALENT); IIdType conceptMapId1 = myConceptMapDao.create(conceptMap1, mySrd).getId().toUnqualifiedVersionless(); conceptMap1 = myConceptMapDao.read(conceptMapId1); @@ -139,7 +140,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test element2.setCode("13333").setDisplay("Source Code 13333"); TargetElementComponent target2 = element2.addTarget(); - target2.setCode("11111").setDisplay("Target Code 11111"); + target2.setCode("11111").setDisplay("Target Code 11111").setRelationship(ConceptMapRelationship.EQUIVALENT); IIdType conceptMapId2 = myConceptMapDao.create(conceptMap2, mySrd).getId().toUnqualifiedVersionless(); conceptMap2 = myConceptMapDao.read(conceptMapId2); @@ -174,8 +175,10 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test assertEquals(1, getNumberOfParametersByName(respParams, "match")); param = getParametersByName(respParams, "match").get(0); - assertEquals(2, param.getPart().size()); - ParametersParameterComponent part = getPartByName(param, "concept"); + 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()); From b4a87adc640081b396658527a072178998021d2b Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 9 Sep 2020 16:53:14 -0400 Subject: [PATCH 21/71] Suggested test that includes case where ConceptVersion is not specified in $translate operation. --- ...ourceProviderDstu3ConceptMap_Ian_Test.java | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java new file mode 100644 index 00000000000..2bc51dc1649 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java @@ -0,0 +1,206 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +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.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.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderDstu3ConceptMap_Ian_Test extends BaseResourceProviderDstu3Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMap_Ian_Test.class); + + @BeforeEach + @Transactional + public void before02() { + //- conceptMap v1 + ConceptMap conceptMap = new ConceptMap(); + conceptMap.setUrl(CM_URL).setVersion("v1").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("12222").setDisplay("Target Code 12222").setEquivalence(ConceptMapEquivalence.EQUAL); + + IIdType conceptMapId1 = myConceptMapDao.create(conceptMap, mySrd).getId().toUnqualifiedVersionless(); + conceptMap = myConceptMapDao.read(conceptMapId1); + + ourLog.info("ConceptMap: v1 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + //- conceptMap v2 + conceptMap = new ConceptMap(); + conceptMap.setUrl(CM_URL).setVersion("v2").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); + + ConceptMapGroupComponent group2 = conceptMap.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(conceptMap, mySrd).getId().toUnqualifiedVersionless(); + conceptMap = myConceptMapDao.read(conceptMapId2); + + ourLog.info("ConceptMap: v2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_v2() { + + // Call translate with ConceptMap v2. + 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 = 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(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_v1() { + + // Call translate with ConceptMap v1. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_NoVersion() { + + // Call translate with no ConceptMap version. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + +} From c873b6d6dd382717ba5ff9c6683abfe9a4c6837e Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Wed, 9 Sep 2020 22:24:02 -0400 Subject: [PATCH 22/71] Fixed ConceptMap $translation without conceptMapVersion issue. --- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 68 +++++- .../ResourceProviderDstu3ConceptMapTest.java | 202 ++++++++++++++---- ...ourceProviderDstu3ConceptMap_Ian_Test.java | 160 +++++++++++--- 3 files changed, 346 insertions(+), 84 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index af88a1606ad..817df66d9bd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1937,6 +1937,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) { @@ -1963,12 +1969,23 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem().getValueAsString())); } - if (translationQuery.hasUrl()) { + if (translationQuery.hasUrl()) { predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString())); - } - - if (translationQuery.hasConceptMapVersion()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().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"))); + } + } + } else { + if (translationQuery.hasConceptMapVersion()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString())); + } } if (translationQuery.hasSource()) { @@ -2028,6 +2045,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) { @@ -2054,12 +2077,23 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); } - if (translationQuery.hasUrl()) { + if (translationQuery.hasUrl()) { predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl().getValueAsString())); - } - - if (translationQuery.hasConceptMapVersion()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().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"))); + } + } + } else { + if (translationQuery.hasConceptMapVersion()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString())); + } } if (translationQuery.hasTargetSystem()) { @@ -2127,6 +2161,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.findTermConceptMapByUrl(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) { 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 d7847d48747..9f7505160bc 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 @@ -211,45 +211,12 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst @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"); - - 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"); - - IIdType conceptMapId2 = myConceptMapDao.create(conceptMap2, mySrd).getId().toUnqualifiedVersionless(); - conceptMap2 = myConceptMapDao.read(conceptMapId2); - - ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2)); - - + 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(CM_URL)); + 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)); @@ -284,6 +251,163 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst assertEquals(CS_URL, coding.getSystem()); assertEquals("Version 1", coding.getVersion()); part = getPartByName(param, "source"); - assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseConcaptMapUrl_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 testTranslateWithReverseConcaptMapUrl_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 testTranslateWithReverseConcaptMapUrl_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 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/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java index 2bc51dc1649..afd5964b9e5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java @@ -26,50 +26,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ResourceProviderDstu3ConceptMap_Ian_Test extends BaseResourceProviderDstu3Test { private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMap_Ian_Test.class); + @BeforeEach @Transactional public void before02() { - //- conceptMap v1 - ConceptMap conceptMap = new ConceptMap(); - conceptMap.setUrl(CM_URL).setVersion("v1").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("12222").setDisplay("Target Code 12222").setEquivalence(ConceptMapEquivalence.EQUAL); - - IIdType conceptMapId1 = myConceptMapDao.create(conceptMap, mySrd).getId().toUnqualifiedVersionless(); - conceptMap = myConceptMapDao.read(conceptMapId1); - - ourLog.info("ConceptMap: v1 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); - - //- conceptMap v2 - conceptMap = new ConceptMap(); - conceptMap.setUrl(CM_URL).setVersion("v2").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2)); - - ConceptMapGroupComponent group2 = conceptMap.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(conceptMap, mySrd).getId().toUnqualifiedVersionless(); - conceptMap = myConceptMapDao.read(conceptMapId2); - - ourLog.info("ConceptMap: v2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); } @Test public void testTranslateWithVersionedConcaptMapUrl_v2() { + createConceptMap("v1", "12222", "Target Code 12222"); + createConceptMap("v2", "13333", "Target Code 13333"); + // Call translate with ConceptMap v2. Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); @@ -116,6 +85,9 @@ public class ResourceProviderDstu3ConceptMap_Ian_Test extends BaseResourceProvid @Test public void testTranslateWithVersionedConcaptMapUrl_v1() { + createConceptMap("v1", "12222", "Target Code 12222"); + createConceptMap("v2", "13333", "Target Code 13333");; + // Call translate with ConceptMap v1. Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); @@ -162,6 +134,9 @@ public class ResourceProviderDstu3ConceptMap_Ian_Test extends BaseResourceProvid @Test public void testTranslateWithVersionedConcaptMapUrl_NoVersion() { + createConceptMap("v1", "12222", "Target Code 12222"); + createConceptMap("v2", "13333", "Target Code 13333"); + // Call translate with no ConceptMap version. Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CM_URL)); @@ -203,4 +178,119 @@ public class ResourceProviderDstu3ConceptMap_Ian_Test extends BaseResourceProvid assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); } + @Test + public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v1() { + + createConceptMap(null, "12222", "Target Code 12222"); // first version is null + createConceptMap("v2", "13333", "Target Code 13333"); + + // Call translate with no ConceptMap version. + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v2() { + + createConceptMap("v1", "12222", "Target Code 12222"); + createConceptMap(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(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + private void createConceptMap(String version, String targetCode, String targetDisplay) { + + ConceptMap conceptMap = new ConceptMap(); + conceptMap.setUrl(CM_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)); + } + + } From 972bdbd87d86c23f8c6800e5f6a2e438733547c2 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Thu, 10 Sep 2020 21:17:41 -0400 Subject: [PATCH 23/71] Updated based on code review --- .../fhir/jpa/dao/data/ITermConceptMapDao.java | 4 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 16 +- .../FhirResourceDaoDstu3ConceptMapTest.java | 8 +- .../r4/FhirResourceDaoR4ConceptMapTest.java | 6 +- .../ResourceProviderDstu3ConceptMapTest.java | 358 +++++++++++++++--- ...ourceProviderDstu3ConceptMap_Ian_Test.java | 296 --------------- .../jpa/term/TerminologySvcImplR4Test.java | 4 +- 7 files changed, 325 insertions(+), 367 deletions(-) delete mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java 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 f3b609faa6e..616432e2d69 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 @@ -40,10 +40,10 @@ public interface ITermConceptMapDao extends JpaRepository Optional findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid); @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url and cm.myVersion is null") - Optional findTermConceptMapByUrl(@Param("url") String theUrl); + 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 findTermConceptMapByUrl(Pageable thePage, @Param("url") String theUrl); + 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/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 817df66d9bd..7553e6dbd3f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1552,7 +1552,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { String conceptMapVersion = termConceptMap.getVersion(); Optional optionalExistingTermConceptMapByUrl = null; if (isBlank(conceptMapVersion)) { - optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl); + optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndNullVersion(conceptMapUrl); } else { optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); } @@ -1982,12 +1982,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); } } - } else { - if (translationQuery.hasConceptMapVersion()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString())); - } - } - + } + if (translationQuery.hasSource()) { predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString())); } @@ -2090,10 +2086,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); } } - } else { - if (translationQuery.hasConceptMapVersion()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion().getValueAsString())); - } } if (translationQuery.hasTargetSystem()) { @@ -2166,7 +2158,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { Pageable page = PageRequest.of(0, 1); - List theConceptMapList = myConceptMapDao.findTermConceptMapByUrl(page, + List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theTranslationRequest.getUrl().asStringValue()); if (!theConceptMapList.isEmpty()) { return theConceptMapList.get(0).getVersion(); 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 271ef8f7c2e..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 @@ -111,7 +111,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { public void testConcaptMapFindTermConceptMapByUrl() { Pageable page = PageRequest.of(0, 1); - List theExpConceptMapList = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL); + List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); assertEquals(1, theExpConceptMapList.size()); assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl()); @@ -143,7 +143,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { // should return the latest one which is v2 Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); @@ -169,9 +169,9 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { assertEquals(theUrl, theExpConceptMapV1.get().getUrl()); assertEquals("v1", theExpConceptMapV1.get().getVersion()); - // should return the latest one which is v2 + // should return the latest one which in this case is not versioned Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); 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 6caf695c8d0..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 @@ -1158,7 +1158,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { public void testConcaptMapFindTermConceptMapByUrl() { Pageable page = PageRequest.of(0, 1); - List theExpConceptMapList = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL); + List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); assertEquals(1, theExpConceptMapList.size()); assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl()); @@ -1190,7 +1190,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { // should return the latest one which is v2 Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); @@ -1218,7 +1218,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { // should return the latest one which is v2 Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); 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 9f7505160bc..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 @@ -9,15 +9,15 @@ 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.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.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; @@ -25,7 +25,6 @@ import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Transactional; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.api.IGenericClient; public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDstu3Test { private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMapTest.class); @@ -129,46 +128,14 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithConcaptMapUrlAndVersion() { + public void testTranslateWithConceptMapUrlAndVersion() { - //- 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)); + 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(CM_URL)); + 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)); @@ -176,7 +143,6 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); - Parameters respParams = ourClient .operation() .onType(ConceptMap.class) @@ -205,11 +171,287 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst assertEquals(CS_URL_2, coding.getSystem()); assertEquals("Version 2", coding.getVersion()); part = getPartByName(param, "source"); - assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + assertEquals(url, ((UriType) part.getValue()).getValueAsString()); } @Test - public void testTranslateWithReverseConcaptMapUrlAndVersion() { + 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"); @@ -255,7 +497,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithReverseConcaptMapUrl_NoVersion() { + public void testTranslateWithReverseConceptMapUrl_NoVersion() { String url = "http://url"; createReverseConceptMap(url, "v1", "12222", "Source Code 12222"); @@ -300,7 +542,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithReverseConcaptMapUrl_NoVersion_null_v1() { + public void testTranslateWithReverseConceptMapUrl_NoVersion_null_v1() { String url = "http://url"; createReverseConceptMap(url, null, "12222", "Source Code 12222"); @@ -345,7 +587,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithReverseConcaptMapUrl_NoVersion_null_v2() { + public void testTranslateWithReverseConceptMapUrl_NoVersion_null_v2() { String url = "http://url"; createReverseConceptMap(url, "v1", "12222", "Source Code 12222"); @@ -389,6 +631,26 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst 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 diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java deleted file mode 100644 index afd5964b9e5..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMap_Ian_Test.java +++ /dev/null @@ -1,296 +0,0 @@ -package ca.uhn.fhir.jpa.provider.dstu3; - -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.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.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ResourceProviderDstu3ConceptMap_Ian_Test extends BaseResourceProviderDstu3Test { - private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMap_Ian_Test.class); - - - @BeforeEach - @Transactional - public void before02() { - - } - - @Test - public void testTranslateWithVersionedConcaptMapUrl_v2() { - - createConceptMap("v1", "12222", "Target Code 12222"); - createConceptMap("v2", "13333", "Target Code 13333"); - - // Call translate with ConceptMap v2. - 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 = 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(CM_URL, ((UriType) part.getValue()).getValueAsString()); - - } - - @Test - public void testTranslateWithVersionedConcaptMapUrl_v1() { - - createConceptMap("v1", "12222", "Target Code 12222"); - createConceptMap("v2", "13333", "Target Code 13333");; - - // Call translate with ConceptMap v1. - Parameters inParams = new Parameters(); - inParams.addParameter().setName("url").setValue(new UriType(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); - - } - - @Test - public void testTranslateWithVersionedConcaptMapUrl_NoVersion() { - - createConceptMap("v1", "12222", "Target Code 12222"); - createConceptMap("v2", "13333", "Target Code 13333"); - - // Call translate with no ConceptMap version. - Parameters inParams = new Parameters(); - inParams.addParameter().setName("url").setValue(new UriType(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); - } - - @Test - public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v1() { - - createConceptMap(null, "12222", "Target Code 12222"); // first version is null - createConceptMap("v2", "13333", "Target Code 13333"); - - // Call translate with no ConceptMap version. - Parameters inParams = new Parameters(); - inParams.addParameter().setName("url").setValue(new UriType(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); - } - - @Test - public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v2() { - - createConceptMap("v1", "12222", "Target Code 12222"); - createConceptMap(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(CM_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(CM_URL, ((UriType) part.getValue()).getValueAsString()); - } - - private void createConceptMap(String version, String targetCode, String targetDisplay) { - - ConceptMap conceptMap = new ConceptMap(); - conceptMap.setUrl(CM_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)); - } - - -} 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 c0849888342..f6d11c922be 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 @@ -315,7 +315,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { @Override protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { Pageable page = PageRequest.of(0, 1); - List optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); assertEquals(1, optionalConceptMap.size()); TermConceptMap conceptMap = optionalConceptMap.get(0); @@ -494,7 +494,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { @Override protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { Pageable page = PageRequest.of(0, 1); - List optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); assertEquals(1, optionalConceptMap.size()); TermConceptMap conceptMap = optionalConceptMap.get(0); From 16a5f57ae955531b35926971a6b929ae48521809 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Thu, 10 Sep 2020 21:41:06 -0400 Subject: [PATCH 24/71] Added for ConceptMap DB migration --- .../uhn/fhir/jpa/entity/TermConceptMap.java | 2 +- .../tasks/HapiFhirJpaMigrationTasks.java | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java index 7c6e6e14c1d..6760772b3cc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java @@ -41,7 +41,7 @@ public class TermConceptMap implements Serializable { private static final long serialVersionUID = 1L; static final int MAX_URL_LENGTH = 200; - static final int MAX_VER_LENGTH = 200; + public static final int MAX_VER_LENGTH = 200; @Id() @SequenceGenerator(name = "SEQ_CONCEPT_MAP_PID", sequenceName = "SEQ_CONCEPT_MAP_PID") 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 9418769cf84..c0bce6464fb 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 @@ -1,5 +1,11 @@ package ca.uhn.fhir.jpa.migrate.tasks; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + /*- * #%L * HAPI FHIR JPA Server - Migration @@ -21,6 +27,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.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask; @@ -39,12 +46,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri; import ca.uhn.fhir.jpa.model.entity.SearchParamPresent; import ca.uhn.fhir.util.VersionEnum; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - @SuppressWarnings({"SqlNoDataSourceInspection", "SpellCheckingInspection"}) public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { @@ -141,6 +142,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").nonNullable().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"); } protected void init510_20200725() { From 448caa3f938a4b62c4aa21ca8814174008474f0a Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Fri, 11 Sep 2020 09:02:46 -0400 Subject: [PATCH 25/71] Changed the ConceptMap.VER to nullable --- .../uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c0bce6464fb..5761620aa63 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 @@ -145,7 +145,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { //ConceptMap add version for search Builder.BuilderWithTableName trmConceptMap = version.onTable("TRM_CONCEPT_MAP"); - trmConceptMap.addColumn("20200910.1", "VER").nonNullable().type(ColumnTypeEnum.STRING, TermConceptMap.MAX_VER_LENGTH); + 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"); } From 750cbf4ba3ffd22afbd71518e5b41d229bd66750 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Fri, 11 Sep 2020 09:20:01 -0400 Subject: [PATCH 26/71] @Deprecated the old findTermConceptMapByUrl method --- .../java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapDao.java | 5 +++++ 1 file changed, 5 insertions(+) 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 616432e2d69..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 @@ -39,6 +39,11 @@ public interface ITermConceptMapDao extends JpaRepository @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid") Optional findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid); + // 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); From 91c1df7ce48871d39a673d360361a450d629470a Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Fri, 11 Sep 2020 21:38:42 -0400 Subject: [PATCH 27/71] Supported ConceptMap Version from the properties --- .../fhir/jpa/term/loinc/BaseLoincHandler.java | 14 ++++++++-- .../LoincIeeeMedicalDeviceCodeHandler.java | 26 +++++++++++-------- .../LoincPartRelatedCodeMappingHandler.java | 5 ++++ .../term/loinc/LoincRsnaPlaybookHandler.java | 6 +++++ .../term/TerminologyLoaderSvcLoincTest.java | 7 ++--- 5 files changed, 42 insertions(+), 16 deletions(-) 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 45d18b792dc..ab73585d29f 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; - private 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) @@ -226,6 +226,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; @@ -263,6 +264,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 75bf1fbc2b6..85c0b39026b 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 @@ -1,5 +1,17 @@ package ca.uhn.fhir.jpa.term.loinc; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static org.apache.commons.lang3.StringUtils.trim; + +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.csv.CSVRecord; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.ValueSet; + /*- * #%L * HAPI FHIR JPA Server @@ -21,18 +33,8 @@ package ca.uhn.fhir.jpa.term.loinc; */ import ca.uhn.fhir.jpa.entity.TermConcept; -import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; -import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.r4.model.ConceptMap; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.ValueSet; - -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import static org.apache.commons.lang3.StringUtils.trim; +import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implements IRecordHandler { @@ -51,6 +53,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")); @@ -63,6 +66,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implemen new ConceptMapping() .setConceptMapId(LOINC_IEEE_CM_ID) .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 5fefd31a549..8e100c6b2eb 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 @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; @@ -75,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 loincIeeeCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + Enumerations.ConceptMapEquivalence equivalence; switch (trim(defaultString(mapType))) { case "": @@ -130,6 +134,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme new ConceptMapping() .setConceptMapId(loincPartMapId) .setConceptMapUri(loincPartMapUri) + .setConceptMapVersion(loincIeeeCmVersion) .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 690b7effd73..7fcf71dd3cf 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 @@ -31,6 +31,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.*; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; @@ -80,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 loincIeeeCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + // RSNA Codes VS ValueSet vs; if (!myIdToValueSet.containsKey(RSNA_CODES_VS_ID)) { @@ -176,6 +180,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor new ConceptMapping() .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI) + .setConceptMapVersion(loincIeeeCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) .setSourceCode(partNumber) @@ -193,6 +198,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor new ConceptMapping() .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI) + .setConceptMapVersion(loincIeeeCmVersion) .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/term/TerminologyLoaderSvcLoincTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java index c41a29eddb6..00bfb529f62 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 @@ -17,12 +17,10 @@ import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsUsHandler; import ca.uhn.fhir.jpa.term.loinc.LoincUniversalOrderSetHandler; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.ValueSet; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -167,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()); @@ -220,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(); @@ -236,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(); @@ -302,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()); From 0d929d4d191e993b63ff085fb617d84ccc0d248d Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Mon, 14 Sep 2020 09:30:03 -0400 Subject: [PATCH 28/71] Updated based on review comments --- .../jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java | 4 ++-- .../uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) 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 8e100c6b2eb..bfff1d1d273 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 @@ -77,7 +77,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme String extCodeSystemCopyrightNotice = trim(theRecord.get("ExtCodeSystemCopyrightNotice")); // ConceptMap version from properties files - String loincIeeeCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String loincPartMapVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); Enumerations.ConceptMapEquivalence equivalence; switch (trim(defaultString(mapType))) { @@ -134,7 +134,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme new ConceptMapping() .setConceptMapId(loincPartMapId) .setConceptMapUri(loincPartMapUri) - .setConceptMapVersion(loincIeeeCmVersion) + .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 7fcf71dd3cf..51c54734dff 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 @@ -82,7 +82,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor String longName = trim(theRecord.get("LongName")); // ConceptMap version from properties files - String loincIeeeCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String loincRsnaCmVersion = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); // RSNA Codes VS ValueSet vs; @@ -180,7 +180,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor new ConceptMapping() .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI) - .setConceptMapVersion(loincIeeeCmVersion) + .setConceptMapVersion(loincRsnaCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) .setSourceCode(partNumber) @@ -198,7 +198,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor new ConceptMapping() .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI) - .setConceptMapVersion(loincIeeeCmVersion) + .setConceptMapVersion(loincRsnaCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) .setSourceCode(loincNumber) From cd104ad6358ba4d2bf350bed002945491bc3615a Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 15 Sep 2020 10:21:42 -0400 Subject: [PATCH 29/71] Revised approach for versioning of ValueSet. --- .../fhir/jpa/dao/data/ITermValueSetDao.java | 15 +- .../jpa/dao/data/ITermValueSetVersionDao.java | 52 ---- .../dstu3/FhirResourceDaoValueSetDstu3.java | 7 +- .../dao/expunge/ExpungeEverythingService.java | 11 +- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 25 +- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 7 +- .../ca/uhn/fhir/jpa/entity/TermValueSet.java | 105 +++++++- .../fhir/jpa/entity/TermValueSetVersion.java | 249 ------------------ .../fhir/jpa/term/BaseTermReadSvcImpl.java | 205 +++++--------- .../term/TermCodeSystemStorageSvcImpl.java | 2 +- .../jpa/term/TermDeferredStorageSvcImpl.java | 1 + .../jpa/term/ValueSetConceptAccumulator.java | 48 ++-- .../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 4 - .../FhirResourceDaoDstu3TerminologyTest.java | 4 + .../FhirResourceDaoDstu3ValueSetTest.java | 20 +- .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 5 - ...esourceDaoR4SearchWithElasticSearchIT.java | 1 + ...sourceDaoR4TerminologyElasticsearchIT.java | 1 + .../r4/FhirResourceDaoR4TerminologyTest.java | 5 + .../dao/r4/FhirResourceDaoR4ValidateTest.java | 32 +-- .../ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java | 6 +- .../ResourceProviderDstu3ValueSetTest.java | 1 + .../r4/ResourceProviderR4CodeSystemTest.java | 3 - ...urceProviderR4CodeSystemVersionedTest.java | 3 - .../r4/ResourceProviderR4ValueSetTest.java | 30 ++- .../r4/TerminologyUploaderProviderR4Test.java | 2 +- .../r5/ResourceProviderR5ValueSetTest.java | 29 +- .../jpa/term/TerminologySvcImplDstu3Test.java | 11 +- .../jpa/term/TerminologySvcImplR4Test.java | 17 +- .../term/ValueSetConceptAccumulatorTest.java | 12 +- .../jpa/term/ValueSetExpansionR4Test.java | 113 +++++--- 31 files changed, 388 insertions(+), 638 deletions(-) delete mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java delete mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java index 40dbc15027e..33883b6073c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java @@ -29,18 +29,27 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.List; import java.util.Optional; public interface ITermValueSetDao extends JpaRepository { - @Query("SELECT vs FROM TermValueSet vs WHERE vs.myResourcePid = :resource_pid") Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url") Optional findByUrl(@Param("url") String theUrl); - @Query("SELECT vs FROM TermValueSet vs WHERE vs.myCurrentVersion.myId = :vsv_pid") - Optional findWithCodeSystemVersionAsCurrentVersion(@Param("vsv_pid") Long theValueSetVersionPid); + @Query("SELECT vs FROM TermValueSet vs WHERE vs.myExpansionStatus = :expansion_status") + Slice findByExpansionStatus(Pageable pageable, @Param("expansion_status") TermValueSetPreExpansionStatusEnum theExpansionStatus); + + @Query(value="SELECT vs FROM TermValueSet vs INNER JOIN ResourceTable r ON r.myId = vs.myResourcePid WHERE vs.myUrl = :url ORDER BY r.myUpdated DESC") + List findTermValueSetByUrl(Pageable thePage, @Param("url") String theUrl); + + @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url AND vs.myVersion IS NULL") + Optional findTermValueSetByUrlAndNullVersion(@Param("url") String theUrl); + + @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url AND vs.myVersion = :version") + Optional findTermValueSetByUrlAndVersion(@Param("url") String theUrl, @Param("version") String theVersion); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java deleted file mode 100644 index b81d4a69914..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetVersionDao.java +++ /dev/null @@ -1,52 +0,0 @@ -package ca.uhn.fhir.jpa.dao.data; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2020 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; -import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; - -import java.util.List; -import java.util.Optional; - -public interface ITermValueSetVersionDao extends JpaRepository { - - @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myResourcePid = :resource_pid") - Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); - - @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myValueSetPid = :valueset_pid AND vsv.myValueSetVersionId = :version" ) - TermValueSetVersion findByValueSetPidAndVersion(@Param("valueset_pid") Long theValueSetPid, @Param("version") String theVersion); - - @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myValueSetPid = :valueset_pid AND vsv.myValueSetVersionId IS NULL" ) - TermValueSetVersion findByValueSetPidAndNullVersion(@Param("valueset_pid") Long theValueSetPid); - - @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myExpansionStatus = :expansion_status") - Slice findByExpansionStatus(Pageable pageable, @Param("expansion_status") TermValueSetPreExpansionStatusEnum theExpansionStatus); - - @Query("SELECT vsv FROM TermValueSetVersion vsv WHERE vsv.myValueSetPid = :valueset_pid") - List findByValueSetPid(@Param("valueset_pid") Long theValueSetPid); - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 9e6e229c04f..9def15576aa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -65,9 +65,6 @@ import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet; public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoValueSetDstu3.class); - @Autowired - private ITermReadSvc myHapiTerminologySvc; - @Autowired private DefaultProfileValidationSupport myDefaultProfileValidationSupport; @@ -283,12 +280,12 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao { - counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermValueSet.class.getSimpleName() + " d SET d.myCurrentVersion = null")); - return null; - }); +// myTxTemplate.execute(t -> { +// counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermValueSet.class.getSimpleName() + " d SET d.myCurrentVersion = null")); +// return null; +// }); counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class)); counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class)); counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class)); @@ -140,7 +139,7 @@ public class ExpungeEverythingService { counter.addAndGet(expungeEverythingByType(SearchInclude.class)); counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class)); counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class)); - counter.addAndGet(expungeEverythingByType(TermValueSetVersion.class)); +// counter.addAndGet(expungeEverythingByType(TermValueSetVersion.class)); counter.addAndGet(expungeEverythingByType(TermValueSet.class)); counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class)); counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java index 7a104f1ba97..20695859f3f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java @@ -60,9 +60,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { - @Autowired - private ITermReadSvc myHapiTerminologySvc; - @Autowired private DefaultProfileValidationSupport myDefaultProfileValidationSupport; @@ -113,17 +110,14 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao ValueSet source = new ValueSet(); source.setUrl(theUri); -// source.getCompose().addInclude().addValueSet(theUri); + source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { -// ConceptSetComponent include = source.getCompose().addInclude(); -// ConceptSetFilterComponent filter = include.addFilter(); - ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter(); + ConceptSetComponent include = source.getCompose().addInclude(); + ConceptSetFilterComponent filter = include.addFilter(); filter.setProperty("display"); filter.setOp(FilterOperator.EQUAL); filter.setValue(theFilter); - } else { - source.getCompose().addInclude().addValueSet(theUri); } ValueSet retVal = doExpand(source); @@ -151,17 +145,14 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao ValueSet source = new ValueSet(); source.setUrl(theUri); -// source.getCompose().addInclude().addValueSet(theUri); + source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { -// ConceptSetComponent include = source.getCompose().addInclude(); -// ConceptSetFilterComponent filter = include.addFilter(); - ConceptSetFilterComponent filter = source.getCompose().addInclude().addValueSet(theUri).addFilter(); + ConceptSetComponent include = source.getCompose().addInclude(); + ConceptSetFilterComponent filter = include.addFilter(); filter.setProperty("display"); filter.setOp(FilterOperator.EQUAL); filter.setValue(theFilter); - } else { - source.getCompose().addInclude().addValueSet(theUri); } ValueSet retVal = doExpand(source, theOffset, theCount); @@ -268,9 +259,9 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) { if (retVal.getDeleted() == null) { ValueSet valueSet = (ValueSet) theResource; - myHapiTerminologySvc.storeTermValueSet(retVal, valueSet); + myTerminologySvc.storeTermValueSet(retVal, valueSet); } else { - myHapiTerminologySvc.deleteValueSetAndChildren(retVal); + myTerminologySvc.deleteValueSetAndChildren(retVal); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 5001ca5420a..7000debd950 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -64,9 +64,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { - @Autowired - private ITermReadSvc myHapiTerminologySvc; - @Autowired @Qualifier("myDefaultProfileValidationSupport") private IValidationSupport myDefaultProfileValidationSupport; @@ -267,9 +264,9 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao if (myDaoConfig.isPreExpandValueSets() && !retVal.isUnchangedInCurrentOperation()) { if (retVal.getDeleted() == null) { ValueSet valueSet = (ValueSet) theResource; - myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSet)); + myTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet40_50.convertValueSet(valueSet)); } else { - myHapiTerminologySvc.deleteValueSetAndChildren(retVal); + myTerminologySvc.deleteValueSetAndChildren(retVal); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java index c137c3d4643..4cbe867d6cf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java @@ -25,16 +25,19 @@ import ca.uhn.fhir.util.ValidateUtil; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.ColumnDefault; import javax.annotation.Nonnull; import javax.persistence.*; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; @Table(name = "TRM_VALUESET", uniqueConstraints = { - @UniqueConstraint(name = "IDX_VALUESET_URL", columnNames = {"URL"}) + @UniqueConstraint(name = "IDX_VALUESET_URL", columnNames = {"URL", "VER"}) }) @Entity() public class TermValueSet implements Serializable { @@ -43,12 +46,7 @@ public class TermValueSet implements Serializable { public static final int MAX_EXPANSION_STATUS_LENGTH = 50; public static final int MAX_NAME_LENGTH = 200; public static final int MAX_URL_LENGTH = 200; - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "CURRENT_VERSION_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_TRMVALUESET_CURVER")) - private TermValueSetVersion myCurrentVersion; - @Column(name = "CURRENT_VERSION_PID", nullable = true, insertable = false, updatable = false) - private Long myCurrentVersionPid; + static final int MAX_VER_LENGTH = 200; @Id() @SequenceGenerator(name = "SEQ_VALUESET_PID", sequenceName = "SEQ_VALUESET_PID") @@ -59,6 +57,9 @@ public class TermValueSet implements Serializable { @Column(name = "URL", nullable = false, length = MAX_URL_LENGTH) private String myUrl; + @Column(name = "VER", nullable = true, length = MAX_VER_LENGTH) + private String myVersion; + @OneToOne() @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMVALUESET_RES")) private ResourceTable myResource; @@ -69,11 +70,29 @@ public class TermValueSet implements Serializable { @Column(name = "VSNAME", nullable = true, length = MAX_NAME_LENGTH) private String myName; + @OneToMany(mappedBy = "myValueSet", fetch = FetchType.LAZY) + private List myConcepts; + + @Column(name = "TOTAL_CONCEPTS", nullable = false) + @ColumnDefault("0") + private Long myTotalConcepts; + + @Column(name = "TOTAL_CONCEPT_DESIGNATIONS", nullable = false) + @ColumnDefault("0") + private Long myTotalConceptDesignations; + + @Enumerated(EnumType.STRING) + @Column(name = "EXPANSION_STATUS", nullable = false, length = MAX_EXPANSION_STATUS_LENGTH) + private TermValueSetPreExpansionStatusEnum myExpansionStatus; + @Transient private transient Integer myHashCode; public TermValueSet() { super(); + myExpansionStatus = TermValueSetPreExpansionStatusEnum.NOT_EXPANDED; + myTotalConcepts = 0L; + myTotalConceptDesignations = 0L; } public Long getId() { @@ -110,12 +129,72 @@ public class TermValueSet implements Serializable { return this; } - public TermValueSetVersion getCurrentVersion() { - return myCurrentVersion; + public List getConcepts() { + if (myConcepts == null) { + myConcepts = new ArrayList<>(); + } + + return myConcepts; } - public TermValueSet setCurrentVersion(TermValueSetVersion theCurrentVersion) { - myCurrentVersion = theCurrentVersion; + public Long getTotalConcepts() { + return myTotalConcepts; + } + + public TermValueSet setTotalConcepts(Long theTotalConcepts) { + myTotalConcepts = theTotalConcepts; + return this; + } + + public TermValueSet decrementTotalConcepts() { + if (myTotalConcepts > 0) { + myTotalConcepts--; + } + return this; + } + + public TermValueSet incrementTotalConcepts() { + myTotalConcepts++; + return this; + } + + public Long getTotalConceptDesignations() { + return myTotalConceptDesignations; + } + + public TermValueSet setTotalConceptDesignations(Long theTotalConceptDesignations) { + myTotalConceptDesignations = theTotalConceptDesignations; + return this; + } + + public TermValueSet decrementTotalConceptDesignations() { + if (myTotalConceptDesignations > 0) { + myTotalConceptDesignations--; + } + return this; + } + + public TermValueSet incrementTotalConceptDesignations() { + myTotalConceptDesignations++; + return this; + } + + public TermValueSetPreExpansionStatusEnum getExpansionStatus() { + return myExpansionStatus; + } + + public void setExpansionStatus(TermValueSetPreExpansionStatusEnum theExpansionStatus) { + myExpansionStatus = theExpansionStatus; + } + + public String getVersion() { + return myVersion; + } + + public TermValueSet setVersion(String theVersion) { + ValidateUtil.isNotTooLongOrThrowIllegalArgument(theVersion, MAX_VER_LENGTH, + "Version exceeds maximum length (" + MAX_VER_LENGTH + "): " + length(theVersion)); + myVersion = theVersion; return this; } @@ -148,6 +227,10 @@ public class TermValueSet implements Serializable { .append(myResource != null ? ("myResource=" + myResource.toString()) : ("myResource=(null)")) .append("myResourcePid", myResourcePid) .append("myName", myName) + .append(myConcepts != null ? ("myConcepts - size=" + myConcepts.size()) : ("myConcepts=(null)")) + .append("myTotalConcepts", myTotalConcepts) + .append("myTotalConceptDesignations", myTotalConceptDesignations) + .append("myExpansionStatus", myExpansionStatus) .toString(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java deleted file mode 100644 index 3b9bb8280f2..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetVersion.java +++ /dev/null @@ -1,249 +0,0 @@ -package ca.uhn.fhir.jpa.entity; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2020 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.util.ValidateUtil; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import org.hibernate.annotations.ColumnDefault; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -import static org.apache.commons.lang3.StringUtils.length; - -@Table(name = "TRM_VALUESET_VER", uniqueConstraints = { - @UniqueConstraint(name = "IDX_VALUESET_URL_AND_VER", columnNames = {"VALUESET_PID", "VS_VERSION_ID"}) -}) -@Entity() -public class TermValueSetVersion implements Serializable { - private static final long serialVersionUID = 1L; - - public static final int MAX_EXPANSION_STATUS_LENGTH = 50; - public static final int MAX_NAME_LENGTH = 200; - public static final int MAX_VERSION_LENGTH = 200; - - @Id() - @SequenceGenerator(name = "SEQ_VALUESETVER_PID", sequenceName = "SEQ_VALUESETVER_PID") - @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESETVER_PID") - @Column(name = "PID") - private Long myId; - - @OneToOne() - @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMVALUESETVER_RES")) - private ResourceTable myResource; - - @Column(name = "RES_ID", insertable = false, updatable = false) - private Long myResourcePid; - - @OneToMany(mappedBy = "myValueSet", fetch = FetchType.LAZY) - private List myConcepts; - - @Column(name = "TOTAL_CONCEPTS", nullable = false) - @ColumnDefault("0") - private Long myTotalConcepts; - - @Column(name = "TOTAL_CONCEPT_DESIGNATIONS", nullable = false) - @ColumnDefault("0") - private Long myTotalConceptDesignations; - - @Enumerated(EnumType.STRING) - @Column(name = "EXPANSION_STATUS", nullable = false, length = MAX_EXPANSION_STATUS_LENGTH) - private TermValueSetPreExpansionStatusEnum myExpansionStatus; - - @Column(name = "VS_VERSION_ID", nullable = true, updatable = false, length = MAX_VERSION_LENGTH) - private String myValueSetVersionId; - - /** - * This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration - * issued. It should be made non-nullable at some point. - */ - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "VALUESET_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_VALSETVER_VS_ID")) - private TermValueSet myValueSet; - - @Column(name = "VALUESET_PID", insertable = false, updatable = false) - private Long myValueSetPid; - - @SuppressWarnings("unused") - @OneToOne(mappedBy = "myCurrentVersion", optional = true, fetch = FetchType.LAZY) - private TermValueSet myValueSetHavingThisVersionAsCurrentVersionIfAny; - - public TermValueSetVersion() { - super(); - myExpansionStatus = TermValueSetPreExpansionStatusEnum.NOT_EXPANDED; - myTotalConcepts = 0L; - myTotalConceptDesignations = 0L; - } - - public Long getId() { - return myId; - } - - public ResourceTable getResource() { - return myResource; - } - - public TermValueSetVersion setResource(ResourceTable theResource) { - myResource = theResource; - return this; - } - - public List getConcepts() { - if (myConcepts == null) { - myConcepts = new ArrayList<>(); - } - - return myConcepts; - } - - public Long getTotalConcepts() { - return myTotalConcepts; - } - - public TermValueSetVersion setTotalConcepts(Long theTotalConcepts) { - myTotalConcepts = theTotalConcepts; - return this; - } - - public TermValueSetVersion decrementTotalConcepts() { - if (myTotalConcepts > 0) { - myTotalConcepts--; - } - return this; - } - - public TermValueSetVersion incrementTotalConcepts() { - myTotalConcepts++; - return this; - } - - public Long getTotalConceptDesignations() { - return myTotalConceptDesignations; - } - - public TermValueSetVersion setTotalConceptDesignations(Long theTotalConceptDesignations) { - myTotalConceptDesignations = theTotalConceptDesignations; - return this; - } - - public TermValueSetVersion decrementTotalConceptDesignations() { - if (myTotalConceptDesignations > 0) { - myTotalConceptDesignations--; - } - return this; - } - - public TermValueSetVersion incrementTotalConceptDesignations() { - myTotalConceptDesignations++; - return this; - } - - public TermValueSetPreExpansionStatusEnum getExpansionStatus() { - return myExpansionStatus; - } - - public void setExpansionStatus(TermValueSetPreExpansionStatusEnum theExpansionStatus) { - myExpansionStatus = theExpansionStatus; - } - - public String getValueSetVersionId() { - return myValueSetVersionId; - } - - public TermValueSetVersion setValueSetVersionId(String theValueSetVersionId) { - ValidateUtil.isNotTooLongOrThrowIllegalArgument( - theValueSetVersionId, MAX_VERSION_LENGTH, - "Version ID exceeds maximum length (" + MAX_VERSION_LENGTH + "): " + length(theValueSetVersionId)); - myValueSetVersionId = theValueSetVersionId; - return this; - } - - public TermValueSet getValueSet() { - return myValueSet; - } - - public TermValueSetVersion setValueSet(TermValueSet theValueSet) { - myValueSet = theValueSet; - return this; - } - - public Long getValueSetPid() { - return myValueSetPid; - } - - @Override - public boolean equals(Object theO) { - if (this == theO) return true; - - if (!(theO instanceof TermValueSetVersion)) return false; - - TermValueSetVersion that = (TermValueSetVersion) theO; - - return new EqualsBuilder() - .append(myValueSetVersionId, that.myValueSetVersionId) - .append(myValueSetPid, that.myValueSetPid) - .isEquals(); - } - - @Override - public int hashCode() { - HashCodeBuilder b = new HashCodeBuilder(17, 37); - b.append(myValueSetVersionId); - b.append(myValueSetPid); - return b.toHashCode(); - } - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) - .append("myId", myId) - .append(myResource != null ? ("myResource=" + myResource.toString()) : ("myResource=(null)")) - .append("myResourcePid", myResourcePid) - .append("valueSetPid", myValueSetPid) - .append("valueSetVersionId", myValueSetVersionId) - .append(myConcepts != null ? ("myConcepts - size=" + myConcepts.size()) : ("myConcepts=(null)")) - .append("myTotalConcepts", myTotalConcepts) - .append("myTotalConceptDesignations", myTotalConceptDesignations) - .append("myExpansionStatus", myExpansionStatus) - .toString(); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 06576adb74f..e38d53284b8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -45,7 +45,6 @@ import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptViewDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; @@ -63,7 +62,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptView; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; @@ -83,7 +81,6 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.CoverageIgnore; -import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; @@ -207,8 +204,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Autowired protected ITermValueSetDao myValueSetDao; @Autowired - protected ITermValueSetVersionDao myValueSetVersionDao; - @Autowired protected ITermValueSetConceptDao myValueSetConceptDao; @Autowired protected ITermValueSetConceptDesignationDao myValueSetConceptDesignationDao; @@ -338,37 +333,16 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public void deleteValueSetForResource(ResourceTable theResourceTable) { // Get existing entity so it can be deleted. - Optional optionalExistingTermValueSetVersionById = myValueSetVersionDao.findByResourcePid(theResourceTable.getId()); + Optional optionalExistingTermValueSetById = myValueSetDao.findByResourcePid(theResourceTable.getId()); - if (optionalExistingTermValueSetVersionById.isPresent()) { - TermValueSetVersion existingTermValueSetVersion = optionalExistingTermValueSetVersionById.get(); + if (optionalExistingTermValueSetById.isPresent()) { + TermValueSet existingTermValueSet = optionalExistingTermValueSetById.get(); - ourLog.info("Deleting existing TermValueSetVersion[{}] and its children...", existingTermValueSetVersion.getId()); - myValueSetConceptDesignationDao.deleteByTermValueSetId(existingTermValueSetVersion.getId()); - myValueSetConceptDao.deleteByTermValueSetId(existingTermValueSetVersion.getId()); - - // Check if this is the current version. If so, clear the current version from TermValueSet. - TermValueSet termValueSet = existingTermValueSetVersion.getValueSet(); - TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); - txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - Boolean versionIsCurrent = termValueSet.getCurrentVersion().getId() == existingTermValueSetVersion.getId(); - if (versionIsCurrent) { - txTemplate.executeWithoutResult(t -> { - termValueSet.setCurrentVersion(null); - myValueSetDao.save(termValueSet); - }); - } - - myValueSetVersionDao.deleteById(existingTermValueSetVersion.getId()); - ourLog.info("Done deleting existing TermValueSetVersion[{}] and its children.", existingTermValueSetVersion.getId()); - - // If this is the current version, then delete the TermValueSet master as well. - if (versionIsCurrent) { - txTemplate.executeWithoutResult(t -> { - ourLog.info(" * Deleting value set {}", termValueSet.getId()); - myValueSetDao.deleteById(termValueSet.getId()); - }); - } + ourLog.info("Deleting existing TermValueSet[{}] and its children...", existingTermValueSet.getId()); + myValueSetConceptDesignationDao.deleteByTermValueSetId(existingTermValueSet.getId()); + myValueSetConceptDao.deleteByTermValueSetId(existingTermValueSet.getId()); + myValueSetDao.deleteById(existingTermValueSet.getId()); + ourLog.info("Done deleting existing TermValueSet[{}] and its children.", existingTermValueSet.getId()); } } @@ -418,7 +392,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { Optional optionalTermValueSet; if (theValueSetToExpand.hasUrl()) { - optionalTermValueSet = myValueSetDao.findByUrl(theValueSetToExpand.getUrl()); + if (theValueSetToExpand.hasVersion()) { + optionalTermValueSet = myValueSetDao.findTermValueSetByUrlAndVersion(theValueSetToExpand.getUrl(), theValueSetToExpand.getVersion()); + } else { + optionalTermValueSet = myValueSetDao.findTermValueSetByUrlAndNullVersion(theValueSetToExpand.getUrl()); + } } else { optionalTermValueSet = Optional.empty(); } @@ -429,17 +407,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } TermValueSet termValueSet = optionalTermValueSet.get(); - TermValueSetVersion termValueSetVersion; - String valueSetVersion = theValueSetToExpand.getVersion(); - if (valueSetVersion != null) { - termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndVersion(termValueSet.getId(),theValueSetToExpand.getVersion()); - } else { - termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - } - if (termValueSetVersion.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { + if (termValueSet.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { ourLog.warn("{} is present in terminology tables but not ready for persistence-backed invocation of operation $expand. Will perform in-memory expansion without parameters. Current status: {} | {}", - getValueSetInfo(theValueSetToExpand), termValueSetVersion.getExpansionStatus().name(), termValueSetVersion.getExpansionStatus().getDescription()); + getValueSetInfo(theValueSetToExpand), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); return expandValueSetInMemory(theExpansionOptions, theValueSetToExpand, null); // In-memory expansion. } @@ -450,7 +421,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions); int offset = expansionOptions.getOffset(); int count = expansionOptions.getCount(); - populateExpansionComponent(expansionComponent, termValueSetVersion, offset, count, termValueSet.getUrl()); + populateExpansionComponent(expansionComponent, termValueSet, offset, count, termValueSet.getUrl()); ValueSet valueSet = new ValueSet(); valueSet.setStatus(Enumerations.PublicationStatus.ACTIVE); @@ -459,8 +430,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return valueSet; } - private void populateExpansionComponent(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSetVersion theTermValueSetVersion, int theOffset, int theCount, String theValueSetUrl) { - int total = theTermValueSetVersion.getTotalConcepts().intValue(); + private void populateExpansionComponent(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount, String theValueSetUrl) { + int total = theTermValueSet.getTotalConcepts().intValue(); theExpansionComponent.setTotal(total); theExpansionComponent.setOffset(theOffset); theExpansionComponent.addParameter().setName("offset").setValue(new IntegerType(theOffset)); @@ -470,14 +441,14 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return; } - expandConcepts(theExpansionComponent, theTermValueSetVersion, theOffset, theCount, theValueSetUrl); + expandConcepts(theExpansionComponent, theTermValueSet, theOffset, theCount, theValueSetUrl); } - private void expandConcepts(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSetVersion theTermValueSetVersion, int theOffset, int theCount, String theValueSetUrl) { + private void expandConcepts(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount, String theValueSetUrl) { int conceptsExpanded = 0; int designationsExpanded = 0; int toIndex = theOffset + theCount; - Collection conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theOffset, toIndex, theTermValueSetVersion.getId()); + Collection conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theOffset, toIndex, theTermValueSet.getId()); if (conceptViews.isEmpty()) { logConceptsExpanded("No concepts to expand. ", theValueSetUrl, conceptsExpanded); @@ -1273,17 +1244,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } TermValueSet termValueSet = optionalTermValueSet.get(); - TermValueSetVersion termValueSetVersion; - String valueSetVersion = theValueSet.getVersion(); - if (valueSetVersion != null) { - termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndVersion(termValueSet.getId(), valueSetVersion); - } else { - termValueSetVersion = myValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - } - if (termValueSetVersion.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { + if (termValueSet.getExpansionStatus() != TermValueSetPreExpansionStatusEnum.EXPANDED) { ourLog.warn("{} is present in terminology tables but not ready for persistence-backed invocation of operation $validation-code. Will perform in-memory code validation. Current status: {} | {}", - getValueSetInfo(theValueSet), termValueSetVersion.getExpansionStatus().name(), termValueSetVersion.getExpansionStatus().getDescription()); + getValueSetInfo(theValueSet), termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); return false; } @@ -1698,15 +1662,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { while (true) { StopWatch sw = new StopWatch(); - TermValueSetVersion valueSetToExpand = txTemplate.execute(t -> { - Optional optionalTermValueSetVersion = getNextTermValueSetNotExpanded(); - if (!optionalTermValueSetVersion.isPresent()) { + TermValueSet valueSetToExpand = txTemplate.execute(t -> { + Optional optionalTermValueSet = getNextTermValueSetNotExpanded(); + if (!optionalTermValueSet.isPresent()) { return null; } - TermValueSetVersion termValueSetVersion = optionalTermValueSetVersion.get(); - termValueSetVersion.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANSION_IN_PROGRESS); - return myValueSetVersionDao.saveAndFlush(termValueSetVersion); + TermValueSet termValueSet = optionalTermValueSet.get(); + termValueSet.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANSION_IN_PROGRESS); + return myValueSetDao.saveAndFlush(termValueSet); }); if (valueSetToExpand == null) { return; @@ -1715,18 +1679,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { // We have a ValueSet to pre-expand. try { ValueSet valueSet = txTemplate.execute(t -> { - TermValueSetVersion refreshedValueSetVersionToExpand = myValueSetVersionDao.findById(valueSetToExpand.getId()).get(); - return getValueSetFromResourceTable(refreshedValueSetVersionToExpand.getResource()); + TermValueSet refreshedValueSetToExpand = myValueSetDao.findById(valueSetToExpand.getId()).get(); + return getValueSetFromResourceTable(refreshedValueSetToExpand.getResource()); }); - ValueSetConceptAccumulator accumulator = new ValueSetConceptAccumulator(valueSetToExpand, myValueSetVersionDao, myValueSetDao, myValueSetConceptDao, myValueSetConceptDesignationDao); + ValueSetConceptAccumulator accumulator = new ValueSetConceptAccumulator(valueSetToExpand, myValueSetDao, myValueSetConceptDao, myValueSetConceptDesignationDao); expandValueSet(null, valueSet, accumulator); // We are done with this ValueSet. txTemplate.execute(t -> { - TermValueSetVersion finalValueSetToExpand = myValueSetVersionDao.findById(valueSetToExpand.getId()).get(); - finalValueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANDED); - myValueSetVersionDao.saveAndFlush(finalValueSetToExpand); + valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.EXPANDED); + myValueSetDao.saveAndFlush(valueSetToExpand); return null; }); @@ -1736,7 +1699,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ourLog.error("Failed to pre-expand ValueSet: " + e.getMessage(), e); txTemplate.execute(t -> { valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND); - myValueSetVersionDao.saveAndFlush(valueSetToExpand); + myValueSetDao.saveAndFlush(valueSetToExpand); return null; }); } @@ -1801,9 +1764,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { protected abstract ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable); - private Optional getNextTermValueSetNotExpanded() { - Optional retVal = Optional.empty(); - Slice page = myValueSetVersionDao.findByExpansionStatus(PageRequest.of(0, 1), TermValueSetPreExpansionStatusEnum.NOT_EXPANDED); + private Optional getNextTermValueSetNotExpanded() { + Optional retVal = Optional.empty(); + Slice page = myValueSetDao.findByExpansionStatus(PageRequest.of(0, 1), TermValueSetPreExpansionStatusEnum.NOT_EXPANDED); if (!page.getContent().isEmpty()) { retVal = Optional.of(page.getContent().get(0)); @@ -1823,7 +1786,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Get CodeSystem and validate CodeSystemVersion */ - TermValueSet termValueSet = getOrCreateDistinctTermValueSet(theResourceTable.getPersistentId(), theValueSet.getUrl(), theValueSet.getName(), theValueSet.getVersion(), theResourceTable); + TermValueSet termValueSet = new TermValueSet(); + termValueSet.setResource(theResourceTable); + termValueSet.setUrl(theValueSet.getUrl()); + termValueSet.setVersion(theValueSet.getVersion()); + termValueSet.setName(theValueSet.hasName() ? theValueSet.getName() : null); // Delete version being replaced deleteValueSetForResource(theResourceTable); @@ -1831,77 +1798,31 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Do the upload. */ - TermValueSetVersion termValueSetVersion = termValueSet.getCurrentVersion(); - if (termValueSetVersion == null) { - termValueSetVersion = new TermValueSetVersion(); - } - - termValueSetVersion.setResource(theResourceTable); - termValueSetVersion.setValueSetVersionId(theValueSet.getVersion()); - termValueSetVersion.setValueSet(termValueSet); - - termValueSetVersion = myValueSetVersionDao.saveAndFlush(termValueSetVersion); - - termValueSet.setCurrentVersion(termValueSetVersion); - termValueSet = myValueSetDao.saveAndFlush(termValueSet); - - ourLog.info("Done storing TermValueSet[{}] for {}", termValueSet.getId(), theValueSet.getIdElement().toVersionless().getValueAsString()); - } - - - @Nonnull - private TermValueSet getOrCreateDistinctTermValueSet(ResourcePersistentId theValueSetResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, ResourceTable theValueSetResourceTable) { - Optional termValueSetOptional = myValueSetDao.findByUrl(theSystemUri); - TermValueSet termValueSet; - if (!termValueSetOptional.isPresent()) { - termValueSetOptional = myValueSetDao.findByResourcePid(theValueSetResourcePid.getIdAsLong()); - if (!termValueSetOptional.isPresent()) { - termValueSet = new TermValueSet(); - } else { - termValueSet = termValueSetOptional.get(); - } + String url = termValueSet.getUrl(); + String version = termValueSet.getVersion(); + Optional optionalExistingTermValueSetByUrl; + if (version != null) { + optionalExistingTermValueSetByUrl = myValueSetDao.findTermValueSetByUrlAndVersion(url, version); } else { - termValueSet = termValueSetOptional.get(); + optionalExistingTermValueSetByUrl = myValueSetDao.findTermValueSetByUrlAndNullVersion(url); } - - termValueSet.setResource(theValueSetResourceTable); - termValueSet.setUrl(theSystemUri); - termValueSet.setName(theSystemName); - termValueSet = myValueSetDao.save(termValueSet); - checkForValueSetVersionDuplicate(termValueSet,theSystemUri, theSystemVersionId, theValueSetResourceTable); - return termValueSet; - } - - - private void checkForValueSetVersionDuplicate(TermValueSet theValueSet, String theSystemUri, String theSystemVersionId, ResourceTable theValueSetResourceTable) { - // Check if TermValueSetVersion entity already exists. - TermValueSetVersion valueSetVersionEntity; - String msg = null; - if (theSystemVersionId == null) { - // Check if a non-versioned TermValueSetVersion entity already exists for this TermValueSet. - valueSetVersionEntity = myValueSetVersionDao.findByValueSetPidAndNullVersion(theValueSet.getId()); - if (valueSetVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateValueSetUrl", theSystemUri, valueSetVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); - } + if (!optionalExistingTermValueSetByUrl.isPresent()) { + myValueSetDao.save(termValueSet); } else { - // Check if a TermValueSetVersion entity already exists for this TermValueSet and version. - valueSetVersionEntity = myValueSetVersionDao.findByValueSetPidAndVersion(theValueSet.getId(), theSystemVersionId); - if (valueSetVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateValueSetUrlAndVersion", theSystemUri, theSystemVersionId, valueSetVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + TermValueSet existingTermValueSet = optionalExistingTermValueSetByUrl.get(); + String msg; + if (version != null) { + msg = myContext.getLocalizer().getMessage( + BaseTermReadSvcImpl.class, + "cannotCreateDuplicateValueSetUrlAndVersion", + url, version, existingTermValueSet.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } else { - // Check if a TermValueSetVersion entity already exists for this ValueSet resource (i.e. with a different version or URL) - Optional valueSetVersionEntityOptional = myValueSetVersionDao.findByResourcePid(theValueSetResourceTable.getId()); - if (valueSetVersionEntityOptional.isPresent()) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotUpdateUrlOrVersionForValueSetResource", theSystemUri, theSystemVersionId, valueSetVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); - throw new UnprocessableEntityException(msg); - } - } - } - // Throw exception if the TermValueSet version is being duplicated. - if (valueSetVersionEntity != null) { - if (!ObjectUtil.equals(valueSetVersionEntity.getResource().getId(), theValueSetResourceTable.getId())) { - throw new UnprocessableEntityException(msg); + msg = myContext.getLocalizer().getMessage( + BaseTermReadSvcImpl.class, + "cannotCreateDuplicateValueSetUrl", + url, existingTermValueSet.getResource().getIdDt().toUnqualifiedVersionless().getValue()); } + throw new UnprocessableEntityException(msg); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index 2098fbbce5f..ac19b652593 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -715,7 +715,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { // Check if a TermCodeSystemVersion entity already exists for this CodeSystem resource (i.e. with a different version or URL) codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourceTable.getId()); if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotUpdateUrlOrVersionForCodeSystemResource", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); + msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotUpdateUrlOrVersionForCodeSystemResource", codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue(), theSystemUri, theSystemVersionId); throw new UnprocessableEntityException(msg); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java index 5c5fe0c5a07..e27177f3f1a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java @@ -227,6 +227,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { myDeferredConceptMaps.clear(); myDeferredConcepts.clear(); myDefferedCodeSystemsDeletions.clear(); + myConceptLinksToSaveLater.clear(); } @Transactional(propagation = Propagation.NEVER) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java index 5b444ee6a5a..fda53d03c5e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java @@ -23,12 +23,10 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.entity.TermConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.util.ValidateUtil; import javax.annotation.Nonnull; @@ -43,8 +41,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValueSetConceptAccumulator.class); - private TermValueSetVersion myTermValueSetVersion; - final private ITermValueSetVersionDao myValueSetVersionDao; + private TermValueSet myTermValueSet; final private ITermValueSetDao myValueSetDao; final private ITermValueSetConceptDao myValueSetConceptDao; final private ITermValueSetConceptDesignationDao myValueSetConceptDesignationDao; @@ -52,9 +49,8 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { private int myDesignationsSaved; private int myConceptsExcluded; - public ValueSetConceptAccumulator(@Nonnull TermValueSetVersion theTermValueSetVersion, @Nonnull ITermValueSetVersionDao theValueSetVersionDao, @Nonnull ITermValueSetDao theValueSetDao, @Nonnull ITermValueSetConceptDao theValueSetConceptDao, @Nonnull ITermValueSetConceptDesignationDao theValueSetConceptDesignationDao) { - myTermValueSetVersion = theTermValueSetVersion; - myValueSetVersionDao = theValueSetVersionDao; + public ValueSetConceptAccumulator(@Nonnull TermValueSet theTermValueSet, @Nonnull ITermValueSetDao theValueSetDao, @Nonnull ITermValueSetConceptDao theValueSetConceptDao, @Nonnull ITermValueSetConceptDesignationDao theValueSetConceptDesignationDao) { + myTermValueSet = theTermValueSet; myValueSetDao = theValueSetDao; myValueSetConceptDao = theValueSetConceptDao; myValueSetConceptDesignationDao = theValueSetConceptDesignationDao; @@ -91,23 +87,23 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } // Get existing entity so it can be deleted. - Optional optionalConcept = myValueSetConceptDao.findByTermValueSetIdSystemAndCode(myTermValueSetVersion.getId(), theSystem, theCode); + Optional optionalConcept = myValueSetConceptDao.findByTermValueSetIdSystemAndCode(myTermValueSet.getId(), theSystem, theCode); if (optionalConcept.isPresent()) { TermValueSetConcept concept = optionalConcept.get(); - ourLog.debug("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSetVersion.getValueSet().getUrl()); + ourLog.debug("Excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl()); for (TermValueSetConceptDesignation designation : concept.getDesignations()) { myValueSetConceptDesignationDao.deleteById(designation.getId()); - myTermValueSetVersion.decrementTotalConceptDesignations(); + myTermValueSet.decrementTotalConceptDesignations(); } myValueSetConceptDao.deleteById(concept.getId()); - myTermValueSetVersion.decrementTotalConcepts(); - myValueSetVersionDao.save(myTermValueSetVersion); - ourLog.debug("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSetVersion.getValueSet().getUrl()); + myTermValueSet.decrementTotalConcepts(); + myValueSetDao.save(myTermValueSet); + ourLog.debug("Done excluding [{}|{}] from ValueSet[{}]", concept.getSystem(), concept.getCode(), myTermValueSet.getUrl()); if (++myConceptsExcluded % 250 == 0) { - ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSetVersion.getValueSet().getUrl()); + ourLog.info("Have excluded {} concepts from ValueSet[{}]", myConceptsExcluded, myTermValueSet.getUrl()); } } } @@ -116,9 +112,8 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystem, "ValueSet contains a concept with no system value"); ValidateUtil.isNotBlankOrThrowInvalidRequest(theCode, "ValueSet contains a concept with no code value"); - myTermValueSetVersion = myValueSetVersionDao.findById(myTermValueSetVersion.getId()).get(); TermValueSetConcept concept = new TermValueSetConcept(); - concept.setValueSet(myTermValueSetVersion.getValueSet()); + concept.setValueSet(myTermValueSet); concept.setOrder(myConceptsSaved); concept.setSystem(theSystem); concept.setCode(theCode); @@ -126,23 +121,21 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { concept.setDisplay(theDisplay); } myValueSetConceptDao.save(concept); - myTermValueSetVersion.incrementTotalConcepts(); - myValueSetVersionDao.save(myTermValueSetVersion); + myValueSetDao.save(myTermValueSet.incrementTotalConcepts()); if (++myConceptsSaved % 250 == 0) { - ourLog.info("Have pre-expanded {} concepts in ValueSet[{}]", myConceptsSaved, myTermValueSetVersion.getValueSet().getUrl()); + ourLog.info("Have pre-expanded {} concepts in ValueSet[{}]", myConceptsSaved, myTermValueSet.getUrl()); } return concept; } - private void saveConceptDesignation(TermValueSetConcept theConcept, TermConceptDesignation theDesignation) { + private TermValueSetConceptDesignation saveConceptDesignation(TermValueSetConcept theConcept, TermConceptDesignation theDesignation) { ValidateUtil.isNotBlankOrThrowInvalidRequest(theDesignation.getValue(), "ValueSet contains a concept designation with no value"); TermValueSetConceptDesignation designation = new TermValueSetConceptDesignation(); designation.setConcept(theConcept); - myTermValueSetVersion = myValueSetVersionDao.findById(myTermValueSetVersion.getId()).get(); - designation.setValueSet(myTermValueSetVersion.getValueSet()); + designation.setValueSet(myTermValueSet); designation.setLanguage(theDesignation.getLanguage()); if (isNoneBlank(theDesignation.getUseSystem(), theDesignation.getUseCode())) { designation.setUseSystem(theDesignation.getUseSystem()); @@ -153,12 +146,13 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } designation.setValue(theDesignation.getValue()); myValueSetConceptDesignationDao.save(designation); - myValueSetVersionDao.save(myTermValueSetVersion.incrementTotalConceptDesignations()); + myValueSetDao.save(myTermValueSet.incrementTotalConceptDesignations()); if (++myDesignationsSaved % 250 == 0) { - ourLog.debug("Have pre-expanded {} designations for Concept[{}|{}] in ValueSet[{}]", myDesignationsSaved, theConcept.getSystem(), theConcept.getCode(), myTermValueSetVersion.getValueSet().getUrl()); + ourLog.debug("Have pre-expanded {} designations for Concept[{}|{}] in ValueSet[{}]", myDesignationsSaved, theConcept.getSystem(), theConcept.getCode(), myTermValueSet.getUrl()); } + return designation; } public Boolean removeGapsFromConceptOrder() { @@ -166,13 +160,13 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { return false; } - ourLog.info("Removing gaps from concept order for ValueSet[{}]", myTermValueSetVersion.getValueSet().getUrl()); + ourLog.info("Removing gaps from concept order for ValueSet[{}]", myTermValueSet.getUrl()); int order = 0; - List conceptIds = myValueSetConceptDao.findIdsByTermValueSetId(myTermValueSetVersion.getId()); + List conceptIds = myValueSetConceptDao.findIdsByTermValueSetId(myTermValueSet.getId()); for (Long conceptId : conceptIds) { myValueSetConceptDao.updateOrderById(conceptId, order++); } - ourLog.info("Have removed gaps from concept order for {} concepts in ValueSet[{}]", conceptIds.size(), myTermValueSetVersion.getValueSet().getUrl()); + ourLog.info("Have removed gaps from concept order for {} concepts in ValueSet[{}]", conceptIds.size(), myTermValueSet.getUrl()); return true; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index 0484f6e1ae5..7c8a52d6a48 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -29,7 +29,6 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.model.entity.ModelConfig; @@ -53,7 +52,6 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; -import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.hibernate.search.jpa.FullTextEntityManager; @@ -344,8 +342,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { private IBulkDataExportSvc myBulkDataExportSvc; @Autowired protected ITermValueSetDao myTermValueSetDao; - @Autowired - protected ITermValueSetVersionDao myTermValueSetVersionDao; @AfterEach() public void afterCleanupDao() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index d34393b7b61..7c21676735e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -80,6 +80,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { private CodeSystem createExternalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setName("ACME Codes"); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -124,6 +125,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { private void createExternalCsLarge() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -165,6 +167,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { private CodeSystem createExternalCsDogs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -698,6 +701,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { public void testLookupSnomed() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl("http://snomed.info/sct"); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java index ca5e887afad..9500b926ac7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java @@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeType; @@ -23,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional; import java.io.IOException; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; @@ -59,9 +59,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { runInTransaction(() -> { TermValueSet vsEntity = myTermValueSetDao.findByUrl("http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.2--20171231000000").orElseThrow(() -> new IllegalStateException()); - Long valueSetId = vsEntity.getId(); - TermValueSetVersion vsvEntity = myTermValueSetVersionDao.findByValueSetPid(valueSetId).get(0); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsvEntity.getExpansionStatus()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsEntity.getExpansionStatus()); }); IValidationSupport.CodeValidationResult validationOutcome; @@ -79,12 +77,12 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { validationOutcome = myValueSetDao.validateCode(vsIdentifier, null, code, system, null, null, null, mySrd); assertEquals(false, validationOutcome.isOk()); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); runInTransaction(() -> { TermValueSet vsEntity = myTermValueSetDao.findByUrl("http://decor.nictiz.nl/fhir/ValueSet/2.16.840.1.113883.2.4.3.11.60.40.2.20.5.2--20171231000000").orElseThrow(() -> new IllegalStateException()); - TermValueSetVersion vsvEntity = myTermValueSetVersionDao.findByValueSetPid(vsEntity.getId()).get(0); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsvEntity.getExpansionStatus()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsEntity.getExpansionStatus()); }); // Validate good @@ -99,6 +97,16 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { } + private boolean clearDeferredStorageQueue() { + + if(!myTerminologyDeferredStorageSvc.isStorageQueueEmpty()) { + myTerminologyDeferredStorageSvc.saveAllDeferred(); + return false; + } else { + return true; + } + + } @Test @Disabled diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 5fe669b636a..12ef9a2206a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -47,7 +47,6 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.entity.TermCodeSystem; @@ -65,7 +64,6 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; -import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; @@ -82,7 +80,6 @@ import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.test.utilities.ITestDataBuilder; -import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; @@ -439,8 +436,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired protected ITermValueSetDao myTermValueSetDao; @Autowired - protected ITermValueSetVersionDao myTermValueSetVersionDao; - @Autowired protected ITermValueSetConceptDao myTermValueSetConceptDao; @Autowired protected ITermValueSetConceptDesignationDao myTermValueSetConceptDesignationDao; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java index 2139b93b543..1b614aa7d04 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java @@ -209,6 +209,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest { private CodeSystem createExternalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyElasticsearchIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyElasticsearchIT.java index e5c298a8011..098754e33ea 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyElasticsearchIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyElasticsearchIT.java @@ -85,6 +85,7 @@ public class FhirResourceDaoR4TerminologyElasticsearchIT extends BaseJpaTest { public void testExpandWithIncludeContainingDashesInInclude() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index 63b88299e1b..90d30645a5b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -80,6 +80,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { private CodeSystem createExternalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -129,6 +130,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { private CodeSystem createExternalCsDogs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -159,6 +161,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { private void createExternalCsLarge() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -473,6 +476,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { public void testExpandWithIncludeContainingDashesInInclude() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -815,6 +819,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { public void testLookupSnomed() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl("http://snomed.info/sct"); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 0cf1bc2d874..0ef54220a16 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -7,11 +7,8 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl; -import ca.uhn.fhir.jpa.term.TerminologyLoaderSvcLoincTest; -import ca.uhn.fhir.jpa.term.ZipCollectionBuilder; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; @@ -81,7 +78,6 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; 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 static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -212,9 +208,10 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { myStructureDefinitionDao.create(loadResourceFromClasspath(StructureDefinition.class, "/r4/bl/bb-sd.json")); runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit","20190731").orElseThrow(() -> new IllegalArgumentException()); +// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); }); OperationOutcome outcome; @@ -242,17 +239,19 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { // Before, the VS wasn't pre-expanded. Try again with it pre-expanded runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit","20190731").orElseThrow(() -> new IllegalArgumentException()); +// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); }); myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables(); runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit", "20190731").orElseThrow(() -> new IllegalArgumentException()); +// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsv.getExpansionStatus()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vs.getExpansionStatus()); }); // Use a code that's in the ValueSet @@ -285,9 +284,10 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { myStructureDefinitionDao.create(loadResourceFromClasspath(StructureDefinition.class, "/r4/bl/bb-sd.json")); runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); - TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit", "20190731").orElseThrow(() -> new IllegalArgumentException()); +// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); }); OperationOutcome outcome; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index e3967c118b4..9cd0bb49e5a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -44,7 +44,6 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor; import ca.uhn.fhir.jpa.model.entity.ModelConfig; @@ -70,7 +69,6 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.BasePagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; -import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; @@ -385,8 +383,8 @@ public abstract class BaseJpaR5Test extends BaseJpaTest { protected IFhirResourceDaoValueSet myValueSetDao; @Autowired protected ITermValueSetDao myTermValueSetDao; - @Autowired - protected ITermValueSetVersionDao myTermValueSetVersionDao; +// @Autowired +// protected ITermValueSetVersionDao myTermValueSetVersionDao; @Autowired protected ITermValueSetConceptDao myTermValueSetConceptDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 0468e5176b8..e7ee8efdc5c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -137,6 +137,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 runInTransaction(() -> { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index 0ed780b76e4..48986d9b46c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -25,9 +25,6 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); myCodeSystemDao.create(cs, mySrd); - ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); - myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless(); - CodeSystem parentChildCs = new CodeSystem(); parentChildCs.setUrl(SYSTEM_PARENTCHILD); parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java index d6cce634524..8853d23a439 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java @@ -37,9 +37,6 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v2.xml"); myCodeSystemDao.create(cs, mySrd); - ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); - myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless(); - CodeSystem parentChildCs = new CodeSystem(); parentChildCs.setUrl(SYSTEM_PARENTCHILD); parentChildCs.setVersion("1"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 5780bc2c54f..6cb278e6bc8 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -10,14 +10,12 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -809,9 +807,11 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(0, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(0, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); } @@ -828,11 +828,14 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); - TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); +// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); + TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -854,7 +857,8 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(1); +// concept = termValueSetVersion.getConcepts().get(1); + concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -864,7 +868,8 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { // ... - concept = termValueSetVersion.getConcepts().get(22); +// concept = termValueSetVersion.getConcepts().get(22); + concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -879,7 +884,8 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(23); +// concept = termValueSetVersion.getConcepts().get(23); + concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index 5dc82c5fbc1..f1aa18de3ff 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -106,7 +106,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes ourLog.info(resp); assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); - assertThat(((Reference) respParam.getParameter().get(2).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9]+")); + assertThat(((Reference) respParam.getParameter().get(2).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9\\.\\-]+")); /* * Try uploading a second time diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index f94754d8d6f..8577c366884 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -1120,9 +1120,11 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(0, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(0, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); } @@ -1139,11 +1141,14 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); - TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); +// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); + TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1165,7 +1170,8 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(1); +// concept = termValueSetVersion.getConcepts().get(1); + concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1175,7 +1181,8 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { // ... - concept = termValueSetVersion.getConcepts().get(22); +// concept = termValueSetVersion.getConcepts().get(22); + concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1190,7 +1197,8 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(23); +// concept = termValueSetVersion.getConcepts().get(23); + concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1408,6 +1416,7 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails) { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index c3787c589b0..0ffe92d13bb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -63,6 +63,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setName("SYSTEM NAME"); + codeSystem.setVersion("SYSTEM VERSION"); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); @@ -116,6 +117,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { private void createCodeSystem2() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL_2); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -135,6 +137,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { runInTransaction(() -> { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(LOINC_URI); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -204,6 +207,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { public void testCreateDuplicateCodeSystemUri() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -226,12 +230,13 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { // Try to update to a different resource codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); try { myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); fail(); } catch (UnprocessableEntityException e) { - assertThat(e.getMessage(), containsString("Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/")); + assertThat(e.getMessage(), containsString("Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\" and CodeSystem.version \"SYSTEM VERSION\", already have one with resource ID: CodeSystem/")); } } @@ -1813,6 +1818,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { public void testStoreCodeSystemInvalidCyclicLoop() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); + codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); @@ -1834,7 +1840,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { child.addChild(parent, RelationshipTypeEnum.ISA); try { - myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getPersistentId(), "http://foo", "SYSTEM NAME", "SYSTEM VERSION", cs, table); + myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(table.getPersistentId(), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table); fail(); } catch (InvalidRequestException e) { assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); @@ -1948,6 +1954,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { CodeSystem cs = new CodeSystem(); cs.setUrl("http://codesystems-r-us"); + cs.setVersion("SYSTEM VERSION"); cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); IIdType csId = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless(); 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 9029075718f..629037325fe 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 @@ -12,7 +12,6 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.entity.TermValueSet; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CanonicalType; @@ -195,10 +194,12 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); Long termValueSetId = termValueSet.getId(); assertEquals(3, myTermValueSetConceptDesignationDao.countByTermValueSetId(termValueSetId).intValue()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); + assertEquals(3, termValueSet.getTotalConceptDesignations().intValue()); assertEquals(24, myTermValueSetConceptDao.countByTermValueSetId(termValueSetId).intValue()); - assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); +// assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); + assertEquals(24, termValueSet.getTotalConcepts().intValue()); new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override @@ -233,10 +234,12 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); Long termValueSetId = termValueSet.getId(); assertEquals(3, myTermValueSetConceptDesignationDao.countByTermValueSetId(termValueSetId).intValue()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); + assertEquals(3, termValueSet.getTotalConceptDesignations().intValue()); assertEquals(24, myTermValueSetConceptDao.countByTermValueSetId(termValueSetId).intValue()); - assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); +// assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); + assertEquals(24, termValueSet.getTotalConcepts().intValue()); new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java index e0480afcfa7..16d3dc3d930 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulatorTest.java @@ -2,10 +2,10 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDesignationDao; -import ca.uhn.fhir.jpa.dao.data.ITermValueSetVersionDao; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetDao; +import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,9 +25,9 @@ import static org.mockito.Mockito.when; public class ValueSetConceptAccumulatorTest { private ValueSetConceptAccumulator myAccumulator; - private TermValueSetVersion myValueSetVersion; + private TermValueSet myValueSet; @Mock - private ITermValueSetVersionDao myValueSetVersionDao; + private ITermValueSetDao myValueSetDao; @Mock private ITermValueSetConceptDesignationDao myValueSetDesignationDao; @Mock @@ -35,8 +35,8 @@ public class ValueSetConceptAccumulatorTest { @BeforeEach public void before() { - myValueSetVersion = new TermValueSetVersion(); - myAccumulator = new ValueSetConceptAccumulator(myValueSetVersion, myValueSetVersionDao, null, myValueSetConceptDao, myValueSetDesignationDao); + myValueSet = new TermValueSet(); + myAccumulator = new ValueSetConceptAccumulator(myValueSet, myValueSetDao, myValueSetConceptDao, myValueSetDesignationDao); } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index 5dd8ac22d4f..7cc370d572a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; @@ -1166,9 +1165,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(0, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(0, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1185,11 +1186,14 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(codeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); - TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); +// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); + TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1211,7 +1215,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(1); +// concept = termValueSetVersion.getConcepts().get(1); + concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1221,7 +1226,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSetVersion.getConcepts().get(22); +// concept = termValueSetVersion.getConcepts().get(22); + concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1236,7 +1242,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(23); +// concept = termValueSetVersion.getConcepts().get(23); + concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1270,9 +1277,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(0, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(0, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1289,11 +1298,14 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(codeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); - TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); +// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); + TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1315,7 +1327,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(1); +// concept = termValueSetVersion.getConcepts().get(1); + concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1325,7 +1338,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSetVersion.getConcepts().get(22); +// concept = termValueSetVersion.getConcepts().get(22); + concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1340,7 +1354,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(23); +// concept = termValueSetVersion.getConcepts().get(23); + concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1374,9 +1389,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(0, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(0, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1393,11 +1410,14 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(codeSystem.getConcept().size() - 2, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); - TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); +// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); + TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1419,7 +1439,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(1); +// concept = termValueSetVersion.getConcepts().get(1); + concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1429,7 +1450,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSetVersion.getConcepts().get(20); +// concept = termValueSetVersion.getConcepts().get(20); + concept = termValueSet.getConcepts().get(20); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1444,7 +1466,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(21); +// concept = termValueSetVersion.getConcepts().get(21); + concept = termValueSet.getConcepts().get(21); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); @@ -1478,9 +1501,11 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(0, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(0, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -1497,11 +1522,14 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); - TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); - assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); - assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); +// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); +// assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); +// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); + assertEquals(codeSystem.getConcept().size() - 2, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); - TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); +// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); + TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8450-9", concept.getCode()); @@ -1523,7 +1551,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(1); +// concept = termValueSetVersion.getConcepts().get(1); + concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("11378-7", concept.getCode()); @@ -1533,7 +1562,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... - concept = termValueSetVersion.getConcepts().get(20); +// concept = termValueSetVersion.getConcepts().get(20); + concept = termValueSet.getConcepts().get(20); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8491-3", concept.getCode()); @@ -1548,7 +1578,8 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); - concept = termValueSetVersion.getConcepts().get(21); +// concept = termValueSetVersion.getConcepts().get(21); + concept = termValueSet.getConcepts().get(21); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); assertEquals("8492-1", concept.getCode()); From fe684ee4c0c2066c086fde29586ed62add223010 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 15 Sep 2020 12:26:17 -0400 Subject: [PATCH 30/71] Further cleanup --- .../fhir/jpa/dao/data/ITermValueSetDao.java | 2 +- .../dao/expunge/ExpungeEverythingService.java | 5 --- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 32 ++++++++------- .../r4/FhirResourceDaoR4TerminologyTest.java | 2 +- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 16 ++------ .../ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java | 2 - .../ResourceProviderDstu3ValueSetTest.java | 2 - .../r4/ResourceProviderR4ValueSetTest.java | 8 ---- .../r5/ResourceProviderR5ValueSetTest.java | 11 ----- .../jpa/term/TerminologySvcDeltaR4Test.java | 2 +- .../jpa/term/TerminologySvcImplR4Test.java | 6 --- .../jpa/term/ValueSetExpansionR4Test.java | 40 ------------------- 12 files changed, 24 insertions(+), 104 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java index 33883b6073c..a7d72f71d6e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java @@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.dao.data; * #L% */ -import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import org.springframework.data.domain.Pageable; @@ -33,6 +32,7 @@ import java.util.List; import java.util.Optional; public interface ITermValueSetDao extends JpaRepository { + @Query("SELECT vs FROM TermValueSet vs WHERE vs.myResourcePid = :resource_pid") Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java index e80a2b67d9c..75e74b5ab75 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java @@ -117,10 +117,6 @@ public class ExpungeEverythingService { counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null")); return null; }); -// myTxTemplate.execute(t -> { -// counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermValueSet.class.getSimpleName() + " d SET d.myCurrentVersion = null")); -// return null; -// }); counter.addAndGet(expungeEverythingByType(NpmPackageVersionResourceEntity.class)); counter.addAndGet(expungeEverythingByType(NpmPackageVersionEntity.class)); counter.addAndGet(expungeEverythingByType(NpmPackageEntity.class)); @@ -139,7 +135,6 @@ public class ExpungeEverythingService { counter.addAndGet(expungeEverythingByType(SearchInclude.class)); counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class)); counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class)); -// counter.addAndGet(expungeEverythingByType(TermValueSetVersion.class)); counter.addAndGet(expungeEverythingByType(TermValueSet.class)); counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class)); counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index e38d53284b8..f41d9285826 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -343,7 +343,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { myValueSetConceptDao.deleteByTermValueSetId(existingTermValueSet.getId()); myValueSetDao.deleteById(existingTermValueSet.getId()); ourLog.info("Done deleting existing TermValueSet[{}] and its children.", existingTermValueSet.getId()); - } } @@ -395,7 +394,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { if (theValueSetToExpand.hasVersion()) { optionalTermValueSet = myValueSetDao.findTermValueSetByUrlAndVersion(theValueSetToExpand.getUrl(), theValueSetToExpand.getVersion()); } else { - optionalTermValueSet = myValueSetDao.findTermValueSetByUrlAndNullVersion(theValueSetToExpand.getUrl()); + List termValueSets = myValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 1), theValueSetToExpand.getUrl()); + optionalTermValueSet = Optional.of(termValueSets.get(0)); } } else { optionalTermValueSet = Optional.empty(); @@ -421,7 +421,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions); int offset = expansionOptions.getOffset(); int count = expansionOptions.getCount(); - populateExpansionComponent(expansionComponent, termValueSet, offset, count, termValueSet.getUrl()); + populateExpansionComponent(expansionComponent, termValueSet, offset, count); ValueSet valueSet = new ValueSet(); valueSet.setStatus(Enumerations.PublicationStatus.ACTIVE); @@ -430,7 +430,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return valueSet; } - private void populateExpansionComponent(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount, String theValueSetUrl) { + private void populateExpansionComponent(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount) { int total = theTermValueSet.getTotalConcepts().intValue(); theExpansionComponent.setTotal(total); theExpansionComponent.setOffset(theOffset); @@ -441,17 +441,17 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return; } - expandConcepts(theExpansionComponent, theTermValueSet, theOffset, theCount, theValueSetUrl); + expandConcepts(theExpansionComponent, theTermValueSet, theOffset, theCount); } - private void expandConcepts(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount, String theValueSetUrl) { + private void expandConcepts(ValueSet.ValueSetExpansionComponent theExpansionComponent, TermValueSet theTermValueSet, int theOffset, int theCount) { int conceptsExpanded = 0; int designationsExpanded = 0; int toIndex = theOffset + theCount; Collection conceptViews = myTermValueSetConceptViewDao.findByTermValueSetId(theOffset, toIndex, theTermValueSet.getId()); if (conceptViews.isEmpty()) { - logConceptsExpanded("No concepts to expand. ", theValueSetUrl, conceptsExpanded); + logConceptsExpanded("No concepts to expand. ", theTermValueSet, conceptsExpanded); return; } @@ -482,28 +482,28 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { designationComponent.setValue(conceptView.getDesignationVal()); if (++designationsExpanded % 250 == 0) { - logDesignationsExpanded("Expansion of designations in progress. ", theValueSetUrl, designationsExpanded); + logDesignationsExpanded("Expansion of designations in progress. ", theTermValueSet, designationsExpanded); } } if (++conceptsExpanded % 250 == 0) { - logConceptsExpanded("Expansion of concepts in progress. ", theValueSetUrl, conceptsExpanded); + logConceptsExpanded("Expansion of concepts in progress. ", theTermValueSet, conceptsExpanded); } } - logDesignationsExpanded("Finished expanding designations. ", theValueSetUrl, designationsExpanded); - logConceptsExpanded("Finished expanding concepts. ", theValueSetUrl, conceptsExpanded); + logDesignationsExpanded("Finished expanding designations. ", theTermValueSet, designationsExpanded); + logConceptsExpanded("Finished expanding concepts. ", theTermValueSet, conceptsExpanded); } - private void logConceptsExpanded(String theLogDescriptionPrefix, String theValueSetUrl, int theConceptsExpanded) { + private void logConceptsExpanded(String theLogDescriptionPrefix, TermValueSet theTermValueSet, int theConceptsExpanded) { if (theConceptsExpanded > 0) { - ourLog.debug("{}Have expanded {} concepts in ValueSet[{}]", theLogDescriptionPrefix, theConceptsExpanded, theValueSetUrl); + ourLog.debug("{}Have expanded {} concepts in ValueSet[{}]", theLogDescriptionPrefix, theConceptsExpanded, theTermValueSet.getUrl()); } } - private void logDesignationsExpanded(String theLogDescriptionPrefix, String theValueSetUrl, int theDesignationsExpanded) { + private void logDesignationsExpanded(String theLogDescriptionPrefix, TermValueSet theTermValueSet, int theDesignationsExpanded) { if (theDesignationsExpanded > 0) { - ourLog.debug("{}Have expanded {} designations in ValueSet[{}]", theLogDescriptionPrefix, theDesignationsExpanded, theValueSetUrl); + ourLog.debug("{}Have expanded {} designations in ValueSet[{}]", theLogDescriptionPrefix, theDesignationsExpanded, theTermValueSet.getUrl()); } } @@ -1807,7 +1807,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { optionalExistingTermValueSetByUrl = myValueSetDao.findTermValueSetByUrlAndNullVersion(url); } if (!optionalExistingTermValueSetByUrl.isPresent()) { + myValueSetDao.save(termValueSet); + } else { TermValueSet existingTermValueSet = optionalExistingTermValueSetByUrl.get(); String msg; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index 90d30645a5b..29db57d857f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -833,7 +833,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); - IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null,null, mySrd); + IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); assertEquals(true, outcome.isFound()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 0ef54220a16..57f45050655 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -208,9 +208,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { myStructureDefinitionDao.create(loadResourceFromClasspath(StructureDefinition.class, "/r4/bl/bb-sd.json")); runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit","20190731").orElseThrow(() -> new IllegalArgumentException()); -// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); }); @@ -239,18 +237,14 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { // Before, the VS wasn't pre-expanded. Try again with it pre-expanded runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit","20190731").orElseThrow(() -> new IllegalArgumentException()); -// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); }); myTermReadSvc.preExpandDeferredValueSetsToTerminologyTables(); runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit", "20190731").orElseThrow(() -> new IllegalArgumentException()); -// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, vs.getExpansionStatus()); }); @@ -284,9 +278,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { myStructureDefinitionDao.create(loadResourceFromClasspath(StructureDefinition.class, "/r4/bl/bb-sd.json")); runInTransaction(() -> { - TermValueSet vs = myTermValueSetDao.findTermValueSetByUrlAndVersion("https://bb/ValueSet/BBDemographicAgeUnit", "20190731").orElseThrow(() -> new IllegalArgumentException()); -// TermValueSetVersion vsv = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(vs.getId()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vsv.getExpansionStatus()); + TermValueSet vs = myTermValueSetDao.findByUrl("https://bb/ValueSet/BBDemographicAgeUnit").orElseThrow(() -> new IllegalArgumentException()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, vs.getExpansionStatus()); }); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index 9cd0bb49e5a..85784c49eb6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -383,8 +383,6 @@ public abstract class BaseJpaR5Test extends BaseJpaTest { protected IFhirResourceDaoValueSet myValueSetDao; @Autowired protected ITermValueSetDao myTermValueSetDao; -// @Autowired -// protected ITermValueSetVersionDao myTermValueSetVersionDao; @Autowired protected ITermValueSetConceptDao myTermValueSetConceptDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index e7ee8efdc5c..e293bb59bb7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -13,7 +13,6 @@ import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -37,7 +36,6 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 6cb278e6bc8..a1dff9ee8ed 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -807,9 +807,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(0, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(0, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); @@ -828,13 +825,9 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); -// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -857,7 +850,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(1); concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index 8577c366884..c26c8bde431 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -10,7 +10,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; -import ca.uhn.fhir.jpa.entity.TermValueSetVersion; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; @@ -1120,9 +1119,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(0, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(0, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); @@ -1141,13 +1137,9 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals(theValueSetName, termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(theCodeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); -// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1170,7 +1162,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(1); concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1181,7 +1172,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { // ... -// concept = termValueSetVersion.getConcepts().get(22); concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1197,7 +1187,6 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(23); concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index db601c7a330..265e2064761 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -184,7 +184,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { assertEquals(2, outcome.getUpdatedConceptCount()); runInTransaction(() -> { - TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA", null).orElseThrow(() -> new IllegalStateException()); + TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA").orElseThrow(() -> new IllegalStateException()); assertEquals(2, concept.getParents().size()); assertThat(concept.getParentPidsAsString(), matchesPattern("^[0-9]+ [0-9]+$")); }); 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 629037325fe..79f2c37836c 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 @@ -194,11 +194,8 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); Long termValueSetId = termValueSet.getId(); assertEquals(3, myTermValueSetConceptDesignationDao.countByTermValueSetId(termValueSetId).intValue()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); assertEquals(3, termValueSet.getTotalConceptDesignations().intValue()); assertEquals(24, myTermValueSetConceptDao.countByTermValueSetId(termValueSetId).intValue()); -// assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); assertEquals(24, termValueSet.getTotalConcepts().intValue()); new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @@ -234,11 +231,8 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); Long termValueSetId = termValueSet.getId(); assertEquals(3, myTermValueSetConceptDesignationDao.countByTermValueSetId(termValueSetId).intValue()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(3, termValueSetVersion.getTotalConceptDesignations().intValue()); assertEquals(3, termValueSet.getTotalConceptDesignations().intValue()); assertEquals(24, myTermValueSetConceptDao.countByTermValueSetId(termValueSetId).intValue()); -// assertEquals(24, termValueSetVersion.getTotalConcepts().intValue()); assertEquals(24, termValueSet.getTotalConcepts().intValue()); new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index 7cc370d572a..40850b23eaf 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -1165,9 +1165,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(0, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(0, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); @@ -1186,13 +1183,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(codeSystem.getConcept().size(), termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); -// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1215,7 +1208,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(1); concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1226,7 +1218,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... -// concept = termValueSetVersion.getConcepts().get(22); concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1242,7 +1233,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(23); concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1277,9 +1267,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(0, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(0, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); @@ -1298,13 +1285,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(codeSystem.getConcept().size(), termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(codeSystem.getConcept().size(), termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); -// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1327,7 +1310,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(1); concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1338,7 +1320,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... -// concept = termValueSetVersion.getConcepts().get(22); concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1354,7 +1335,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(23); concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1389,9 +1369,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(0, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(0, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); @@ -1410,13 +1387,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(codeSystem.getConcept().size() - 2, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); -// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1439,7 +1412,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(1); concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1450,7 +1422,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... -// concept = termValueSetVersion.getConcepts().get(20); concept = termValueSet.getConcepts().get(20); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1466,7 +1437,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(21); concept = termValueSet.getConcepts().get(21); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1501,9 +1471,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(0, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(0, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); }); @@ -1522,13 +1489,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { ourLog.info("ValueSet:\n" + termValueSet.toString()); assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); assertEquals("Terminology Services Connectation #1 Extensional case #2", termValueSet.getName()); -// TermValueSetVersion termValueSetVersion = myTermValueSetVersionDao.findByValueSetPidAndNullVersion(termValueSet.getId()); -// assertEquals(codeSystem.getConcept().size() - 2, termValueSetVersion.getConcepts().size()); -// assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSetVersion.getExpansionStatus()); assertEquals(codeSystem.getConcept().size() - 2, termValueSet.getConcepts().size()); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); -// TermValueSetConcept concept = termValueSetVersion.getConcepts().get(0); TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1551,7 +1514,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(1); concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1562,7 +1524,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { // ... -// concept = termValueSetVersion.getConcepts().get(20); concept = termValueSet.getConcepts().get(20); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -1578,7 +1539,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(21); concept = termValueSet.getConcepts().get(21); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); From b4cd09a5b692a01ca239311f42ee45901bfd00cb Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 15 Sep 2020 12:30:26 -0400 Subject: [PATCH 31/71] Further cleanup --- .../fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index a1dff9ee8ed..b3cca872d96 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -860,7 +860,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { // ... -// concept = termValueSetVersion.getConcepts().get(22); concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); @@ -876,7 +875,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { assertEquals("Synonym", designation.getUseDisplay()); assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); -// concept = termValueSetVersion.getConcepts().get(23); concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); assertEquals("http://acme.org", concept.getSystem()); From b2b63cbb02b98d1c30618af78ab9f447326262da Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 15 Sep 2020 22:05:46 -0400 Subject: [PATCH 32/71] Fixed JUnit tests. --- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 6 +- .../r4/FhirResourceDaoR4CodeSystemTest.java | 4 +- ...urceProviderR4CodeSystemVersionedTest.java | 16 ++- .../resources/extensional-case-3-cs-v1.xml | 101 ------------------ .../resources/extensional-case-3-cs-v2.xml | 101 ------------------ 5 files changed, 19 insertions(+), 209 deletions(-) delete mode 100644 hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml delete mode 100644 hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index f41d9285826..75da809c5cd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -395,7 +395,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { optionalTermValueSet = myValueSetDao.findTermValueSetByUrlAndVersion(theValueSetToExpand.getUrl(), theValueSetToExpand.getVersion()); } else { List termValueSets = myValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 1), theValueSetToExpand.getUrl()); - optionalTermValueSet = Optional.of(termValueSets.get(0)); + if (termValueSets.size() > 0) { + optionalTermValueSet = Optional.of(termValueSets.get(0)); + } else { + optionalTermValueSet = Optional.empty(); + } } } else { optionalTermValueSet = Optional.empty(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java index 9d917a7af8a..fa4c2aae93b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java @@ -61,7 +61,7 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { // Now the background scheduler will do its thing myTerminologyDeferredStorageSvc.saveDeferred(); runInTransaction(() -> { - assertEquals(0, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemDao.count()); assertEquals(0, myTermCodeSystemVersionDao.count()); assertEquals(0, myTermConceptDao.count()); }); @@ -155,7 +155,7 @@ public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { // The remaining versions and Code System entities should be gone now. runInTransaction(() -> { - assertEquals(0, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemDao.count()); assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); assertEquals(0, myTermCodeSystemVersionDao.count()); List resourceList = myResourceTableDao.findAll(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java index 8853d23a439..5e621e84ad7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java @@ -31,10 +31,18 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi @BeforeEach @Transactional public void before02() throws IOException { - CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v1.xml"); + CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + cs.setVersion("1"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : cs.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v1"); + } myCodeSystemDao.create(cs, mySrd); - cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-v2.xml"); + cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + cs.setVersion("2"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : cs.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v2"); + } myCodeSystemDao.create(cs, mySrd); CodeSystem parentChildCs = new CodeSystem(); @@ -310,7 +318,7 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi assertEquals("version", respParam.getParameter().get(1).getName()); assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(2).getName()); - assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals(("Systolic blood pressure--expiration v1"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(3).getName()); assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); @@ -392,7 +400,7 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi assertEquals("version", respParam.getParameter().get(1).getName()); assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(2).getName()); - assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals(("Systolic blood pressure--expiration v1"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(3).getName()); assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml deleted file mode 100644 index dc023fe3811..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v1.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml b/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml deleted file mode 100644 index a9868b624da..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/resources/extensional-case-3-cs-v2.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 64f2bae2e7ed279df7bcfe7eb6ac2909a27e613c Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Fri, 18 Sep 2020 22:30:59 -0400 Subject: [PATCH 33/71] Impl CodeSystem $validate-code for R4 with basic test cases --- .../api/dao/IFhirResourceDaoCodeSystem.java | 2 + .../jpa/dao/FhirResourceDaoValueSetDstu2.java | 11 + .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 11 + .../dao/r4/FhirResourceDaoCodeSystemR4.java | 11 + .../dao/r5/FhirResourceDaoCodeSystemR5.java | 9 + .../BaseJpaResourceProviderCodeSystemR4.java | 59 ++- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 294 ++++++++---- .../uhn/fhir/jpa/term/api/ITermReadSvc.java | 5 + ...rceProviderR4CodeSystemValidationTest.java | 433 ++++++++++++++++++ .../CommonCodeSystemsTerminologyService.java | 26 ++ 10 files changed, 762 insertions(+), 99 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index 2c00c792b49..a963f82c718 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -47,6 +47,8 @@ public interface IFhirResourceDaoCodeSystem ext SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails); + IValidationSupport.CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails); + class SubsumesResult { private final ConceptSubsumptionOutcome myOutcome; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index 12cbd8fa2f5..e946f626b5b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; @@ -56,6 +57,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static ca.uhn.fhir.jpa.util.LogicUtil.multiXor; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -312,4 +314,13 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { + + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index c42f4125ce1..32d770229ae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; @@ -51,6 +52,8 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; @@ -169,4 +172,12 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 996eba93af4..577f16478ba 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; @@ -50,6 +51,8 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -164,5 +167,13 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index c9bd9125be4..e3fee2f13d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.r5; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.dao.index.IdHelperService; @@ -51,6 +52,8 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoCodeSystem { @@ -166,5 +169,11 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCodeSystemUrl, + IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, + Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 9b78c83991b..94e94a10b05 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -1,5 +1,19 @@ package ca.uhn.fhir.jpa.provider.r4; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; + /* * #%L * HAPI FHIR JPA Server @@ -23,20 +37,11 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.UriType; - -import javax.servlet.http.HttpServletRequest; -import java.util.List; public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 { @@ -98,4 +103,36 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); + + IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); + } finally { + endRequest(theServletRequest); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index a70fba614e8..a1709f9a366 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1,5 +1,99 @@ package ca.uhn.fhir.jpa.term; +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNoneBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.PersistenceContextType; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import javax.persistence.criteria.Fetch; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.lucene.index.Term; +import org.apache.lucene.queries.TermsQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.RegexpQuery; +import org.apache.lucene.search.TermQuery; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; +import org.hibernate.search.jpa.FullTextEntityManager; +import org.hibernate.search.jpa.FullTextQuery; +import org.hibernate.search.query.dsl.BooleanJunction; +import org.hibernate.search.query.dsl.QueryBuilder; +import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; +import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; +import org.quartz.JobExecutionContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; +import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; +import org.springframework.transaction.support.TransactionTemplate; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Stopwatch; + /* * #%L * HAPI FHIR JPA Server @@ -85,94 +179,6 @@ import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.VersionIndependentConcept; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Stopwatch; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.apache.commons.lang3.time.DateUtils; -import org.apache.lucene.index.Term; -import org.apache.lucene.queries.TermsQuery; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.RegexpQuery; -import org.apache.lucene.search.TermQuery; -import org.hibernate.ScrollMode; -import org.hibernate.ScrollableResults; -import org.hibernate.search.jpa.FullTextEntityManager; -import org.hibernate.search.jpa.FullTextQuery; -import org.hibernate.search.query.dsl.BooleanJunction; -import org.hibernate.search.query.dsl.QueryBuilder; -import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; -import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.CanonicalType; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ConceptMap; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.IntegerType; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; -import org.quartz.JobExecutionContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; -import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; -import org.springframework.transaction.support.TransactionTemplate; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.validation.constraints.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNoneBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public static final int DEFAULT_FETCH_SIZE = 250; @@ -2555,5 +2561,117 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return ourLastResultsFromTranslationWithReverseCache; } + @Override + public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { + CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); + boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0; + + Coding coding = toCanonicalCoding(theCoding); + boolean haveCoding = coding != null && coding.isEmpty() == false; + + boolean haveCode = theCode != null && theCode.isEmpty() == false; + + if (!haveCodeableConcept && !haveCoding && !haveCode) { + throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate."); + } + if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) { + throw new InvalidRequestException("$validate-code can only validate (code) OR (coding) OR (codeableConcept)"); + } + + boolean haveIdentifierParam = isNotBlank(theCodeSystemUrl); + String codeSystemUrl; + if (theCodeSystemId != null) { + IBaseResource codeSystem = myDaoRegistry.getResourceDao("CodeSystem").read(theCodeSystemId); + codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem); + } else if (haveIdentifierParam) { + codeSystemUrl = theCodeSystemUrl; + } else { + throw new InvalidRequestException("Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate."); + } + + + String code = theCode; + String version = theVersion; + String display = theDisplay; + + if (haveCodeableConcept) { + for (int i = 0; i < codeableConcept.getCoding().size(); i++) { + Coding nextCoding = codeableConcept.getCoding().get(i); + if (nextCoding.hasSystem()) { + if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) { + throw new InvalidRequestException("Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate."); + } + codeSystemUrl = nextCoding.getSystem(); + } + code = nextCoding.getCode(); + display = nextCoding.getDisplay(); + CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, version, code, display); + if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) { + return nextValidation; + } + } + } else if (haveCoding) { + if (coding.hasSystem()) { + if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) { + throw new InvalidRequestException("Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate."); + } + codeSystemUrl = coding.getSystem(); + } + code = coding.getCode(); + display = coding.getDisplay(); + } + + return codeSystemValidateCode(codeSystemUrl, version, code, display); + } + + + @SuppressWarnings("unchecked") + private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theCodeSystemVersion, String theCode, String theDisplay) { + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TermConcept.class); + Root root = query.from(TermConcept.class); + + Fetch systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER); + Join systemVersionJoin = (Join)systemVersionFetch; + Fetch systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER); + Join systemJoin = (Join)systemFetch; + + ArrayList predicates = new ArrayList<>(); + + if (isNotBlank(theCode)) { + predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode)); + } + + if (isNotBlank(theDisplay)) { + predicates.add(criteriaBuilder.equal(root.get("myDisplay"), theDisplay)); + } + + if (isNoneBlank(theCodeSystemUrl)) { + predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl)); + } + + if (isNoneBlank(theCodeSystemVersion)) { + predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion)); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + final TypedQuery typedQuery = myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + List resultsList = hibernateQuery.getResultList(); + + if (!resultsList.isEmpty()) { + TermConcept concept = resultsList.get(0); + return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay()); + } + + if (isBlank(theDisplay)) + return createFailureCodeValidationResult(theCodeSystemUrl, theCode); + else + return createFailureCodeValidationResult(theCodeSystemUrl, theCode, " - Concept Display : " + theDisplay); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java index 124cf2d6450..765ff1a43dc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java @@ -126,4 +126,9 @@ public interface ITermReadSvc extends IValidationSupport { */ boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet); + /** + * Version independent + */ + CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theValueSetUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept); + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java new file mode 100644 index 00000000000..5616f517f36 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -0,0 +1,433 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; + +import javax.annotation.Nonnull; + +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; + +public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProviderR4Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemValidationTest.class); + + private IIdType myCsId; + private static final String CS_ACMS_URL = "http://acme.org"; + + @BeforeEach + @Transactional + public void before02() throws IOException { + loadAndPersistCodeSystem(); + } + + private void loadAndPersistCodeSystem() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + codeSystem.setId("CodeSystem/cs"); + persistCodeSystem(codeSystem); + } + + private void persistCodeSystem(CodeSystem theCodeSystem) { + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myCsId = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myCsId, null).getId(); + } + + @Test + public void testValidateCodeFoundByCode() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCode() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5-a")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeMatchDisplay() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeNotMatchDisplay() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + inParams.addParameter().setName("display").setValue(new CodeType("Old Systolic blood pressure.inspiration - expiration")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5 - Concept Display : Old Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeWithoutUrl() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodeWithId() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + + Parameters respParam = myClient.operation().onInstance(myCsId).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeWithoutCodeOrCodingOrCodeableConcept() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeWithCodeAndCoding() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-1"))); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $validate-code can only validate (code) OR (coding) OR (codeableConcept)",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodingWithUrlNotMatch() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem("http://url2"))); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCoding() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5"))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodingWithSystem() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem(CS_ACMS_URL))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodingUrlNotMatch() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem("http://url2"))); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodingWithDisplay() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setDisplay("Systolic blood pressure.inspiration - expiration"))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCoding() throws Exception { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5-a"))); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConcept() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithSystem() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithDisplay() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCodeableConcept() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5-a"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeableConceptUrlNotMatch() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem("http://url2").setDisplay("Systolic blood pressure.inspiration - expiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + try { + myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); + } + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedEntries() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedFirstEntry() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7-a").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedSecondEntry() throws Exception { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5-a").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); + inParams.addParameter().setName("codeableConcept").setValue(cc); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure--inspiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index 51dfe5dd86b..cfea4d2ac89 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -332,6 +332,32 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { return url; } + public static String getCodeSystemUrl(@Nonnull IBaseResource theCodeSystem) { + String url; + switch (theCodeSystem.getStructureFhirVersionEnum()) { + case DSTU2_HL7ORG: { + url = ((CodeSystem) theCodeSystem).getUrl(); + break; + } + case DSTU3: { + url = ((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem).getUrl(); + break; + } + case R4: { + url = ((org.hl7.fhir.r4.model.CodeSystem) theCodeSystem).getUrl(); + break; + } + case R5: { + url = ((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem).getUrl(); + break; + } + case DSTU2: + case DSTU2_1: + default: + throw new IllegalArgumentException("Can not handle version: " + theCodeSystem.getStructureFhirVersionEnum()); + } + return url; + } private static HashMap buildUspsCodes() { HashMap uspsCodes = new HashMap<>(); uspsCodes.put("AK", "Alaska"); From 05de0c503124bd20bfe7081838d533ef68fb8dc5 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 12:36:14 -0400 Subject: [PATCH 34/71] Added more test cases for R4 --- .../BaseJpaResourceProviderCodeSystemR4.java | 2 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 7 +- ...rceProviderR4CodeSystemValidationTest.java | 120 ++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 94e94a10b05..f0f13171899 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -118,7 +118,7 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 Date: Sat, 19 Sep 2020 12:52:18 -0400 Subject: [PATCH 35/71] Changed fetch size to 1 for CodeSystem $validate-code --- .../java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 4e8ba4def3c..6ebc0315f97 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -31,12 +31,11 @@ import javax.persistence.PersistenceContextType; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Fetch; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; -import javax.persistence.criteria.Fetch; -import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; @@ -182,6 +181,7 @@ import ca.uhn.fhir.util.VersionIndependentConcept; public abstract class BaseTermReadSvcImpl implements ITermReadSvc { public static final int DEFAULT_FETCH_SIZE = 250; + private static final int SINGLE_FETCH_SIZE = 1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTermReadSvcImpl.class); private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions(); private static final TermCodeSystemVersion NO_CURRENT_VERSION = new TermCodeSystemVersion().setId(-1L); @@ -2663,16 +2663,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { final TypedQuery typedQuery = myEntityManager.createQuery(query.select(root)); org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery; - hibernateQuery.setFetchSize(myFetchSize); + hibernateQuery.setFetchSize(SINGLE_FETCH_SIZE); List resultsList = hibernateQuery.getResultList(); if (!resultsList.isEmpty()) { TermConcept concept = resultsList.get(0); return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay()); } - - for (TermConcept c :resultsList) - System.out.println(c); if (isBlank(theDisplay)) return createFailureCodeValidationResult(theCodeSystemUrl, theCode); From 01d88a665d4db8ff3d8a4abe9b65edeb5f373aae Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 14:13:07 -0400 Subject: [PATCH 36/71] Added CodeSystem/$validate-code for R5 --- .../BaseJpaResourceProviderCodeSystemR4.java | 2 +- .../BaseJpaResourceProviderCodeSystemR5.java | 36 ++++++++++++ ...rceProviderR4CodeSystemValidationTest.java | 6 +- .../r5/ResourceProviderR5CodeSystemTest.java | 58 +++++++++++++++++++ 4 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index f0f13171899..37a8445a412 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -117,8 +117,8 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); + + IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); + } finally { + endRequest(theServletRequest); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java index 6278a6db55a..e92450d6dd2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -94,7 +94,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); - inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + inParams.addParameter().setName("display").setValue(new StringType("Systolic blood pressure.inspiration - expiration")); Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); @@ -112,7 +112,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); inParams.addParameter().setName("code").setValue(new CodeType("8452-5")); - inParams.addParameter().setName("display").setValue(new CodeType("Old Systolic blood pressure.inspiration - expiration")); + inParams.addParameter().setName("display").setValue(new StringType("Old Systolic blood pressure.inspiration - expiration")); Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); @@ -156,7 +156,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv Parameters inParams = new Parameters(); inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL)); - inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration")); + inParams.addParameter().setName("display").setValue(new StringType("Systolic blood pressure.inspiration - expiration")); try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java new file mode 100644 index 00000000000..2606db1b248 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemTest.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5CodeSystemTest.class); + + @Test + public void testValidateCodeWithUrlAndVersion_v1() { + + String url = "http://url"; + createCodeSystem(url, "v1", "1", "Code v1 display"); + createCodeSystem(url, "v2", "1", "Code v2 display"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(url)); + inParams.addParameter().setName("version").setValue(new StringType("v1")); + inParams.addParameter().setName("code").setValue(new CodeType("1")); + inParams.addParameter().setName("display").setValue(new StringType("Code v1 display")); + + Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + + ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParam)); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Code v1 display", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + private void createCodeSystem(String url, String version, String code, String display) { + + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(url).setVersion(version); + + ConceptDefinitionComponent concept1 = codeSystem.addConcept(); + concept1.setCode("1000").setDisplay("Code Dispaly 1000"); + + ConceptDefinitionComponent concept = codeSystem.addConcept(); + concept.setCode(code).setDisplay(display); + + ConceptDefinitionComponent concept2 = codeSystem.addConcept(); + concept2.setCode("2000").setDisplay("Code Dispaly 2000"); + + ourLog.info("CodeSystem: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); + + myCodeSystemDao.create(codeSystem, mySrd); + } +} From a3a032584bc8c083591f48a4176e94e56aa61860 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 15:18:28 -0400 Subject: [PATCH 37/71] Made Transactional --- .../src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 6ebc0315f97..5c4615f5e56 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -2562,6 +2562,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override + @Transactional public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); From 8f692a4776001ffce2b2ac2779bf106fd4f90775 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Sat, 19 Sep 2020 22:54:39 -0400 Subject: [PATCH 38/71] CodeSystem/$validate-code not supported for DSTU2, DSTU3 --- .../uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java | 9 +++------ .../jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index e946f626b5b..c15b8784b25 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java @@ -314,13 +314,10 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCodeSystemUrl, - IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, - CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { - - return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, + IPrimitiveType theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 32d770229ae..29200610dae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -173,11 +173,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeSystemUrl, - IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, - Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { - - return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); + public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, + IPrimitiveType theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { + throw new UnsupportedOperationException(); } } From 302bbca8fd4ef3a54aaa1ed11f50959c3bd1c762 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 20 Sep 2020 22:01:21 -0400 Subject: [PATCH 39/71] Changes to support multi-versioning in ValueSet $expand operation. --- .../fhir/util/VersionIndependentConcept.java | 9 + .../ca/uhn/fhir/i18n/hapi-messages.properties | 1 - .../jpa/api/dao/IFhirResourceDaoValueSet.java | 4 + .../jpa/dao/FhirResourceDaoValueSetDstu2.java | 10 + ...JpaPersistedResourceValidationSupport.java | 27 +- .../dao/data/ITermCodeSystemVersionDao.java | 2 +- .../fhir/jpa/dao/data/ITermValueSetDao.java | 1 + .../dstu3/FhirResourceDaoValueSetDstu3.java | 69 +- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 82 +- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 89 +- .../BaseJpaResourceProviderValueSetDstu3.java | 14 +- .../r4/BaseJpaResourceProviderValueSetR4.java | 14 +- .../r5/BaseJpaResourceProviderValueSetR5.java | 14 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 120 +- .../jpa/term/IValueSetConceptAccumulator.java | 4 + .../term/TermCodeSystemStorageSvcImpl.java | 29 +- .../jpa/term/TermDeferredStorageSvcImpl.java | 31 +- .../jpa/term/ValueSetConceptAccumulator.java | 12 +- ...ansionComponentWithConceptAccumulator.java | 20 +- ...ourceDaoDstu3ValueSetMultiVersionTest.java | 222 +++ .../r4/FhirResourceDaoR4TerminologyTest.java | 20 + ...ResourceDaoR4ValueSetMultiVersionTest.java | 222 +++ ...ResourceDaoR5ValueSetMultiVersionTest.java | 222 +++ .../r4/ResourceProviderR4CodeSystemTest.java | 32 +- ...urceProviderR4CodeSystemVersionedTest.java | 54 +- .../r4/ResourceProviderR4ValueSetTest.java | 9 +- ...sourceProviderR4ValueSetVersionedTest.java | 1415 +++++++++++++++++ .../term/TermDeferredStorageSvcImplTest.java | 5 - .../jpa/term/TerminologySvcImplR4Test.java | 52 + .../ValueSetExpansionR4ElasticsearchIT.java | 2 +- .../jpa/term/ValueSetExpansionR4Test.java | 2 +- .../tasks/HapiFhirJpaMigrationTasks.java | 5 + 32 files changed, 2583 insertions(+), 231 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetMultiVersionTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetMultiVersionTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java index abd9643c8e7..ec5439fbffc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java @@ -28,6 +28,7 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; public class VersionIndependentConcept implements Comparable { private final String mySystem; + private final String mySystemVersion; private final String myCode; private final String myDisplay; private int myHashCode; @@ -40,7 +41,12 @@ public class VersionIndependentConcept implements Comparable exten T expandByIdentifier(String theUri, String theFilter); + T expandByIdentifier(String theUri, String theValueSetVersion, String theFilter); + T expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount); + T expandByIdentifier(String theUri, String theValueSetVersion, String theFilter, int theOffset, int theCount); + void purgeCaches(); IValidationSupport.CodeValidationResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index 12cbd8fa2f5..6069ebcd335 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 @@ -174,11 +174,21 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) { if (theSystem != null && theSystem.startsWith("http://hl7.org/fhir/")) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java index a37904f02d5..1480e8ff0bc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java @@ -25,8 +25,11 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.SortOrderEnum; +import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -121,18 +124,25 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport SearchParameterMap params = new SearchParameterMap(); params.setLoadSynchronousUpTo(1); params.add(IAnyResource.SP_RES_ID, new StringParam(theUri)); - search = myDaoRegistry.getResourceDao("ValueSet").search(params); + search = myDaoRegistry.getResourceDao(resourceName).search(params); if (search.size() == 0) { params = new SearchParameterMap(); params.setLoadSynchronousUpTo(1); params.add(ValueSet.SP_URL, new UriParam(theUri)); - search = myDaoRegistry.getResourceDao("ValueSet").search(params); + search = myDaoRegistry.getResourceDao(resourceName).search(params); } } else { + int versionSeparator = theUri.lastIndexOf('|'); SearchParameterMap params = new SearchParameterMap(); params.setLoadSynchronousUpTo(1); - params.add(ValueSet.SP_URL, new UriParam(theUri)); - search = myDaoRegistry.getResourceDao("ValueSet").search(params); + if (versionSeparator != -1) { + params.add(ValueSet.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1))); + params.add(ValueSet.SP_URL, new UriParam(theUri.substring(0,versionSeparator))); + } else { + params.add(ValueSet.SP_URL, new UriParam(theUri)); + } + params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC)); + search = myDaoRegistry.getResourceDao(resourceName).search(params); } } else if ("StructureDefinition".equals(resourceName)) { // Don't allow the core FHIR definitions to be overwritten @@ -156,9 +166,16 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport } search = myDaoRegistry.getResourceDao("Questionnaire").search(params); } else if ("CodeSystem".equals(resourceName)) { + int versionSeparator = theUri.lastIndexOf('|'); SearchParameterMap params = new SearchParameterMap(); params.setLoadSynchronousUpTo(1); - params.add(CodeSystem.SP_URL, new UriParam(theUri)); + if (versionSeparator != -1) { + params.add(CodeSystem.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1))); + params.add(CodeSystem.SP_URL, new UriParam(theUri.substring(0,versionSeparator))); + } else { + params.add(CodeSystem.SP_URL, new UriParam(theUri)); + } + params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC)); search = myDaoRegistry.getResourceDao(resourceName).search(params); } else if ("ImplementationGuide".equals(resourceName)) { SearchParameterMap params = new SearchParameterMap(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java index 8b56e223d30..fc55636836f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java @@ -49,7 +49,7 @@ public interface ITermCodeSystemVersionDao extends JpaRepository findByCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemHavingThisVersionAsCurrentVersionIfAny.myResource.myId = :resource_id") TermCodeSystemVersion findCurrentVersionForCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java index a7d72f71d6e..bc698dc8b3b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java @@ -36,6 +36,7 @@ public interface ITermValueSetDao extends JpaRepository { @Query("SELECT vs FROM TermValueSet vs WHERE vs.myResourcePid = :resource_pid") Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); + @Deprecated @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url") Optional findByUrl(@Param("url") String theUrl); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 9def15576aa..0ebb0a93764 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -21,22 +21,18 @@ package ca.uhn.fhir.jpa.dao.dstu3; */ import ca.uhn.fhir.context.support.ConceptValidationOptions; -import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ElementUtil; -import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IntegerType; @@ -49,9 +45,6 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; import java.util.List; @@ -63,16 +56,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet; public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { - private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoValueSetDstu3.class); - - @Autowired - private DefaultProfileValidationSupport myDefaultProfileValidationSupport; private IValidationSupport myValidationSupport; - @Autowired - private IFhirResourceDaoCodeSystem myCodeSystemDao; - @Override public void start() { super.start(); @@ -127,21 +113,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { - @Autowired - private DefaultProfileValidationSupport myDefaultProfileValidationSupport; - private IValidationSupport myValidationSupport; - @Autowired - private IFhirResourceDaoCodeSystem myCodeSystemDao; @Override public void start() { @@ -107,21 +95,7 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao throw new InvalidRequestException("URI must not be blank or missing"); } - ValueSet source = new ValueSet(); - source.setUrl(theUri); - - source.getCompose().addInclude().addValueSet(theUri); - - if (isNotBlank(theFilter)) { - ConceptSetComponent include = source.getCompose().addInclude(); - ConceptSetFilterComponent filter = include.addFilter(); - filter.setProperty("display"); - filter.setOp(FilterOperator.EQUAL); - filter.setValue(theFilter); - } - - ValueSet retVal = doExpand(source); - return retVal; + return doExpand(createSourceValueSet(theUri, null, theFilter)); // if (defaultValueSet != null) { // source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet)); @@ -136,27 +110,54 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao // return expand(defaultValueSet, theFilter); } + @Override + public ValueSet expandByIdentifier(String theUri, String theValueSetVersion, String theFilter) { + if (isBlank(theUri)) { + throw new InvalidRequestException("URI must not be blank or missing"); + } + + return doExpand(createSourceValueSet(theUri, theValueSetVersion, theFilter)); + } + + private ValueSet createSourceValueSet(String theUri, String theValueSetVersion, String theFilter) { + ValueSet source = new ValueSet(); + source.setUrl(theUri); + if (theValueSetVersion != null) { + source.setVersion(theValueSetVersion); + } + + if (theValueSetVersion != null) { + source.getCompose().addInclude().addValueSet(theUri + "|" +theValueSetVersion); + } else { + source.getCompose().addInclude().addValueSet(theUri); + } + + if (isNotBlank(theFilter)) { + ValueSet.ConceptSetComponent include = source.getCompose().addInclude(); + ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + filter.setProperty("display"); + filter.setOp(ValueSet.FilterOperator.EQUAL); + filter.setValue(theFilter); + } + return source; + } + @Override public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { if (isBlank(theUri)) { throw new InvalidRequestException("URI must not be blank or missing"); } - ValueSet source = new ValueSet(); - source.setUrl(theUri); + return doExpand(createSourceValueSet(theUri,null,theFilter), theOffset, theCount); + } - source.getCompose().addInclude().addValueSet(theUri); - - if (isNotBlank(theFilter)) { - ConceptSetComponent include = source.getCompose().addInclude(); - ConceptSetFilterComponent filter = include.addFilter(); - filter.setProperty("display"); - filter.setOp(FilterOperator.EQUAL); - filter.setValue(theFilter); + @Override + public ValueSet expandByIdentifier(String theUri, String theValueSetVersion, String theFilter, int theOffset, int theCount) { + if (isBlank(theUri)) { + throw new InvalidRequestException("URI must not be blank or missing"); } - ValueSet retVal = doExpand(source, theOffset, theCount); - return retVal; + return doExpand(createSourceValueSet(theUri,theValueSetVersion,theFilter), theOffset, theCount); } @Override @@ -194,6 +195,9 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao ValueSet toExpand = new ValueSet(); toExpand.setId(theSource.getId()); toExpand.setUrl(theSource.getUrl()); + if (theSource.getVersion() != null) { + toExpand.setVersion(theSource.getVersion()); + } for (ConceptSetComponent next : theSource.getCompose().getInclude()) { toExpand.getCompose().addInclude(next); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 7000debd950..7f6002d31ad 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -20,39 +20,28 @@ package ca.uhn.fhir.jpa.dao.r5; * #L% */ -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.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; 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.jpa.util.LogicUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ElementUtil; -import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Enumerations; import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; -import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; -import org.hl7.fhir.utilities.validation.ValidationOptions; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import java.util.Collections; import java.util.Date; import java.util.List; @@ -64,15 +53,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoValueSet { - @Autowired - @Qualifier("myDefaultProfileValidationSupport") - private IValidationSupport myDefaultProfileValidationSupport; - private IValidationSupport myValidationSupport; - @Autowired - private IFhirResourceDaoCodeSystem myCodeSystemDao; - @Override public void start() { super.start(); @@ -113,21 +95,7 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao throw new InvalidRequestException("URI must not be blank or missing"); } - ValueSet source = new ValueSet(); - source.setUrl(theUri); - - source.getCompose().addInclude().addValueSet(theUri); - - if (isNotBlank(theFilter)) { - ConceptSetComponent include = source.getCompose().addInclude(); - ConceptSetFilterComponent filter = include.addFilter(); - filter.setProperty("display"); - filter.setOp(Enumerations.FilterOperator.EQUAL); - filter.setValue(theFilter); - } - - ValueSet retVal = doExpand(source); - return retVal; + return doExpand(createSourceValueSet(theUri, null, theFilter)); // if (defaultValueSet != null) { // source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet)); @@ -142,27 +110,55 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao // return expand(defaultValueSet, theFilter); } + @Override + public ValueSet expandByIdentifier(String theUri, String theValueSetUri, String theFilter) { + if (isBlank(theUri)) { + throw new InvalidRequestException("URI must not be blank or missing"); + } + + return doExpand(createSourceValueSet(theUri, theValueSetUri, theFilter)); + + } + + private ValueSet createSourceValueSet(String theUri, String theValueSetVersion, String theFilter) { + ValueSet source = new ValueSet(); + source.setUrl(theUri); + if (theValueSetVersion != null) { + source.setVersion(theValueSetVersion); + } + + if (theValueSetVersion != null) { + source.getCompose().addInclude().addValueSet(theUri + "|" +theValueSetVersion); + } else { + source.getCompose().addInclude().addValueSet(theUri); + } + + if (isNotBlank(theFilter)) { + ValueSet.ConceptSetComponent include = source.getCompose().addInclude(); + ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + filter.setProperty("display"); + filter.setOp(Enumerations.FilterOperator.EQUAL); + filter.setValue(theFilter); + } + return source; + } + @Override public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { if (isBlank(theUri)) { throw new InvalidRequestException("URI must not be blank or missing"); } - ValueSet source = new ValueSet(); - source.setUrl(theUri); + return doExpand(createSourceValueSet(theUri, null, theFilter), theOffset, theCount); + } - source.getCompose().addInclude().addValueSet(theUri); - - if (isNotBlank(theFilter)) { - ConceptSetComponent include = source.getCompose().addInclude(); - ConceptSetFilterComponent filter = include.addFilter(); - filter.setProperty("display"); - filter.setOp(Enumerations.FilterOperator.EQUAL); - filter.setValue(theFilter); + @Override + public ValueSet expandByIdentifier(String theUri, String theValueSetVersion, String theFilter, int theOffset, int theCount) { + if (isBlank(theUri)) { + throw new InvalidRequestException("URI must not be blank or missing"); } - ValueSet retVal = doExpand(source, theOffset, theCount); - return retVal; + return doExpand(createSourceValueSet(theUri, theValueSetVersion, theFilter), theOffset, theCount); } @Override @@ -200,6 +196,9 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao ValueSet toExpand = new ValueSet(); toExpand.setId(theSource.getId()); toExpand.setUrl(theSource.getUrl()); + if (theSource.getVersion() != null) { + toExpand.setVersion(theSource.getVersion()); + } for (ConceptSetComponent next : theSource.getCompose().getInclude()) { toExpand.getCompose().addInclude(next); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java index bd394f64027..3ce3a72c1ec 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java @@ -46,6 +46,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst // Note: url is correct and identifier is not, but identifier was only added as // of 3.1.0 so we'll leave url for now. See: https://groups.google.com/d/msgid/hapi-fhir/CAN2Cfy8kW%2BAOkgC6VjPsU3gRCpExCNZBmJdi-k5R_TWeyWH4tA%40mail.gmail.com?utm_medium=email&utm_source=footer @OperationParam(name = "url", min = 0, max = 1) UriType theUrl, + @OperationParam(name = "valueSetVersion", min = 0, max = 1) org.hl7.fhir.r4.model.StringType theValueSetVersion, @OperationParam(name = "identifier", min = 0, max = 1) UriType theIdentifier, @OperationParam(name = "filter", min = 0, max = 1) StringType theFilter, @OperationParam(name = "offset", min = 0, max = 1) IntegerType theOffset, @@ -60,6 +61,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst boolean haveIdentifier = url != null && isNotBlank(url.getValue()); boolean haveValueSet = theValueSet != null && !theValueSet.isEmpty(); + boolean haveValueSetVersion = theValueSetVersion != null && !theValueSetVersion.isEmpty(); if (!haveId && !haveIdentifier && !haveValueSet) { throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request."); @@ -99,7 +101,11 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst if (haveId) { return dao.expand(theId, toFilterString(theFilter), offset, count, theRequestDetails); } else if (haveIdentifier) { - return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter), offset, count); + if (haveValueSetVersion) { + return dao.expandByIdentifier(url.getValue(), theValueSetVersion.getValue(), toFilterString(theFilter), offset, count); + } else { + return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter), offset, count); + } } else { return dao.expand(theValueSet, toFilterString(theFilter), offset, count); } @@ -107,7 +113,11 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst if (haveId) { return dao.expand(theId, toFilterString(theFilter), theRequestDetails); } else if (haveIdentifier) { - return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter)); + if (haveValueSetVersion) { + return dao.expandByIdentifier(url.getValue(), theValueSetVersion.getValue(), toFilterString(theFilter)); + } else { + return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter)); + } } else { return dao.expand(theValueSet, toFilterString(theFilter)); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java index c8d4890cf04..6c32c0def2f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java @@ -44,6 +44,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4 theAddedCodes, TermConcept theConcept, boolean theAdd, AtomicInteger theCodeCounter) { String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri(); + String codeSystemVersion = theConcept.getCodeSystemVersion().getCodeSystemVersionId(); String code = theConcept.getCode(); String display = theConcept.getDisplay(); Collection designations = theConcept.getDesignations(); - addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, code, display); + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, codeSystemVersion, code, display); + } + + private void addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, Collection theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCodeSystemVersion, String theCode, String theDisplay) { + if (isNoneBlank(theCodeSystem, theCode)) { + if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode + "|" + theCodeSystemVersion)) { + theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCodeSystemVersion, theCode, theDisplay, theDesignations); + theCodeCounter.incrementAndGet(); + } + + if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode + "|" + theCodeSystemVersion)) { + theValueSetCodeAccumulator.excludeConcept(theCodeSystem, theCodeSystemVersion, theCode); + theCodeCounter.decrementAndGet(); + } + } } private void addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, Collection theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCode, String theDisplay) { @@ -609,7 +625,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ArrayList retVal = new ArrayList<>(); for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent nextContains : expandedR4.getContains()) { - retVal.add(new VersionIndependentConcept(nextContains.getSystem(), nextContains.getCode(), nextContains.getDisplay())); + retVal.add(new VersionIndependentConcept(nextContains.getSystem(), nextContains.getCode(), nextContains.getDisplay(), nextContains.getVersion())); } return retVal; } @@ -740,8 +756,14 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TermCodeSystem codeSystem = uriToCodeSystem.get(nextConcept.getSystem()); if (codeSystem != null) { + TermCodeSystemVersion termCodeSystemVersion; + if (nextConcept.getSystemVersion() != null) { + termCodeSystemVersion = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(codeSystem.getPid(), nextConcept.getSystemVersion()); + } else { + termCodeSystemVersion = codeSystem.getCurrentVersion(); + } myConceptDao - .findByCodeSystemAndCode(codeSystem.getCurrentVersion(), nextConcept.getCode()) + .findByCodeSystemAndCode(termCodeSystemVersion, nextConcept.getCode()) .ifPresent(concept -> addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, concept, theAdd, theCodeCounter) ); @@ -770,7 +792,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Nonnull private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs) { - TermCodeSystemVersion csv = theCs.getCurrentVersion(); + String codeSystemVersion = theIncludeOrExclude.getVersion(); + TermCodeSystemVersion csv; + if (isEmpty(codeSystemVersion)) { + csv = theCs.getCurrentVersion(); + } else { + csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCs.getPid(), codeSystemVersion); + } FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); /* @@ -797,7 +825,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Filters */ - handleFilters(bool, theSystem, qb, theIncludeOrExclude); + handleFilters(bool, theSystem, codeSystemVersion, qb, theIncludeOrExclude); Query luceneQuery = bool.createQuery(); @@ -906,15 +934,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void handleFilters(BooleanJunction theBool, String theSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { + private void handleFilters(BooleanJunction theBool, String theSystem, String theSystemVersion, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { if (theIncludeOrExclude.getFilter().size() > 0) { for (ValueSet.ConceptSetFilterComponent nextFilter : theIncludeOrExclude.getFilter()) { - handleFilter(theSystem, theQb, theBool, nextFilter); + handleFilter(theSystem, theSystemVersion, theQb, theBool, nextFilter); } } } - private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilter(String theSystem, String theSystemVersion, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { if (isBlank(theFilter.getValue()) && theFilter.getOp() == null && isBlank(theFilter.getProperty())) { return; } @@ -930,7 +958,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "concept": case "code": - handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter); + handleFilterConceptAndCode(theSystem, theSystemVersion, theQb, theBool, theFilter); break; case "parent": case "child": @@ -939,11 +967,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "ancestor": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestor(theSystem, theBool, theFilter); + handleFilterLoincAncestor(theSystem, theSystemVersion, theBool, theFilter); break; case "descendant": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincDescendant(theSystem, theBool, theFilter); + handleFilterLoincDescendant(theSystem, theSystemVersion, theBool, theFilter); break; case "copyright": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); @@ -993,8 +1021,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { bool.must(textQuery); } - private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - TermConcept code = findCode(theSystem, theFilter.getValue()) + private void handleFilterConceptAndCode(String theSystem, String theSystemVersion, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + TermConcept code = findCode(theSystem, theFilter.getValue(), theSystemVersion) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theFilter.getValue())); if (theFilter.getOp() == ValueSet.FilterOperator.ISA) { @@ -1039,41 +1067,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincAncestor(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincAncestor(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterAncestorEqual(theSystem, theBool, theFilter); + addLoincFilterAncestorEqual(theSystem, theSystemVersion, theBool, theFilter); break; case IN: - addLoincFilterAncestorIn(theSystem, theBool, theFilter); + addLoincFilterAncestorIn(theSystem, theSystemVersion, theBool, theFilter); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - addLoincFilterAncestorEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); + private void addLoincFilterAncestorEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterAncestorEqual(theSystem, theSystemVersion, theBool, theFilter.getProperty(), theFilter.getValue()); } - private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { - List terms = getAncestorTerms(theSystem, theProperty, theValue); + private void addLoincFilterAncestorEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getAncestorTerms(theSystem, theSystemVersion, theProperty, theValue); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterAncestorIn(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterAncestorIn(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value)); + terms.addAll(getAncestorTerms(theSystem, theSystemVersion, theFilter.getProperty(), value)); } theBool.must(new TermsQuery(terms)); } - private List getAncestorTerms(String theSystem, String theProperty, String theValue) { + private List getAncestorTerms(String theSystem, String theSystemVersion, String theProperty, String theValue) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue) + TermConcept code = findCode(theSystem, theValue, theSystemVersion) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); retVal.add(new Term("myParentPids", "" + code.getId())); @@ -1083,41 +1111,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincDescendant(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincDescendant(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterDescendantEqual(theSystem, theBool, theFilter); + addLoincFilterDescendantEqual(theSystem, theSystemVersion, theBool, theFilter); break; case IN: - addLoincFilterDescendantIn(theSystem, theBool, theFilter); + addLoincFilterDescendantIn(theSystem, theSystemVersion, theBool, theFilter); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); + private void addLoincFilterDescendantEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterDescendantEqual(theSystem, theSystemVersion, theBool, theFilter.getProperty(), theFilter.getValue()); } - private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { - List terms = getDescendantTerms(theSystem, theProperty, theValue); + private void addLoincFilterDescendantEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getDescendantTerms(theSystem, theSystemVersion, theProperty, theValue); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterDescendantIn(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterDescendantIn(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value)); + terms.addAll(getDescendantTerms(theSystem, theSystemVersion, theFilter.getProperty(), value)); } theBool.must(new TermsQuery(terms)); } - private List getDescendantTerms(String theSystem, String theProperty, String theValue) { + private List getDescendantTerms(String theSystem, String theSystemVersion, String theProperty, String theValue) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue) + TermConcept code = findCode(theSystem, theValue, theSystemVersion) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); String[] parentPids = code.getParentPidsAsString().split(" "); @@ -1391,7 +1419,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri, String theVersion) { StringBuilder key = new StringBuilder(theUri); if (theVersion != null) { - key.append("_").append(theVersion); + key.append("|").append(theVersion); } TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), t -> myTxTemplate.execute(tx -> { TermCodeSystemVersion csv = null; @@ -1842,21 +1870,21 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override @Transactional public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion, String theCodingAVersion, String theCodingBVersion) { - VersionIndependentConceptWithSystemVersion conceptA = toConcept(theCodeA, theSystem, theCodingA, theSystemVersion, theCodingAVersion); - VersionIndependentConceptWithSystemVersion conceptB = toConcept(theCodeB, theSystem, theCodingB, theSystemVersion, theCodingBVersion); + VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA, theSystemVersion, theCodingAVersion); + VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB, theSystemVersion, theCodingBVersion); if (!StringUtils.equals(conceptA.getSystem(), conceptB.getSystem())) { throw new InvalidRequestException("Unable to test subsumption across different code systems"); } - if (!StringUtils.equals(conceptA.getCodeSystemVersion(), conceptB.getCodeSystemVersion())) { + if (!StringUtils.equals(conceptA.getSystemVersion(), conceptB.getSystemVersion())) { throw new InvalidRequestException("Unable to test subsumption across different code system versions"); } - TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), conceptA.getCodeSystemVersion()) + TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), conceptA.getSystemVersion()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptA)); - TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), conceptB.getCodeSystemVersion()) + TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), conceptB.getSystemVersion()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptB)); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -2214,7 +2242,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem) { + public CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem) { IValidationSupport validationSupport = provideValidationSupport(); IBaseResource codeSystem = validationSupport.fetchCodeSystem(theSystem); if (codeSystem != null) { @@ -2423,7 +2451,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @NotNull - private VersionIndependentConceptWithSystemVersion toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType, IPrimitiveType theSystemVersionType, String theCodingVersionType) { + private VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType, IPrimitiveType theSystemVersionType, String theCodingVersionType) { String code = theCodeType != null ? theCodeType.getValueAsString() : null; String system = theSystemType != null ? theSystemType.getValueAsString() : null; String systemVersion = theSystemVersionType != null ? theSystemVersionType.getValueAsString() : null; @@ -2432,10 +2460,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { system = theCodingType.getSystem(); systemVersion = theCodingVersionType; } - return new VersionIndependentConceptWithSystemVersion(system, code, systemVersion); + return new VersionIndependentConcept(system, code, null, systemVersion); } - private static class VersionIndependentConceptWithSystemVersion extends VersionIndependentConcept { +/* private static class VersionIndependentConceptWithSystemVersion extends VersionIndependentConcept { String myCodeSystemVersion; @@ -2449,7 +2477,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - +*/ /** * This method is present only for unit tests, do not call from client code */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java index db098111343..f3b7dd24c5d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java @@ -33,8 +33,12 @@ public interface IValueSetConceptAccumulator { void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, @Nullable Collection theDesignations); + void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, @Nullable Collection theDesignations); + void excludeConcept(String theSystem, String theCode); + void excludeConcept(String theSystem, String theSystemVersion, String theCode); + @Nullable default Integer getCapacityRemaining() { return null; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index ac19b652593..b55e14cf0f0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -351,32 +351,23 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystemUri, "No system URI supplied"); // Grab the existing version so we can delete it - TermCodeSystem existingCodeSystem = myCodeSystemDao.findByCodeSystemUri(theSystemUri); - TermCodeSystemVersion existing = null; - if (existingCodeSystem != null) { - existing = getExistingTermCodeSystemVersion(existingCodeSystem.getPid(), theSystemVersionId); - } - - /* - * Get CodeSystem and validate CodeSystemVersion - */ - TermCodeSystem codeSystem = getOrCreateDistinctTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theSystemVersionId, theCodeSystemResourceTable); + List existing = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourcePid.getIdAsLong()); /* * Delete version being replaced. */ - if(existing != null) { - ourLog.info("Deleting old code system version {}", existing.getPid()); - Long codeSystemVersionPid = existing.getPid(); + for (TermCodeSystemVersion next : existing) { + ourLog.info("Deleting old code system version {}", next.getPid()); + Long codeSystemVersionPid = next.getPid(); deleteCodeSystemVersion(codeSystemVersionPid); - } /* * Do the upload */ + TermCodeSystem codeSystem = getOrCreateDistinctTermCodeSystem(theCodeSystemResourcePid, theSystemUri, theSystemName, theSystemVersionId, theCodeSystemResourceTable); theCodeSystemVersion.setCodeSystem(codeSystem); theCodeSystemVersion.setCodeSystemDisplayName(theSystemName); @@ -687,13 +678,14 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { if (codeSystem == null) { codeSystem = new TermCodeSystem(); } + } else { + checkForCodeSystemVersionDuplicate(codeSystem, theSystemUri, theSystemVersionId, theCodeSystemResourceTable); } codeSystem.setResource(theCodeSystemResourceTable); codeSystem.setCodeSystemUri(theSystemUri); codeSystem.setName(theSystemName); codeSystem = myCodeSystemDao.save(codeSystem); - checkForCodeSystemVersionDuplicate(codeSystem,theSystemUri, theSystemVersionId, theCodeSystemResourceTable); return codeSystem; } @@ -711,13 +703,6 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(theCodeSystem.getPid(), theSystemVersionId); if (codeSystemVersionEntity != null) { msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotCreateDuplicateCodeSystemUrlAndVersion", theSystemUri, theSystemVersionId, codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue()); - } else { - // Check if a TermCodeSystemVersion entity already exists for this CodeSystem resource (i.e. with a different version or URL) - codeSystemVersionEntity = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemResourceTable.getId()); - if (codeSystemVersionEntity != null) { - msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "cannotUpdateUrlOrVersionForCodeSystemResource", codeSystemVersionEntity.getResource().getIdDt().toUnqualifiedVersionless().getValue(), theSystemUri, theSystemVersionId); - throw new UnprocessableEntityException(msg); - } } } // Throw exception if the TermCodeSystemVersion is being duplicated. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java index e27177f3f1a..3b4cbaff184 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImpl.java @@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.term; * #L% */ -import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; @@ -69,14 +68,12 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { @Autowired protected PlatformTransactionManager myTransactionMgr; private boolean myProcessDeferred = true; - private List myDefferedCodeSystemsDeletions = Collections.synchronizedList(new ArrayList<>()); - private List myDefferedCodeSystemVersionsDeletions = Collections.synchronizedList(new ArrayList<>()); - private List myDeferredConcepts = Collections.synchronizedList(new ArrayList<>()); - private List myDeferredValueSets = Collections.synchronizedList(new ArrayList<>()); - private List myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>()); - private List myConceptLinksToSaveLater = Collections.synchronizedList(new ArrayList<>()); - @Autowired - private DaoConfig myDaoConfig; + final private List myDefferedCodeSystemsDeletions = Collections.synchronizedList(new ArrayList<>()); + final private List myDefferedCodeSystemVersionsDeletions = Collections.synchronizedList(new ArrayList<>()); + final private List myDeferredConcepts = Collections.synchronizedList(new ArrayList<>()); + final private List myDeferredValueSets = Collections.synchronizedList(new ArrayList<>()); + final private List myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>()); + final private List myConceptLinksToSaveLater = Collections.synchronizedList(new ArrayList<>()); @Autowired private ITermConceptParentChildLinkDao myConceptParentChildLinkDao; @Autowired @@ -121,9 +118,11 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { @Override @Transactional public void deleteCodeSystemForResource(ResourceTable theCodeSystemToDelete) { - TermCodeSystemVersion codeSystemVersionToDelete = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemToDelete.getResourceId()); - if (codeSystemVersionToDelete != null) { - myDefferedCodeSystemVersionsDeletions.add(codeSystemVersionToDelete); + List codeSystemVersionsToDelete = myCodeSystemVersionDao.findByCodeSystemResourcePid(theCodeSystemToDelete.getResourceId()); + for (TermCodeSystemVersion codeSystemVersionToDelete : codeSystemVersionsToDelete){ + if (codeSystemVersionToDelete != null) { + myDefferedCodeSystemVersionsDeletions.add(codeSystemVersionToDelete); + } } TermCodeSystem codeSystemToDelete = myCodeSystemDao.findByResourcePid(theCodeSystemToDelete.getResourceId()); if (codeSystemToDelete != null) { @@ -297,8 +296,7 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { @Override public boolean isStorageQueueEmpty() { - boolean retVal = true; - retVal &= !isProcessDeferredPaused(); + boolean retVal = !isProcessDeferredPaused(); retVal &= !isDeferredConcepts(); retVal &= !isConceptLinksToSaveLater(); retVal &= !isDeferredValueSets(); @@ -368,11 +366,6 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc { myTransactionMgr = theTxManager; } - @VisibleForTesting - void setDaoConfigForUnitTest(DaoConfig theDaoConfig) { - myDaoConfig = theDaoConfig; - } - @VisibleForTesting void setCodeSystemStorageSvcForUnitTest(ITermCodeSystemStorageSvc theCodeSystemStorageSvc) { myCodeSystemStorageSvc = theCodeSystemStorageSvc; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java index fda53d03c5e..30345a91def 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java @@ -71,7 +71,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } @Override - public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { + public void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, Collection theDesignations) { TermValueSetConcept concept = saveConcept(theSystem, theCode, theDisplay); if (theDesignations != null) { for (TermConceptDesignation designation : theDesignations) { @@ -80,8 +80,18 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } } + @Override + public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { + includeConceptWithDesignations(theSystem, null, theCode, theDisplay, theDesignations); + } + @Override public void excludeConcept(String theSystem, String theCode) { + excludeConcept(theSystem, null, theCode); + } + + @Override + public void excludeConcept(String theSystem, String theSystemVersion, String theCode) { if (isAnyBlank(theSystem, theCode)) { return; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java index 65bba09b3c8..b3af6a3ca15 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java @@ -73,12 +73,15 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V } @Override - public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { + public void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, Collection theDesignations) { incrementConceptsCount(); ValueSet.ValueSetExpansionContainsComponent contains = this.addContains(); contains.setSystem(theSystem); contains.setCode(theCode); contains.setDisplay(theDisplay); + if (theSystemVersion != null) { + contains.setVersion(theSystemVersion); + } if (theDesignations != null) { for (TermConceptDesignation termConceptDesignation : theDesignations) { contains @@ -93,6 +96,21 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V } } + @Override + public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { + this.includeConceptWithDesignations(theSystem, null, theCode, theDisplay, theDesignations); + } + + @Override + public void excludeConcept(String theSystem, String theSystemVersion, String theCode) { + this + .getContains() + .removeIf(t -> + theSystem.equals(t.getSystem()) && + theCode.equals(t.getCode()) && + theSystemVersion.equals(t.getVersion())); + } + @Override public void excludeConcept(String theSystem, String theCode) { this diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetMultiVersionTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetMultiVersionTest.java new file mode 100644 index 00000000000..f01ca08a9a5 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetMultiVersionTest.java @@ -0,0 +1,222 @@ +package ca.uhn.fhir.jpa.dao.dstu3; + +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import org.hl7.fhir.dstu3.model.CodeSystem; +import org.hl7.fhir.dstu3.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class FhirResourceDaoDstu3ValueSetMultiVersionTest extends BaseJpaDstu3Test { + + public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; + public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; + + private enum ValueSetVersions { NULL, V1, V2 } + + @Autowired + protected ITermValueSetConceptDao myTermValueSetConceptDao; + + private DaoMethodOutcome createLocalCsAndVs(String theVersion, Set theCodeSystemCodes) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion(theVersion); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + for (String codeSystemCode : theCodeSystemCodes) { + codeSystem.addConcept().setCode(codeSystemCode); + } + myCodeSystemDao.create(codeSystem, mySrd); + + return createLocalVs(codeSystem, theVersion); + + } + + private DaoMethodOutcome createLocalVs(CodeSystem theCodeSystem, String theVersion) { + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.setVersion(theVersion); + if (theVersion == null) { + valueSet.setName("ValueSet_noVersion"); + } else { + valueSet.setName("ValueSet_"+theVersion); + } + valueSet.getCompose().addInclude().setSystem(theCodeSystem.getUrl()); + return myValueSetDao.create(valueSet, mySrd); + } + + private Map createVersionedValueSets() { + + HashMap valueSets = new HashMap<>(); + + Set valueSetConcepts_noVersion = new HashSet<>(); + valueSetConcepts_noVersion.add("hello"); + valueSetConcepts_noVersion.add("goodbye"); + valueSets.put(ValueSetVersions.NULL, createLocalCsAndVs(null, valueSetConcepts_noVersion)); + + Set valueSetConcepts_v1 = new HashSet<>(valueSetConcepts_noVersion); + valueSetConcepts_v1.add("hi"); + valueSets.put(ValueSetVersions.V1, createLocalCsAndVs("v1", valueSetConcepts_v1)); + + Set valueSetConcepts_v2 = new HashSet<>(valueSetConcepts_v1); + valueSetConcepts_v2.add("so-long"); + valueSets.put(ValueSetVersions.V2, createLocalCsAndVs("v2", valueSetConcepts_v2)); + + return valueSets; + } + + @Test + public void testCreateVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + Optional optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + assertTrue(optionalTermValueSet.isPresent()); + Long nullVersion_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.NULL).getEntity()).getId(); + assertNotNull(nullVersion_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(nullVersion_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_noVersion", optionalTermValueSet.get().getName()); + + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + assertTrue(optionalTermValueSet.isPresent()); + Long v1Version_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.V1).getEntity()).getId(); + assertNotNull(v1Version_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(v1Version_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_v1", optionalTermValueSet.get().getName()); + + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2"); + assertTrue(optionalTermValueSet.isPresent()); + Long v2Version_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.V2).getEntity()).getId(); + assertNotNull(v2Version_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(v2Version_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_v2", optionalTermValueSet.get().getName()); + + } + + @Test + public void testUpdateVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + TermValueSet termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertEquals("ValueSet_noVersion", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertEquals("ValueSet_v1", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertEquals("ValueSet_v2", termValueSet.getName()); + + // Update ValueSets + ValueSet updated = (ValueSet)myValueSets.get(ValueSetVersions.NULL).getResource(); + updated.setName("ValueSet_noVersion_updated"); + DaoMethodOutcome nullVersion_update_outcome = myValueSetDao.update(updated); + Long nullVersion_resid = ((ResourceTable)nullVersion_update_outcome.getEntity()).getId(); + + updated = (ValueSet)myValueSets.get(ValueSetVersions.V1).getResource(); + updated.setName("ValueSet_v1_updated"); + DaoMethodOutcome v1Version_update_outcome = myValueSetDao.update(updated); + Long v1Version_resid = ((ResourceTable)v1Version_update_outcome.getEntity()).getId(); + + updated = (ValueSet)myValueSets.get(ValueSetVersions.V2).getResource(); + updated.setName("ValueSet_v2_updated"); + DaoMethodOutcome v2Version_update_outcome = myValueSetDao.update(updated); + Long v2Version_resid = ((ResourceTable)v2Version_update_outcome.getEntity()).getId(); + + // Verify that ValueSets were updated. + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertNotNull(nullVersion_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(nullVersion_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_noVersion_updated", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertNotNull(v1Version_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(v1Version_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_v1_updated", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertNotNull(v2Version_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(v2Version_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_v2_updated", termValueSet.getName()); + + } + + @Test + public void testDeleteVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + TermValueSet termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertEquals("ValueSet_noVersion", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertEquals("ValueSet_v1", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertEquals("ValueSet_v2", termValueSet.getName()); + + // Delete ValueSets + myValueSetDao.delete(myValueSets.get(ValueSetVersions.NULL).getResource().getIdElement()); + assertEquals(2, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + Optional optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + + myValueSetDao.delete(myValueSets.get(ValueSetVersions.V1).getResource().getIdElement()); + assertEquals(1, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + + myValueSetDao.delete(myValueSets.get(ValueSetVersions.V2).getResource().getIdElement()); + assertEquals(0, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index 29db57d857f..b589f843df2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -235,6 +235,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { @Test public void testCodeSystemCreateDuplicateFails() { + // No version. CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.COMPLETE); @@ -249,6 +250,25 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { } catch (UnprocessableEntityException e) { assertEquals("Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/" + id.getIdPart(), e.getMessage()); } + + // With version. + codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("1"); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("1"); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + try { + myCodeSystemDao.create(codeSystem, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Can not create multiple CodeSystem resources with CodeSystem.url \"http://example.com/my_code_system\" and CodeSystem.version \"1\", already have one with resource ID: CodeSystem/" + id.getIdPart(), e.getMessage()); + } + } @Test diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java new file mode 100644 index 00000000000..14faf7eef6e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java @@ -0,0 +1,222 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class FhirResourceDaoR4ValueSetMultiVersionTest extends BaseJpaR4Test { + + public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; + public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; + + private enum ValueSetVersions { NULL, V1, V2 } + + @Autowired + protected ITermValueSetConceptDao myTermValueSetConceptDao; + + private DaoMethodOutcome createLocalCsAndVs(String theVersion, Set theCodeSystemCodes) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion(theVersion); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + for (String codeSystemCode : theCodeSystemCodes) { + codeSystem.addConcept().setCode(codeSystemCode); + } + myCodeSystemDao.create(codeSystem, mySrd); + + return createLocalVs(codeSystem, theVersion); + + } + + private DaoMethodOutcome createLocalVs(CodeSystem theCodeSystem, String theVersion) { + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.setVersion(theVersion); + if (theVersion == null) { + valueSet.setName("ValueSet_noVersion"); + } else { + valueSet.setName("ValueSet_"+theVersion); + } + valueSet.getCompose().addInclude().setSystem(theCodeSystem.getUrl()); + return myValueSetDao.create(valueSet, mySrd); + } + + private Map createVersionedValueSets() { + + HashMap valueSets = new HashMap<>(); + + Set valueSetConcepts_noVersion = new HashSet<>(); + valueSetConcepts_noVersion.add("hello"); + valueSetConcepts_noVersion.add("goodbye"); + valueSets.put(ValueSetVersions.NULL, createLocalCsAndVs(null, valueSetConcepts_noVersion)); + + Set valueSetConcepts_v1 = new HashSet<>(valueSetConcepts_noVersion); + valueSetConcepts_v1.add("hi"); + valueSets.put(ValueSetVersions.V1, createLocalCsAndVs("v1", valueSetConcepts_v1)); + + Set valueSetConcepts_v2 = new HashSet<>(valueSetConcepts_v1); + valueSetConcepts_v2.add("so-long"); + valueSets.put(ValueSetVersions.V2, createLocalCsAndVs("v2", valueSetConcepts_v2)); + + return valueSets; + } + + @Test + public void testCreateVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + Optional optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + assertTrue(optionalTermValueSet.isPresent()); + Long nullVersion_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.NULL).getEntity()).getId(); + assertNotNull(nullVersion_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(nullVersion_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_noVersion", optionalTermValueSet.get().getName()); + + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + assertTrue(optionalTermValueSet.isPresent()); + Long v1Version_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.V1).getEntity()).getId(); + assertNotNull(v1Version_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(v1Version_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_v1", optionalTermValueSet.get().getName()); + + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2"); + assertTrue(optionalTermValueSet.isPresent()); + Long v2Version_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.V2).getEntity()).getId(); + assertNotNull(v2Version_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(v2Version_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_v2", optionalTermValueSet.get().getName()); + + } + + @Test + public void testUpdateVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + TermValueSet termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertEquals("ValueSet_noVersion", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertEquals("ValueSet_v1", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertEquals("ValueSet_v2", termValueSet.getName()); + + // Update ValueSets + ValueSet updated = (ValueSet)myValueSets.get(ValueSetVersions.NULL).getResource(); + updated.setName("ValueSet_noVersion_updated"); + DaoMethodOutcome nullVersion_update_outcome = myValueSetDao.update(updated); + Long nullVersion_resid = ((ResourceTable)nullVersion_update_outcome.getEntity()).getId(); + + updated = (ValueSet)myValueSets.get(ValueSetVersions.V1).getResource(); + updated.setName("ValueSet_v1_updated"); + DaoMethodOutcome v1Version_update_outcome = myValueSetDao.update(updated); + Long v1Version_resid = ((ResourceTable)v1Version_update_outcome.getEntity()).getId(); + + updated = (ValueSet)myValueSets.get(ValueSetVersions.V2).getResource(); + updated.setName("ValueSet_v2_updated"); + DaoMethodOutcome v2Version_update_outcome = myValueSetDao.update(updated); + Long v2Version_resid = ((ResourceTable)v2Version_update_outcome.getEntity()).getId(); + + // Verify that ValueSets were updated. + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertNotNull(nullVersion_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(nullVersion_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_noVersion_updated", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertNotNull(v1Version_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(v1Version_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_v1_updated", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertNotNull(v2Version_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(v2Version_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_v2_updated", termValueSet.getName()); + + } + + @Test + public void testDeleteVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + TermValueSet termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertEquals("ValueSet_noVersion", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertEquals("ValueSet_v1", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertEquals("ValueSet_v2", termValueSet.getName()); + + // Delete ValueSets + myValueSetDao.delete(myValueSets.get(ValueSetVersions.NULL).getResource().getIdElement()); + assertEquals(2, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + Optional optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + + myValueSetDao.delete(myValueSets.get(ValueSetVersions.V1).getResource().getIdElement()); + assertEquals(1, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + + myValueSetDao.delete(myValueSets.get(ValueSetVersions.V2).getResource().getIdElement()); + assertEquals(0, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetMultiVersionTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetMultiVersionTest.java new file mode 100644 index 00000000000..147f4b6806e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetMultiVersionTest.java @@ -0,0 +1,222 @@ +package ca.uhn.fhir.jpa.dao.r5; + +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.ValueSet; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class FhirResourceDaoR5ValueSetMultiVersionTest extends BaseJpaR5Test { + + public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; + public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; + + private enum ValueSetVersions { NULL, V1, V2 } + + @Autowired + protected ITermValueSetConceptDao myTermValueSetConceptDao; + + private DaoMethodOutcome createLocalCsAndVs(String theVersion, Set theCodeSystemCodes) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion(theVersion); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + for (String codeSystemCode : theCodeSystemCodes) { + codeSystem.addConcept().setCode(codeSystemCode); + } + myCodeSystemDao.create(codeSystem, mySrd); + + return createLocalVs(codeSystem, theVersion); + + } + + private DaoMethodOutcome createLocalVs(CodeSystem theCodeSystem, String theVersion) { + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.setVersion(theVersion); + if (theVersion == null) { + valueSet.setName("ValueSet_noVersion"); + } else { + valueSet.setName("ValueSet_"+theVersion); + } + valueSet.getCompose().addInclude().setSystem(theCodeSystem.getUrl()); + return myValueSetDao.create(valueSet, mySrd); + } + + private Map createVersionedValueSets() { + + HashMap valueSets = new HashMap<>(); + + Set valueSetConcepts_noVersion = new HashSet<>(); + valueSetConcepts_noVersion.add("hello"); + valueSetConcepts_noVersion.add("goodbye"); + valueSets.put(ValueSetVersions.NULL, createLocalCsAndVs(null, valueSetConcepts_noVersion)); + + Set valueSetConcepts_v1 = new HashSet<>(valueSetConcepts_noVersion); + valueSetConcepts_v1.add("hi"); + valueSets.put(ValueSetVersions.V1, createLocalCsAndVs("v1", valueSetConcepts_v1)); + + Set valueSetConcepts_v2 = new HashSet<>(valueSetConcepts_v1); + valueSetConcepts_v2.add("so-long"); + valueSets.put(ValueSetVersions.V2, createLocalCsAndVs("v2", valueSetConcepts_v2)); + + return valueSets; + } + + @Test + public void testCreateVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + Optional optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + assertTrue(optionalTermValueSet.isPresent()); + Long nullVersion_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.NULL).getEntity()).getId(); + assertNotNull(nullVersion_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(nullVersion_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_noVersion", optionalTermValueSet.get().getName()); + + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + assertTrue(optionalTermValueSet.isPresent()); + Long v1Version_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.V1).getEntity()).getId(); + assertNotNull(v1Version_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(v1Version_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_v1", optionalTermValueSet.get().getName()); + + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2"); + assertTrue(optionalTermValueSet.isPresent()); + Long v2Version_resid = ((ResourceTable)myValueSets.get(ValueSetVersions.V2).getEntity()).getId(); + assertNotNull(v2Version_resid); + assertNotNull(optionalTermValueSet.get().getResource()); + assertEquals(v2Version_resid, optionalTermValueSet.get().getResource().getId()); + assertEquals("ValueSet_v2", optionalTermValueSet.get().getName()); + + } + + @Test + public void testUpdateVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + TermValueSet termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertEquals("ValueSet_noVersion", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertEquals("ValueSet_v1", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertEquals("ValueSet_v2", termValueSet.getName()); + + // Update ValueSets + ValueSet updated = (ValueSet)myValueSets.get(ValueSetVersions.NULL).getResource(); + updated.setName("ValueSet_noVersion_updated"); + DaoMethodOutcome nullVersion_update_outcome = myValueSetDao.update(updated); + Long nullVersion_resid = ((ResourceTable)nullVersion_update_outcome.getEntity()).getId(); + + updated = (ValueSet)myValueSets.get(ValueSetVersions.V1).getResource(); + updated.setName("ValueSet_v1_updated"); + DaoMethodOutcome v1Version_update_outcome = myValueSetDao.update(updated); + Long v1Version_resid = ((ResourceTable)v1Version_update_outcome.getEntity()).getId(); + + updated = (ValueSet)myValueSets.get(ValueSetVersions.V2).getResource(); + updated.setName("ValueSet_v2_updated"); + DaoMethodOutcome v2Version_update_outcome = myValueSetDao.update(updated); + Long v2Version_resid = ((ResourceTable)v2Version_update_outcome.getEntity()).getId(); + + // Verify that ValueSets were updated. + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertNotNull(nullVersion_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(nullVersion_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_noVersion_updated", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertNotNull(v1Version_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(v1Version_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_v1_updated", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertNotNull(v2Version_resid); + assertNotNull(termValueSet.getResource()); + assertEquals(v2Version_resid, termValueSet.getResource().getId()); + assertEquals("ValueSet_v2_updated", termValueSet.getName()); + + } + + @Test + public void testDeleteVersionedValueSets() { + Map myValueSets = createVersionedValueSets(); + + assertEquals(3, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + + TermValueSet termValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET).orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " with null version")); + assertEquals("ValueSet_noVersion", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + assertEquals("ValueSet_v1", termValueSet.getName()); + + termValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + assertEquals("ValueSet_v2", termValueSet.getName()); + + // Delete ValueSets + myValueSetDao.delete(myValueSets.get(ValueSetVersions.NULL).getResource().getIdElement()); + assertEquals(2, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + Optional optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v1")); + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + + myValueSetDao.delete(myValueSets.get(ValueSetVersions.V1).getResource().getIdElement()); + assertEquals(1, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2").orElseThrow(() -> new IllegalArgumentException("No TerValueSet found for " + URL_MY_VALUE_SET + " version v2")); + + myValueSetDao.delete(myValueSets.get(ValueSetVersions.V2).getResource().getIdElement()); + assertEquals(0, myTermValueSetDao.findTermValueSetByUrl(PageRequest.of(0, 10), URL_MY_VALUE_SET).size()); + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndNullVersion(URL_MY_VALUE_SET); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v1"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + optionalTermValueSet = myTermValueSetDao.findTermValueSetByUrlAndVersion(URL_MY_VALUE_SET, "v2"); + if (optionalTermValueSet.isPresent()) { + fail(); + } + + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index 48986d9b46c..6b2fed44ea6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -1,9 +1,16 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.junit.jupiter.api.BeforeEach; @@ -18,6 +25,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test private static final String SYSTEM_PARENTCHILD = "http://parentchild"; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemTest.class); + private Long parentChildCsId; @BeforeEach @Transactional @@ -27,6 +35,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test CodeSystem parentChildCs = new CodeSystem(); parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setName("Parent Child CodeSystem"); parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); @@ -35,7 +44,8 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B"); - myCodeSystemDao.create(parentChildCs); + DaoMethodOutcome parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCsId = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); } @@ -416,5 +426,25 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test assertEquals(ConceptSubsumptionOutcome.NOTSUBSUMED.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); } + @Test + public void testUpdateCodeSystemName() throws IOException { + + CodeSystem initialCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCsId).execute(); + assertEquals("Parent Child CodeSystem", initialCodeSystem.getName()); + initialCodeSystem.setName("Updated Parent Child CodeSystem"); + String encoded = myFhirCtx.newJsonParser().encodeResourceToString(initialCodeSystem); + HttpPut putRequest = new HttpPut(ourServerBase + "/CodeSystem/" + parentChildCsId); + putRequest.setEntity(new StringEntity(encoded, ContentType.parse("application/json+fhir"))); + CloseableHttpResponse resp = ourHttpClient.execute(putRequest); + try { + assertEquals(200, resp.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(resp); + } + + CodeSystem updatedCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCsId).execute(); + assertEquals("Updated Parent Child CodeSystem", updatedCodeSystem.getName()); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java index 5e621e84ad7..485d02df39b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java @@ -1,9 +1,16 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeType; @@ -12,7 +19,6 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.UriType; -import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -27,6 +33,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi private static final String SYSTEM_PARENTCHILD = "http://parentchild"; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemVersionedTest.class); + private long parentChildCs1Id; + private long parentChildCs2Id; @BeforeEach @Transactional @@ -48,6 +56,7 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi CodeSystem parentChildCs = new CodeSystem(); parentChildCs.setUrl(SYSTEM_PARENTCHILD); parentChildCs.setVersion("1"); + parentChildCs.setName("Parent Child CodeSystem 1"); parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); @@ -57,10 +66,12 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi parentA.addConcept().setCode("ParentC").setDisplay("Parent C"); parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B"); - myCodeSystemDao.create(parentChildCs); + DaoMethodOutcome parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCs1Id = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); parentChildCs = new CodeSystem(); parentChildCs.setVersion("2"); + parentChildCs.setName("Parent Child CodeSystem 2"); parentChildCs.setUrl(SYSTEM_PARENTCHILD); parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); @@ -71,7 +82,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi parentA.addConcept().setCode("ParentB").setDisplay("Parent B v2"); parentChildCs.addConcept().setCode("ParentC").setDisplay("Parent C v2"); - myCodeSystemDao.create(parentChildCs); + parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCs2Id = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); } @@ -770,5 +782,41 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi } + @Test + public void testUpdateCodeSystemName() throws IOException { + + CodeSystem initialCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCs1Id).execute(); + assertEquals("Parent Child CodeSystem 1", initialCodeSystem.getName()); + initialCodeSystem.setName("Updated Parent Child CodeSystem 1"); + String encoded = myFhirCtx.newJsonParser().encodeResourceToString(initialCodeSystem); + HttpPut putRequest = new HttpPut(ourServerBase + "/CodeSystem/" + parentChildCs1Id); + putRequest.setEntity(new StringEntity(encoded, ContentType.parse("application/json+fhir"))); + CloseableHttpResponse resp = ourHttpClient.execute(putRequest); + try { + assertEquals(200, resp.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(resp); + } + + CodeSystem updatedCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCs1Id).execute(); + assertEquals("Updated Parent Child CodeSystem 1", updatedCodeSystem.getName()); + + initialCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCs2Id).execute(); + assertEquals("Parent Child CodeSystem 2", initialCodeSystem.getName()); + initialCodeSystem.setName("Updated Parent Child CodeSystem 2"); + encoded = myFhirCtx.newJsonParser().encodeResourceToString(initialCodeSystem); + putRequest = new HttpPut(ourServerBase + "/CodeSystem/" + parentChildCs2Id); + putRequest.setEntity(new StringEntity(encoded, ContentType.parse("application/json+fhir"))); + resp = ourHttpClient.execute(putRequest); + try { + assertEquals(200, resp.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(resp); + } + + updatedCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCs2Id).execute(); + assertEquals("Updated Parent Child CodeSystem 2", updatedCodeSystem.getName()); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index b3cca872d96..3d20a791145 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -43,6 +43,8 @@ import org.hl7.fhir.r4.model.ValueSet.FilterOperator; import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; @@ -245,6 +247,8 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { loadAndPersistCodeSystemAndValueSet(); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(1, page.getContent().size()); Parameters respParam = myClient .operation() @@ -297,6 +301,8 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { loadAndPersistCodeSystemAndValueSet(); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(1, page.getContent().size()); Parameters respParam = myClient .operation() @@ -356,6 +362,8 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { loadAndPersistCodeSystemAndValueSet(); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(1, page.getContent().size()); Parameters respParam = myClient .operation() @@ -1093,7 +1101,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setVersion("SYSTEM VERSION"); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setVersion(theCodeSystemVersion); IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java new file mode 100644 index 00000000000..3dab91a168e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java @@ -0,0 +1,1415 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetConcept; +import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.util.UrlUtil; +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; +import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Enumerations; +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; +import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r4.model.ValueSet.FilterOperator; +import org.hl7.fhir.r4.model.codesystems.HttpVerb; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.Optional; + +import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM; +import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProviderR4Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4ValueSetVersionedTest.class); + private IIdType myExtensionalCsId_v1; + private IIdType myExtensionalCsId_v2; + private IIdType myExtensionalVsId_v1; + private IIdType myExtensionalVsId_v2; + private IIdType myLocalValueSetId_v1; + private IIdType myLocalValueSetId_v2; + private Long myExtensionalVsIdOnResourceTable_v1; + private Long myExtensionalVsIdOnResourceTable_v2; + private ValueSet myLocalVs_v1; + private ValueSet myLocalVs_v2; + + private void loadAndPersistCodeSystemAndValueSet() throws IOException { + loadAndPersistCodeSystem(); + loadAndPersistValueSet(); + } + + private void loadAndPersistCodeSystemAndValueSetWithDesignations() throws IOException { + loadAndPersistCodeSystemWithDesignations(); + loadAndPersistValueSet(); + } + + private void loadAndPersistCodeSystem() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + persistCodeSystem(codeSystem); + } + + private void loadAndPersistCodeSystemWithDesignations() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-with-designations.xml"); + persistCodeSystem(codeSystem); + } + + private void persistCodeSystem(CodeSystem theCodeSystem) { + theCodeSystem.setId("CodeSystem/cs1"); + theCodeSystem.setVersion("1"); + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myExtensionalCsId_v1 = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myExtensionalCsId_v1, null).getId(); + + theCodeSystem.setId("CodeSystem/cs2"); + theCodeSystem.setVersion("2"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : theCodeSystem.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v2"); + } + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myExtensionalCsId_v2 = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myExtensionalCsId_v2, null).getId(); + + } + + private void loadAndPersistValueSet() throws IOException { + ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + persistTwoVersionsOfValueSet(valueSet, HttpVerb.POST); + } + + @SuppressWarnings("EnumSwitchStatementWhichMissesCases") + private void persistTwoVersionsOfValueSet(ValueSet theValueSet, HttpVerb theVerb) { + theValueSet.setVersion("1"); + theValueSet.setId("ValueSet/vs1"); + theValueSet.getCompose().getInclude().get(0).setVersion("1"); + myExtensionalVsId_v1 = persistSingleValueSet(theValueSet, theVerb); + myExtensionalVsIdOnResourceTable_v1 = myValueSetDao.readEntity(myExtensionalVsId_v1, null).getId(); + + theValueSet.setVersion("2"); + theValueSet.setId("ValueSet/vs2"); + theValueSet.getCompose().getInclude().get(0).setVersion("2"); + myExtensionalVsId_v2 = persistSingleValueSet(theValueSet, theVerb); + myExtensionalVsIdOnResourceTable_v2 = myValueSetDao.readEntity(myExtensionalVsId_v2, null).getId(); + + } + + private IIdType persistSingleValueSet(ValueSet theValueSet, HttpVerb theVerb) { + final IIdType[] vsId = new IIdType[1]; + switch (theVerb) { + case POST: + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + vsId[0] = myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + }); + break; + case PUT: + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + vsId[0] = myValueSetDao.update(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + }); + break; + default: + throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb); + } + return vsId[0]; + } + + private String createExternalCs(String theCodeSystemVersion) { + IFhirResourceDao codeSystemDao = myCodeSystemDao; + IResourceTableDao resourceTableDao = myResourceTableDao; + + return createExternalCs(codeSystemDao, resourceTableDao, myTermCodeSystemStorageSvc, mySrd, theCodeSystemVersion).getUrl(); + } + + private void createExternalCsAndLocalVs() { + String codeSystemUrl = createExternalCs("1"); + myLocalVs_v1 = createLocalVs(codeSystemUrl, "1"); + myLocalValueSetId_v1 = persistLocalVs(myLocalVs_v1); + + codeSystemUrl = createExternalCs("2"); + myLocalVs_v2 = createLocalVs(codeSystemUrl, "2"); + myLocalValueSetId_v2 = persistLocalVs(myLocalVs_v2); + + } + +// private void createExternalCsAndLocalVsWithUnknownCode() { +// String codeSystemUrl = createExternalCs(); + +// createLocalVsWithUnknownCode(codeSystemUrl); +// } + + private void createLocalCs() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") + .addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) + ) + .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); + codeSystem + .addConcept().setCode("B").setDisplay("Code B") + .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) + .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); + myCodeSystemDao.create(codeSystem, mySrd); + } + + private void createLocalVsWithIncludeConcept() { + myLocalVs_v1 = new ValueSet(); + myLocalVs_v1.setUrl(URL_MY_VALUE_SET); + myLocalVs_v1.setVersion("1"); + ConceptSetComponent include = myLocalVs_v1.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.setVersion("1"); + include.addConcept().setCode("A").setDisplay("A v1"); + include.addConcept().setCode("AA").setDisplay("AA v1"); + myLocalValueSetId_v1 = myValueSetDao.create(myLocalVs_v1, mySrd).getId().toUnqualifiedVersionless(); + + myLocalVs_v2 = new ValueSet(); + myLocalVs_v2.setUrl(URL_MY_VALUE_SET); + myLocalVs_v2.setVersion("2"); + include = myLocalVs_v2.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.setVersion("2"); + include.addConcept().setCode("A").setDisplay("A v2"); + include.addConcept().setCode("AA").setDisplay("AA v2"); + myLocalValueSetId_v2 = myValueSetDao.create(myLocalVs_v2, mySrd).getId().toUnqualifiedVersionless(); + + } + + private ValueSet createLocalVs(String theCodeSystemUrl, String theValueSetVersion) { + ValueSet myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + myLocalVs.setVersion(theValueSetVersion); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(theCodeSystemUrl); + include.setVersion(theValueSetVersion); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA"); + return myLocalVs; + + } + + private IIdType persistLocalVs(ValueSet theValueSet) { + return myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + + private void createLocalVsWithUnknownCode(String codeSystemUrl) { + myLocalVs_v1 = new ValueSet(); + myLocalVs_v1.setUrl(URL_MY_VALUE_SET); + myLocalVs_v1.setVersion("1"); + ConceptSetComponent include = myLocalVs_v1.getCompose().addInclude(); + include.setSystem(codeSystemUrl); + include.setSystem("1"); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); + myLocalValueSetId_v1 = myValueSetDao.create(myLocalVs_v1, mySrd).getId().toUnqualifiedVersionless(); + + myLocalVs_v2 = new ValueSet(); + myLocalVs_v2.setUrl(URL_MY_VALUE_SET); + myLocalVs_v2.setVersion("2"); + include = myLocalVs_v2.getCompose().addInclude(); + include.setSystem(codeSystemUrl); + include.setSystem("2"); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); + myLocalValueSetId_v2 = myValueSetDao.create(myLocalVs_v2, mySrd).getId().toUnqualifiedVersionless(); + + } + + @Test + public void testExpandById() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Test with v1 of ValueSet + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + // Test with v2 of ValueSet + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + } + + @Test + public void testExpandByIdWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Verify v1 ValueSet + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + // Verify v2 ValueSet + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + } + + @Test + public void testExpandByIdWithFilter() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Verify ValueSet v1 + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + // Verify ValueSet v2 + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + } + + @Test + public void testExpandByIdWithFilterWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Validate ValueSet v1 + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + // Validate ValueSet v2 + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + } + + @Test + public void testExpandByUrlAndVersion() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithBogusVersion() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + try { + myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("3")) + .execute(); + } catch (ResourceNotFoundException e) { + assertEquals(404, e.getStatusCode()); + assertEquals("HTTP 404 Not Found: Unknown ValueSet: http%3A%2F%2Fwww.healthintersections.com.au%2Ffhir%2FValueSet%2Fextensional-case-2%7C3", e.getMessage()); + } + } + + @Test + public void testExpandByUrlWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithPreExpansionAndBogusVersion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + try { + myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("3")) + .execute(); + } catch (ResourceNotFoundException e) { + assertEquals(404, e.getStatusCode()); + assertEquals("HTTP 404 Not Found: Unknown ValueSet: http%3A%2F%2Fwww.healthintersections.com.au%2Ffhir%2FValueSet%2Fextensional-case-2%7C3", e.getMessage()); + } + } + + @Test + public void testExpandByValueSet() throws IOException { + loadAndPersistCodeSystem(); + + // Test with no version specified + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last updated. + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 1 specified. + toExpand.setVersion("1"); + toExpand.setId("ValueSet/vs1"); + toExpand.getCompose().getInclude().get(0).setVersion("1"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v1. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 2 specified. + toExpand.setVersion("2"); + toExpand.setId("ValueSet/vs2"); + toExpand.getCompose().getInclude().get(0).setVersion("2"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByValueSetWithPreExpansion() throws IOException { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystem(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + // Test with no version specified + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last updated. + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 1 specified. + toExpand.setVersion("1"); + toExpand.setId("ValueSet/vs1"); + toExpand.getCompose().getInclude().get(0).setVersion("1"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v1. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 2 specified. + toExpand.setVersion("2"); + toExpand.setId("ValueSet/vs2"); + toExpand.getCompose().getInclude().get(0).setVersion("2"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + + } + + @Test + public void testExpandInlineVsAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalVs_v1); + assertNotNull(myLocalVs_v2); + + myLocalVs_v1.setId(""); + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs_v1) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + myLocalVs_v2.setId(""); + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs_v2) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId_v1); + assertNotNull(myLocalValueSetId_v2); + + // Validate ValueSet v1 + Parameters respParam = myClient + .operation() + .onInstance(myLocalValueSetId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + // Validate ValueSet v2 + respParam = myClient + .operation() + .onInstance(myLocalValueSetId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsCanonicalAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId_v1); + assertNotNull(myLocalValueSetId_v2); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType(URL_MY_VALUE_SET)) + .execute(); + + // Canonical expand should only return most recently updated version, v2. + assertEquals(1, respParam.getParameter().size()); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandValueSetBasedOnCodeSystemWithChangedUrlAndVersion() { + + CodeSystem cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo1"); + cs.setVersion("1"); + cs.addConcept().setCode("foo1").setDisplay("foo1"); + myClient.update().resource(cs).execute(); + + ValueSet vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo1").setVersion("1").addConcept().setCode("foo1"); + myClient.update().resource(vs).execute(); + + ValueSet expanded = myClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + + // Update the CodeSystem Version and Codes + cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo2"); + cs.setVersion("2"); + cs.addConcept().setCode("foo2").setDisplay("foo2"); + myClient.update().resource(cs).execute(); + + vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo2").setVersion("2").addConcept().setCode("foo2"); + myClient.update().resource(vs).execute(); + + expanded = myClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + } + + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(); + + CodeSystem codeSystem_v1 = myCodeSystemDao.read(myExtensionalCsId_v1); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v1)); + CodeSystem codeSystem_v2 = myCodeSystemDao.read(myExtensionalCsId_v2); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v2)); + + ValueSet valueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v1)); + ValueSet valueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v2)); + + String initialValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + String initialValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); + + ValueSet updatedValueSet_v1 = valueSet_v1; + updatedValueSet_v1.setName(valueSet_v1.getName().concat(" - MODIFIED")); + persistSingleValueSet(updatedValueSet_v1, HttpVerb.PUT); + updatedValueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v1)); + + String updatedValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v1,"1", myExtensionalVsIdOnResourceTable_v1); + + ValueSet updatedValueSet_v2 = valueSet_v2; + updatedValueSet_v2.setName(valueSet_v2.getName().concat(" - MODIFIED")); + persistSingleValueSet(updatedValueSet_v2, HttpVerb.PUT); + updatedValueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v2)); + + String updatedValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v2,"2", myExtensionalVsIdOnResourceTable_v2); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(updatedValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(updatedValueSetName_v2, codeSystem_v2); + } + + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(); + + CodeSystem codeSystem_v1 = myCodeSystemDao.read(myExtensionalCsId_v1); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v1)); + CodeSystem codeSystem_v2 = myCodeSystemDao.read(myExtensionalCsId_v2); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v2)); + + ValueSet valueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v1)); + ValueSet valueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v2)); + + String initialValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + String initialValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); + + ValueSet updatedValueSet_v1 = valueSet_v1; + updatedValueSet_v1.setName(valueSet_v1.getName().concat(" - MODIFIED")); + + String url = myClient.getServerBase().concat("/").concat(myExtensionalVsId_v1.getValueAsString()); + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(url) + .setResource(updatedValueSet_v1) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(url); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + myClient.transaction().withBundle(bundle).execute(); + + updatedValueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v1)); + + String updatedValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + + ValueSet updatedValueSet_v2 = valueSet_v2; + updatedValueSet_v2.setName(valueSet_v2.getName().concat(" - MODIFIED")); + + url = myClient.getServerBase().concat("/").concat(myExtensionalVsId_v2.getValueAsString()); + bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(url) + .setResource(updatedValueSet_v2) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(url); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + myClient.transaction().withBundle(bundle).execute(); + + updatedValueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v2)); + + String updatedValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(updatedValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(updatedValueSetName_v2, codeSystem_v2); + + } + + private void validateTermValueSetNotExpanded(String theValueSetName, String theVersion, Long theId) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(theId); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", theVersion); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + }); + } + + private void validateTermValueSetExpandedAndChildrenV1(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable_v1); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "1"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } + + private void validateTermValueSetExpandedAndChildrenV2(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable_v2); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "2"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration v2", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter v2", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum v2", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum v2", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } +/* + @Test + public void testValidateCodeOperationByCodeAndSystemInstance() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnType() throws IOException { + createLocalCs(); + createLocalVsWithIncludeConcept(); + + String url = ourServerBase + + "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + + UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + + "&code=AA"; + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { + createLocalCs(); + createLocalVsWithIncludeConcept(); + + String url = ourServerBase + + "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + + UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + + "&code=AA"; + + ourLog.info("* Requesting: {}", url); + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + + @Test + public void testValidateCodeOperationByCodeAndSystemType() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + + @Test + public void testValidateCodeOperationNoValueSetProvided() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + try { + myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.", e.getMessage()); + } + } + + @Test + public void testValidateCodeOperationOnInstanceWithIsAExpansion() throws IOException { + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://mycs"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + cs.setStatus(Enumerations.PublicationStatus.ACTIVE); + ConceptDefinitionComponent parentA = cs.addConcept().setCode("ParentA").setDisplay("Parent A"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); + myCodeSystemDao.create(cs); + + ValueSet vs = new ValueSet(); + vs.setUrl("http://myvs"); + vs.getCompose() + .addInclude() + .setSystem("http://mycs") + .addFilter() + .setOp(FilterOperator.ISA) + .setProperty("concept") + .setValue("ParentA"); + IIdType vsId = myValueSetDao.create(vs).getId().toUnqualifiedVersionless(); + + HttpGet expandGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$expand?_pretty=true"); + try (CloseableHttpResponse status = ourHttpClient.execute(expandGet)) { + String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", response); + } + + HttpGet validateCodeGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=ChildAA&_pretty=true"); + try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet)) { + String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", response); + Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); + assertEquals(true, output.getParameterBool("result")); + } + + HttpGet validateCodeGet2 = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=FOO&_pretty=true"); + try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet2)) { + String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", response); + Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); + assertEquals(false, output.getParameterBool("result")); + } + + } +*/ + @AfterEach + public void afterResetPreExpansionDefault() { + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); + } + + public CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA"); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA"); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB"); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB"); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + return codeSystem; + } + + public CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion(theCodeSystemVersion); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A" + theCodeSystemVersion); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA" + theCodeSystemVersion); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA" + theCodeSystemVersion); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB" + theCodeSystemVersion); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB" + theCodeSystemVersion); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B" + theCodeSystemVersion); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", theCodeSystemVersion, cs, table); + return codeSystem; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImplTest.java index b406a31dedd..0291e76e7dd 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TermDeferredStorageSvcImplTest.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.jpa.term; -import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; @@ -54,7 +53,6 @@ public class TermDeferredStorageSvcImplTest { TermDeferredStorageSvcImpl svc = new TermDeferredStorageSvcImpl(); svc.setTransactionManagerForUnitTest(myTxManager); svc.setCodeSystemStorageSvcForUnitTest(myTermConceptStorageSvc); - svc.setDaoConfigForUnitTest(new DaoConfig()); when(myTermCodeSystemVersionDao.findById(anyLong())).thenReturn(Optional.of(myTermCodeSystemVersion)); svc.setCodeSystemVersionDaoForUnitTest(myTermCodeSystemVersionDao); @@ -78,7 +76,6 @@ public class TermDeferredStorageSvcImplTest { TermDeferredStorageSvcImpl svc = new TermDeferredStorageSvcImpl(); svc.setTransactionManagerForUnitTest(myTxManager); svc.setCodeSystemStorageSvcForUnitTest(myTermConceptStorageSvc); - svc.setDaoConfigForUnitTest(new DaoConfig()); when(myTermCodeSystemVersionDao.findById(anyLong())).thenReturn(Optional.empty()); svc.setCodeSystemVersionDaoForUnitTest(myTermCodeSystemVersionDao); @@ -104,7 +101,6 @@ public class TermDeferredStorageSvcImplTest { TermDeferredStorageSvcImpl svc = new TermDeferredStorageSvcImpl(); svc.setTransactionManagerForUnitTest(myTxManager); svc.setCodeSystemStorageSvcForUnitTest(myTermConceptStorageSvc); - svc.setDaoConfigForUnitTest(new DaoConfig()); // Simulate the case where an exception is thrown despite a valid code system version. when(myTermCodeSystemVersionDao.findById(anyLong())).thenReturn(Optional.of(myTermCodeSystemVersion)); @@ -129,7 +125,6 @@ public class TermDeferredStorageSvcImplTest { svc.setTransactionManagerForUnitTest(myTxManager); svc.setCodeSystemStorageSvcForUnitTest(myTermConceptStorageSvc); svc.setConceptDaoForUnitTest(myConceptDao); - svc.setDaoConfigForUnitTest(new DaoConfig()); svc.setProcessDeferred(true); svc.addConceptLinkToStorageQueue(conceptLink); svc.saveDeferred(); 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 79f2c37836c..db5702d7819 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 @@ -1942,6 +1942,58 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { }); } + @Test + public void testUpdateCodeSystemUrlAndVersion() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(CS_URL); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A"); + codeSystem + .addConcept().setCode("B").setDisplay("Code A"); + + codeSystem.setVersion("1"); + + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + Set codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A"); + assertThat(toCodes(codes), containsInAnyOrder("A")); + + codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "B"); + assertThat(toCodes(codes), containsInAnyOrder("B")); + + runInTransaction(() -> { + List termCodeSystemVersions = myTermCodeSystemVersionDao.findAll(); + assertEquals(termCodeSystemVersions.size(), 1); + TermCodeSystemVersion termCodeSystemVersion_1 = termCodeSystemVersions.get(0); + assertEquals(termCodeSystemVersion_1.getConcepts().size(), 2); + Set termConcepts = new HashSet<>(termCodeSystemVersion_1.getConcepts()); + assertThat(toCodes(termConcepts), containsInAnyOrder("A", "B")); + + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id.getIdPartAsLong()); + assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); + + }); + + codeSystem.setVersion("2"); + codeSystem.setUrl(CS_URL_2); + + IIdType id_v2 = myCodeSystemDao.update(codeSystem, mySrd).getId().toUnqualified(); + + runInTransaction(() -> { + List termCodeSystemVersions_updated = myTermCodeSystemVersionDao.findAll(); + assertEquals(termCodeSystemVersions_updated.size(), 1); + TermCodeSystemVersion termCodeSystemVersion_2 = termCodeSystemVersions_updated.get(0); + assertEquals(termCodeSystemVersion_2.getConcepts().size(), 2); + Set termConcepts_updated = new HashSet<>(termCodeSystemVersion_2.getConcepts()); + assertThat(toCodes(termConcepts_updated), containsInAnyOrder("A", "B")); + + TermCodeSystem termCodeSystem = myTermCodeSystemDao.findByResourcePid(id_v2.getIdPartAsLong()); + assertEquals("2", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); + assertEquals(CS_URL_2, termCodeSystem.getCodeSystemUri()); + }); + } + @Test public void testUpdateCodeSystemContentModeNotPresent() { CodeSystem codeSystem = new CodeSystem(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java index 84f0f663b9c..e0d35c85fc1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java @@ -195,7 +195,7 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { include.setSystem(CS_URL); myTermSvc.expandValueSet(null, vs, myValueSetCodeAccumulator); - verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection()); + verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), anyString(), nullable(String.class), anyCollection()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index 40850b23eaf..c99339b1243 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -904,7 +904,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { include.setSystem(CS_URL); myTermSvc.expandValueSet(null, vs, myValueSetCodeAccumulator); - verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection()); + verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), anyString(), nullable(String.class), anyCollection()); } @Test 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 9418769cf84..e770e370586 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 @@ -141,6 +141,11 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { //EMPI Target Type empiLink.addColumn("20200727.1","TARGET_TYPE").nullable().type(ColumnTypeEnum.STRING, EmpiLink.TARGET_TYPE_LENGTH); + + //Term CodeSystem Version + Builder.BuilderWithTableName trmCodeSystemVer = version.onTable("TRM_CODESYSTEM_VER"); + trmCodeSystemVer.addIndex("20200916.1", "IDX_CODESYSTEM_AND_VER").unique(true).withColumns("CODESYSTEM_PID", "CS_VERSION_ID"); + } protected void init510_20200725() { From 4216cee8c6409ec1e6779ab739379830497f7d19 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 22 Sep 2020 14:44:32 -0400 Subject: [PATCH 40/71] Added and enhanced tests plus fixes arising from tests. --- .../context/support/IValidationSupport.java | 18 - .../ca/uhn/fhir/i18n/hapi-messages.properties | 2 +- ...aseJpaResourceProviderCodeSystemDstu3.java | 14 +- .../BaseJpaResourceProviderValueSetDstu3.java | 2 +- .../BaseJpaResourceProviderCodeSystemR4.java | 14 +- .../BaseJpaResourceProviderCodeSystemR5.java | 14 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 30 +- .../FhirResourceDaoValueSetDstu2Test.java | 26 + ...ResourceDaoR4ValueSetMultiVersionTest.java | 24 + .../dao/r5/FhirResourceDaoR5ValueSetTest.java | 282 ++++ .../ResourceProviderDstu3CodeSystemTest.java | 2 - ...eProviderDstu3CodeSystemVersionedTest.java | 761 +++++++++ ...rceProviderDstu3ValueSetVersionedTest.java | 1420 +++++++++++++++++ ...urceProviderR4CodeSystemVersionedTest.java | 4 +- .../r4/ResourceProviderR4ValueSetTest.java | 34 - ...sourceProviderR4ValueSetVersionedTest.java | 16 +- 16 files changed, 2578 insertions(+), 85 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index a1efd061a7c..047ed241729 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -183,24 +183,6 @@ public interface IValidationSupport { return null; } - /** - * Validates that the given code exists and if possible returns a display - * name. This method is called to check codes which are found in "example" - * binding fields (e.g. Observation.code in the default profile. - * - * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to - * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. - * @param theOptions Provides options controlling the validation - * @param theCodeSystem The code system, e.g. "http://loinc.org" - * @param theCode The code, e.g. "1234-5" - * @param theDisplay The display name, if it should also be validated - * @param theSystemVersion The code system version. - * @return Returns a validation result object - */ - default CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, String theSystemVersion) { - return null; - } - /** * Validates that the given code exists and if possible returns a display * name. This method is called to check codes which are found in "example" 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 34ff24940b6..578c0e51dc1 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 @@ -145,7 +145,7 @@ ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrlAndVe 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.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 CodeSystem resources with CodeSystem.url "{0}" and CodeSystem.version "{1}", already have one with resource ID: {2} +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} ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotUpdateUrlOrVersionForValueSetResource=Cannot update URL or version for ValueSet resource. Existing ValueSet resource with resource ID {0} found with ValueSet.url "{1}" and ValueSet.version "{2}" ca.uhn.fhir.jpa.patch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 6b5c7cd7c77..49a1a2b9fd8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -61,7 +61,12 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + IValidationSupport.LookupCodeResult result; + if (theVersion != null) { + result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + } else { + result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + } result.throwNotFoundIfAppropriate(); return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } catch (FHIRException e) { @@ -92,7 +97,12 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); + IFhirResourceDaoCodeSystem.SubsumesResult result; + if (theVersion != null || (theCodingA != null && theCodingA.hasVersion()) || (theCodingB != null &&theCodingB.hasVersion())) { + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); + } else { + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + } return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java index 3ce3a72c1ec..2bfb61dc0ef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java @@ -46,7 +46,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst // Note: url is correct and identifier is not, but identifier was only added as // of 3.1.0 so we'll leave url for now. See: https://groups.google.com/d/msgid/hapi-fhir/CAN2Cfy8kW%2BAOkgC6VjPsU3gRCpExCNZBmJdi-k5R_TWeyWH4tA%40mail.gmail.com?utm_medium=email&utm_source=footer @OperationParam(name = "url", min = 0, max = 1) UriType theUrl, - @OperationParam(name = "valueSetVersion", min = 0, max = 1) org.hl7.fhir.r4.model.StringType theValueSetVersion, + @OperationParam(name = "valueSetVersion", min = 0, max = 1) StringType theValueSetVersion, @OperationParam(name = "identifier", min = 0, max = 1) UriType theIdentifier, @OperationParam(name = "filter", min = 0, max = 1) StringType theFilter, @OperationParam(name = "offset", min = 0, max = 1) IntegerType theOffset, diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 9b78c83991b..43320798757 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 @@ -63,7 +63,12 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + IValidationSupport.LookupCodeResult result; + if (theVersion != null) { + result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + } else { + result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + } result.throwNotFoundIfAppropriate(); return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } finally { @@ -92,7 +97,12 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); + IFhirResourceDaoCodeSystem.SubsumesResult result; + if (theVersion != null || (theCodingA != null && theCodingA.hasVersion()) || (theCodingB != null &&theCodingB.hasVersion())) { + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); + } else { + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + } return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 47c880a6a10..3dac6bf36ba 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -63,7 +63,12 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoCodeSystem) getDao(); - IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + IValidationSupport.LookupCodeResult result; + if (theVersion != null) { + result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + } else { + result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); + } result.throwNotFoundIfAppropriate(); return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties); } finally { @@ -92,7 +97,12 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoCodeSystem) getDao(); - IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); + IFhirResourceDaoCodeSystem.SubsumesResult result; + if (theVersion != null || (theCodingA != null && theCodingA.hasVersion()) || (theCodingB != null &&theCodingB.hasVersion())) { + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); + } else { + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + } return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 30b642d4121..5f02e7c4a7e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -250,7 +250,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { String code = theConcept.getCode(); String display = theConcept.getDisplay(); Collection designations = theConcept.getDesignations(); - addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, codeSystemVersion, code, display); + if (StringUtils.isNotEmpty(codeSystemVersion)) { + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, codeSystemVersion, code, display); + } else { + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, code, display); + } } private void addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, Collection theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCodeSystemVersion, String theCode, String theDisplay) { @@ -771,7 +775,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { // This will happen if we're expanding against a built-in (part of FHIR) ValueSet that // isn't actually in the database anywhere Collection emptyCollection = Collections.emptyList(); - addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, emptyCollection, theAdd, theCodeCounter, nextConcept.getSystem(), nextConcept.getCode(), nextConcept.getDisplay()); + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, emptyCollection, theAdd, theCodeCounter, nextConcept.getSystem(), nextConcept.getSystemVersion(), nextConcept.getCode(), nextConcept.getDisplay()); } } if (isNoneBlank(nextConcept.getSystem(), nextConcept.getCode()) && !theAdd && theAddedCodes.remove(nextConcept.getSystem() + "|" + nextConcept.getCode())) { @@ -1251,7 +1255,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { if (theInclude.getConcept().isEmpty()) { for (TermConcept next : theVersion.getConcepts()) { - addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, null, theAdd, theCodeCounter, theSystem, next.getCode(), next.getDisplay()); + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, null, theAdd, theCodeCounter, theSystem, theVersion.getCodeSystemVersionId(), next.getCode(), next.getDisplay()); } } @@ -1259,7 +1263,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { if (!theSystem.equals(theInclude.getSystem()) && isNotBlank(theSystem)) { continue; } - addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, null, theAdd, theCodeCounter, theSystem, next.getCode(), next.getDisplay()); + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, null, theAdd, theCodeCounter, theSystem, theVersion.getCodeSystemVersionId(), next.getCode(), next.getDisplay()); } @@ -1862,6 +1866,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override + @Transactional public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null, null, null); @@ -2242,7 +2247,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem) { + public CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem) { IValidationSupport validationSupport = provideValidationSupport(); IBaseResource codeSystem = validationSupport.fetchCodeSystem(theSystem); if (codeSystem != null) { @@ -2463,21 +2468,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return new VersionIndependentConcept(system, code, null, systemVersion); } -/* private static class VersionIndependentConceptWithSystemVersion extends VersionIndependentConcept { - - String myCodeSystemVersion; - - public VersionIndependentConceptWithSystemVersion(String theSystem, String theCode, String theSystemVersion) { - super(theSystem, theCode); - myCodeSystemVersion = theSystemVersion; - } - - public String getCodeSystemVersion() { - return myCodeSystemVersion; - } - - } -*/ /** * This method is present only for unit tests, do not call from client code */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java index 4878070ca41..32d705e43aa 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java @@ -1,6 +1,8 @@ package ca.uhn.fhir.jpa.dao.dstu2; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; +import ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.resource.ValueSet; @@ -8,6 +10,7 @@ import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.UriDt; +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,6 +25,8 @@ import static org.hamcrest.Matchers.stringContainsInOrder; 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 static org.junit.jupiter.api.Assertions.fail; + public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { @@ -228,5 +233,26 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { assertThat(resp, not(containsString(""))); } + @Test + public void unsupportedMethodsThrowException() { + IFhirResourceDaoCodeSystem testFhirResourceDaoValueSet = new FhirResourceDaoValueSetDstu2(); + // Multi-version lookupCode method + try { + testFhirResourceDaoValueSet.lookupCode(null, null, null, null, null); + fail(); + } catch (UnsupportedOperationException theE) { + // Success + } + + // Multi-version subsumes method + try { + testFhirResourceDaoValueSet.subsumes(null, null, null, null, null, null, null); + fail(); + } catch (UnsupportedOperationException theE) { + // Success + } + + + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java index 14faf7eef6e..ea11d79f7ac 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetMultiVersionTest.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.data.ITermValueSetConceptDao; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.Test; @@ -218,5 +219,28 @@ public class FhirResourceDaoR4ValueSetMultiVersionTest extends BaseJpaR4Test { } + @Test + public void testExpandWithNoResultsInLocalValueSet() { + Set valueSetConcepts = new HashSet<>(); + valueSetConcepts.add("hello"); + valueSetConcepts.add("goodbye"); + + createLocalCsAndVs("1", valueSetConcepts); + + ValueSet vs = new ValueSet(); + ValueSet.ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM + "AA"); + include.setVersion("1"); + include.addConcept().setCode("A"); + + try { + myValueSetDao.expand(vs, null); + fail(); + } catch (PreconditionFailedException e) { + assertEquals("Unknown CodeSystem URI \"http://example.com/my_code_systemAA\" referenced from ValueSet", e.getMessage()); + } + } + + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java new file mode 100644 index 00000000000..2172ae6d808 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java @@ -0,0 +1,282 @@ +package ca.uhn.fhir.jpa.dao.r5; + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; +import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.hl7.fhir.r5.model.ValueSet; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.stringContainsInOrder; +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 static org.junit.jupiter.api.Assertions.fail; + +public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR5ValueSetTest.class); + + @Autowired + protected ITermDeferredStorageSvc myTerminologyDeferredStorageSvc; + + private IIdType myExtensionalVsId; + + @AfterEach + public void after() { + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); + myDaoConfig.setMaximumExpansionSize(new DaoConfig().getMaximumExpansionSize()); + } + + + @BeforeEach + @Transactional + public void before02() throws IOException { + ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless(); + + CodeSystem upload2 = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + myCodeSystemDao.create(upload2, mySrd).getId().toUnqualifiedVersionless(); + + } + + @Test + public void testValidateCodeOperationNoValueSet() { + UriType valueSetIdentifier = null; + IdType id = null; + CodeType code = new CodeType("8450-9-XXX"); + UriType system = new UriType("http://acme.org"); + StringType display = null; + Coding coding = null; + CodeableConcept codeableConcept = null; + try { + myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + fail(); + } catch (InvalidRequestException e) { + assertEquals("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.", e.getMessage()); + } + } + + @Test + public void testValidateCodeOperationByIdentifierAndCodeAndSystem() { + UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); + IdType id = null; + CodeType code = new CodeType("11378-7"); + UriType system = new UriType("http://acme.org"); + StringType display = null; + Coding coding = null; + CodeableConcept codeableConcept = null; + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + } + + @Test + public void testValidateCodeOperationByIdentifierAndCodeAndSystemAndBadDisplay() { + UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); + IdType id = null; + CodeType code = new CodeType("11378-7"); + UriType system = new UriType("http://acme.org"); + StringType display = new StringType("Systolic blood pressure at First encounterXXXX"); + Coding coding = null; + CodeableConcept codeableConcept = null; + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertFalse(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\"", result.getMessage()); + } + + @Test + public void testValidateCodeOperationByIdentifierAndCodeAndSystemAndGoodDisplay() { + UriType valueSetIdentifier = new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); + IdType id = null; + CodeType code = new CodeType("11378-7"); + UriType system = new UriType("http://acme.org"); + StringType display = new StringType("Systolic blood pressure at First encounter"); + Coding coding = null; + CodeableConcept codeableConcept = null; + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + } + + @Test + public void testValidateCodeOperationByResourceIdAndCodeableConcept() { + UriType valueSetIdentifier = null; + IIdType id = myExtensionalVsId; + CodeType code = null; + UriType system = null; + StringType display = null; + Coding coding = null; + CodeableConcept codeableConcept = new CodeableConcept(); + codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7"); + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + } + + @Test + public void testValidateCodeOperationByResourceIdAndCodeableConceptWithExistingValueSetAndPreExpansionEnabled() { + myDaoConfig.setPreExpandValueSets(true); + + UriType valueSetIdentifier = null; + IIdType id = myExtensionalVsId; + CodeType code = null; + UriType system = null; + StringType display = null; + Coding coding = null; + CodeableConcept codeableConcept = new CodeableConcept(); + codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7"); + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + + myTerminologyDeferredStorageSvc.saveDeferred(); + result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + } + + @Test + public void testValidateCodeOperationByResourceIdAndCodeAndSystem() { + UriType valueSetIdentifier = null; + IIdType id = myExtensionalVsId; + CodeType code = new CodeType("11378-7"); + UriType system = new UriType("http://acme.org"); + StringType display = null; + Coding coding = null; + CodeableConcept codeableConcept = null; + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + } + + @Test + public void testValidateCodeOperationByResourceIdAndCodeAndSystemWithExistingValueSetAndPreExpansionEnabled() { + myDaoConfig.setPreExpandValueSets(true); + + UriType valueSetIdentifier = null; + IIdType id = myExtensionalVsId; + CodeType code = new CodeType("11378-7"); + UriType system = new UriType("http://acme.org"); + StringType display = null; + Coding coding = null; + CodeableConcept codeableConcept = null; + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + + myTerminologyDeferredStorageSvc.saveDeferred(); + result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); + assertTrue(result.isOk()); + assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); + } + + @Test + public void testExpandById() { + String resp; + + ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null, mySrd); + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + /* + * Filter with display name + */ + + expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd); + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + //@formatter:off + assertThat(resp, stringContainsInOrder( + "", + "")); + //@formatter:on + + } + + @Test + public void testExpandByValueSet_ExceedsMaxSize() { + // Add a bunch of codes + CustomTerminologySet codesToAdd = new CustomTerminologySet(); + for (int i = 0; i < 100; i++) { + codesToAdd.addRootConcept("CODE" + i, "Display " + i); + } + myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd); + myDaoConfig.setMaximumExpansionSize(50); + + ValueSet vs = new ValueSet(); + vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult"); + vs.getCompose().addInclude().setSystem("http://loinc.org"); + myValueSetDao.create(vs); + + try { + myValueSetDao.expand(vs, null); + fail(); + } catch (InternalErrorException e) { + assertEquals("Expansion of ValueSet produced too many codes (maximum 50) - Operation aborted!", e.getMessage()); + } + } + + + @Test + public void testValidateCodeAgainstBuiltInValueSetAndCodeSystemWithValidCode() { + IPrimitiveType display = null; + Coding coding = null; + CodeableConcept codeableConcept = null; + StringType vsIdentifier = new StringType("http://hl7.org/fhir/ValueSet/administrative-gender"); + StringType code = new StringType("male"); + StringType system = new StringType("http://hl7.org/fhir/administrative-gender"); + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(vsIdentifier, null, code, system, display, coding, codeableConcept, mySrd); + + ourLog.info(result.getMessage()); + assertTrue(result.isOk(), result.getMessage()); + assertEquals("Male", result.getDisplay()); + } + + +} + + diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index 132bdd8fe15..ee2f14e3c5b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -6,11 +6,9 @@ import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.transaction.annotation.Transactional; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java new file mode 100644 index 00000000000..c75bfec66ba --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java @@ -0,0 +1,761 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test; +import ca.uhn.fhir.jpa.provider.dstu3.ResourceProviderDstu3ValueSetTest; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.CodeSystem; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.Enumerations; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; +//import org.hl7.fhir.dstu3.model.codesystems.ConceptSubsumptionOutcome; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourceProviderDstu3Test { + + private static final String SYSTEM_PARENTCHILD = "http://parentchild"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3CodeSystemVersionedTest.class); + private long parentChildCs1Id; + private long parentChildCs2Id; + + @BeforeEach + @Transactional + public void before02() throws IOException { + CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + cs.setVersion("1"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : cs.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v1"); + } + myCodeSystemDao.create(cs, mySrd); + + cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + cs.setVersion("2"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : cs.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v2"); + } + myCodeSystemDao.create(cs, mySrd); + + CodeSystem parentChildCs = new CodeSystem(); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setVersion("1"); + parentChildCs.setName("Parent Child CodeSystem 1"); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + CodeSystem.ConceptDefinitionComponent parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); + parentA.addConcept().setCode("ParentC").setDisplay("Parent C"); + parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B"); + + DaoMethodOutcome parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCs1Id = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); + + parentChildCs = new CodeSystem(); + parentChildCs.setVersion("2"); + parentChildCs.setName("Parent Child CodeSystem 2"); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A v2"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA v2"); + parentA.addConcept().setCode("ParentB").setDisplay("Parent B v2"); + parentChildCs.addConcept().setCode("ParentC").setDisplay("Parent C v2"); + + parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCs2Id = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); + + } + + @Test + public void testLookupOnExternalCodeMultiVersion() { + ResourceProviderDstu3ValueSetVersionedTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "1"); + ResourceProviderDstu3ValueSetVersionedTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "2"); + + // First test with no version specified (should return from last version created) + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // With HTTP GET + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .useHttpGet() + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 1 specified. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A1", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // With HTTP GET + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("1")) + .useHttpGet() + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("1"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A1", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 2 specified. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("SYSTEM NAME", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // With HTTP GET + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .andParameter("version", new StringType("2")) + .useHttpGet() + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals(("2"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Parent A2", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedCode() { + // First test with no version specified (should return from last version created) + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 1 specified. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v1"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 2 specified + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedNonExistentVersion() { + try { + ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("3")) + .execute(); + fail(); + } catch (ResourceNotFoundException e) { + ourLog.info("Lookup failed as expected"); + } + } + + @Test + public void testLookupOperationByCoding() { + // First test with no version specified (should return from last version created) + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version set to 1 + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v1"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version set to 2 + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodes_Subsumes() { + // First test with no version specified (should return result for last version created). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentB")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumes", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentC")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumes", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentB")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumes", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodes_Subsumedby() { + // First test with no version specified (should return result for last version created). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentB")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumed-by", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentC")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumed-by", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentB")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumed-by", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + } + + @Test + public void testSubsumesOnCodes_Disjoint() { + // First test with no version specified (should return result for last version created). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentC")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("not-subsumed", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1 + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentB")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("not-subsumed", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2 + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentA")) + .andParameter("codeB", new CodeType("ParentC")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("not-subsumed", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodings_MismatchedCsVersions() { + try { + ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Unable to test subsumption across different code system versions", e.getMessage()); + } + } + + + @Test + public void testSubsumesOnCodings_Subsumes() { + // First test with no version specified (should return result for last version created). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumes", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumes", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumes", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + + @Test + public void testSubsumesOnCodings_Subsumedby() { + // First test with no version specified (should return result for last version created). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumed-by", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumed-by", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("subsumed-by", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testSubsumesOnCodings_Disjoint() { + // First test with no version specified (should return result for last version created). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("not-subsumed", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1 + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("1")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentB").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("not-subsumed", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2 + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA").setVersion("2")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentC").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals("not-subsumed", ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + + @Test + public void testUpdateCodeSystemName() throws IOException { + + CodeSystem initialCodeSystem = ourClient.read().resource(CodeSystem.class).withId(parentChildCs1Id).execute(); + assertEquals("Parent Child CodeSystem 1", initialCodeSystem.getName()); + initialCodeSystem.setName("Updated Parent Child CodeSystem 1"); + String encoded = myFhirCtx.newJsonParser().encodeResourceToString(initialCodeSystem); + HttpPut putRequest = new HttpPut(ourServerBase + "/CodeSystem/" + parentChildCs1Id); + putRequest.setEntity(new StringEntity(encoded, ContentType.parse("application/json+fhir"))); + CloseableHttpResponse resp = ourHttpClient.execute(putRequest); + try { + assertEquals(200, resp.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(resp); + } + + CodeSystem updatedCodeSystem = ourClient.read().resource(CodeSystem.class).withId(parentChildCs1Id).execute(); + assertEquals("Updated Parent Child CodeSystem 1", updatedCodeSystem.getName()); + + initialCodeSystem = ourClient.read().resource(CodeSystem.class).withId(parentChildCs2Id).execute(); + assertEquals("Parent Child CodeSystem 2", initialCodeSystem.getName()); + initialCodeSystem.setName("Updated Parent Child CodeSystem 2"); + encoded = myFhirCtx.newJsonParser().encodeResourceToString(initialCodeSystem); + putRequest = new HttpPut(ourServerBase + "/CodeSystem/" + parentChildCs2Id); + putRequest.setEntity(new StringEntity(encoded, ContentType.parse("application/json+fhir"))); + resp = ourHttpClient.execute(putRequest); + try { + assertEquals(200, resp.getStatusLine().getStatusCode()); + } finally { + IOUtils.closeQuietly(resp); + } + + updatedCodeSystem = ourClient.read().resource(CodeSystem.class).withId(parentChildCs2Id).execute(); + assertEquals("Updated Parent Child CodeSystem 2", updatedCodeSystem.getName()); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java new file mode 100644 index 00000000000..416699c7201 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java @@ -0,0 +1,1420 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetConcept; +import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CodeSystem; +import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; +import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; +import org.hl7.fhir.dstu3.model.ValueSet; +import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; +import org.hl7.fhir.dstu3.model.codesystems.HttpVerb; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.Optional; + +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM; +import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_VALUE_SET; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProviderDstu3Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3ValueSetVersionedTest.class); + private IIdType myExtensionalCsId_v1; + private IIdType myExtensionalCsId_v2; + private IIdType myExtensionalVsId_v1; + private IIdType myExtensionalVsId_v2; + private IIdType myLocalValueSetId_v1; + private IIdType myLocalValueSetId_v2; + private Long myExtensionalVsIdOnResourceTable_v1; + private Long myExtensionalVsIdOnResourceTable_v2; + private ValueSet myLocalVs_v1; + private ValueSet myLocalVs_v2; + + private void loadAndPersistCodeSystemAndValueSet() throws IOException { + loadAndPersistCodeSystem(); + loadAndPersistValueSet(); + } + + private void loadAndPersistCodeSystemAndValueSetWithDesignations() throws IOException { + loadAndPersistCodeSystemWithDesignations(); + loadAndPersistValueSet(); + } + + private void loadAndPersistCodeSystem() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + persistCodeSystem(codeSystem); + } + + private void loadAndPersistCodeSystemWithDesignations() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-with-designations.xml"); + persistCodeSystem(codeSystem); + } + + private void persistCodeSystem(CodeSystem theCodeSystem) { + theCodeSystem.setId("CodeSystem/cs1"); + theCodeSystem.setVersion("1"); + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myExtensionalCsId_v1 = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myExtensionalCsId_v1, null).getId(); + + theCodeSystem.setId("CodeSystem/cs2"); + theCodeSystem.setVersion("2"); + for(ConceptDefinitionComponent conceptDefinitionComponent : theCodeSystem.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v2"); + } + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myExtensionalCsId_v2 = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myExtensionalCsId_v2, null).getId(); + + } + + private void loadAndPersistValueSet() throws IOException { + ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + persistTwoVersionsOfValueSet(valueSet, HttpVerb.POST); + } + + @SuppressWarnings("EnumSwitchStatementWhichMissesCases") + private void persistTwoVersionsOfValueSet(ValueSet theValueSet, HttpVerb theVerb) { + theValueSet.setVersion("1"); + theValueSet.setId("ValueSet/vs1"); + theValueSet.getCompose().getInclude().get(0).setVersion("1"); + myExtensionalVsId_v1 = persistSingleValueSet(theValueSet, theVerb); + myExtensionalVsIdOnResourceTable_v1 = myValueSetDao.readEntity(myExtensionalVsId_v1, null).getId(); + + theValueSet.setVersion("2"); + theValueSet.setId("ValueSet/vs2"); + theValueSet.getCompose().getInclude().get(0).setVersion("2"); + myExtensionalVsId_v2 = persistSingleValueSet(theValueSet, theVerb); + myExtensionalVsIdOnResourceTable_v2 = myValueSetDao.readEntity(myExtensionalVsId_v2, null).getId(); + + } + + private IIdType persistSingleValueSet(ValueSet theValueSet, HttpVerb theVerb) { + final IIdType[] vsId = new IIdType[1]; + switch (theVerb) { + case POST: + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + vsId[0] = myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + }); + break; + case PUT: + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + vsId[0] = myValueSetDao.update(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + }); + break; + default: + throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb); + } + return vsId[0]; + } + + private String createExternalCs(String theCodeSystemVersion) { + IFhirResourceDao codeSystemDao = myCodeSystemDao; + IResourceTableDao resourceTableDao = myResourceTableDao; + + return createExternalCs(codeSystemDao, resourceTableDao, myTermCodeSystemStorageSvc, mySrd, theCodeSystemVersion).getUrl(); + } + + private void createExternalCsAndLocalVs() { + String codeSystemUrl = createExternalCs("1"); + myLocalVs_v1 = createLocalVs(codeSystemUrl, "1"); + myLocalValueSetId_v1 = persistLocalVs(myLocalVs_v1); + + codeSystemUrl = createExternalCs("2"); + myLocalVs_v2 = createLocalVs(codeSystemUrl, "2"); + myLocalValueSetId_v2 = persistLocalVs(myLocalVs_v2); + + } + +// private void createExternalCsAndLocalVsWithUnknownCode() { +// String codeSystemUrl = createExternalCs(); + +// createLocalVsWithUnknownCode(codeSystemUrl); +// } + + private void createLocalCs() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") + .addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) + ) + .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); + codeSystem + .addConcept().setCode("B").setDisplay("Code B") + .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) + .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); + myCodeSystemDao.create(codeSystem, mySrd); + } + + private void createLocalVsWithIncludeConcept() { + myLocalVs_v1 = new ValueSet(); + myLocalVs_v1.setUrl(URL_MY_VALUE_SET); + myLocalVs_v1.setVersion("1"); + ConceptSetComponent include = myLocalVs_v1.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.setVersion("1"); + include.addConcept().setCode("A").setDisplay("A v1"); + include.addConcept().setCode("AA").setDisplay("AA v1"); + myLocalValueSetId_v1 = myValueSetDao.create(myLocalVs_v1, mySrd).getId().toUnqualifiedVersionless(); + + myLocalVs_v2 = new ValueSet(); + myLocalVs_v2.setUrl(URL_MY_VALUE_SET); + myLocalVs_v2.setVersion("2"); + include = myLocalVs_v2.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.setVersion("2"); + include.addConcept().setCode("A").setDisplay("A v2"); + include.addConcept().setCode("AA").setDisplay("AA v2"); + myLocalValueSetId_v2 = myValueSetDao.create(myLocalVs_v2, mySrd).getId().toUnqualifiedVersionless(); + + } + + private ValueSet createLocalVs(String theCodeSystemUrl, String theValueSetVersion) { + ValueSet myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + myLocalVs.setVersion(theValueSetVersion); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(theCodeSystemUrl); + include.setVersion(theValueSetVersion); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA"); + return myLocalVs; + + } + + private IIdType persistLocalVs(ValueSet theValueSet) { + return myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + + private void createLocalVsWithUnknownCode(String codeSystemUrl) { + myLocalVs_v1 = new ValueSet(); + myLocalVs_v1.setUrl(URL_MY_VALUE_SET); + myLocalVs_v1.setVersion("1"); + ConceptSetComponent include = myLocalVs_v1.getCompose().addInclude(); + include.setSystem(codeSystemUrl); + include.setSystem("1"); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); + myLocalValueSetId_v1 = myValueSetDao.create(myLocalVs_v1, mySrd).getId().toUnqualifiedVersionless(); + + myLocalVs_v2 = new ValueSet(); + myLocalVs_v2.setUrl(URL_MY_VALUE_SET); + myLocalVs_v2.setVersion("2"); + include = myLocalVs_v2.getCompose().addInclude(); + include.setSystem(codeSystemUrl); + include.setSystem("2"); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); + myLocalValueSetId_v2 = myValueSetDao.create(myLocalVs_v2, mySrd).getId().toUnqualifiedVersionless(); + + } + + @Test + public void testExpandById() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Test with v1 of ValueSet + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + // Test with v2 of ValueSet + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + } + + @Test + public void testExpandByIdWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Verify v1 ValueSet + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + // Verify v2 ValueSet + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + } + + @Test + public void testExpandByIdWithFilter() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Verify ValueSet v1 + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + // Verify ValueSet v2 + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + } + + @Test + public void testExpandByIdWithFilterWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Validate ValueSet v1 + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + // Validate ValueSet v2 + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + } + + @Test + public void testExpandByUrlAndVersion() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithBogusVersion() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("3")) + .execute(); + } catch (ResourceNotFoundException e) { + assertEquals(404, e.getStatusCode()); + assertEquals("HTTP 404 Not Found: Unknown ValueSet: http%3A%2F%2Fwww.healthintersections.com.au%2Ffhir%2FValueSet%2Fextensional-case-2%7C3", e.getMessage()); + } + } + + @Test + public void testExpandByUrlWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithPreExpansionAndBogusVersion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("3")) + .execute(); + } catch (ResourceNotFoundException e) { + assertEquals(404, e.getStatusCode()); + assertEquals("HTTP 404 Not Found: Unknown ValueSet: http%3A%2F%2Fwww.healthintersections.com.au%2Ffhir%2FValueSet%2Fextensional-case-2%7C3", e.getMessage()); + } + } + + @Test + public void testExpandByValueSet() throws IOException { + loadAndPersistCodeSystem(); + + // Test with no version specified + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last updated. + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 1 specified. + toExpand.setVersion("1"); + toExpand.setId("ValueSet/vs1"); + toExpand.getCompose().getInclude().get(0).setVersion("1"); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v1. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 2 specified. + toExpand.setVersion("2"); + toExpand.setId("ValueSet/vs2"); + toExpand.getCompose().getInclude().get(0).setVersion("2"); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByValueSetWithPreExpansion() throws IOException { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystem(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + // Test with no version specified + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last updated. + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 1 specified. + toExpand.setVersion("1"); + toExpand.setId("ValueSet/vs1"); + toExpand.getCompose().getInclude().get(0).setVersion("1"); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v1. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 2 specified. + toExpand.setVersion("2"); + toExpand.setId("ValueSet/vs2"); + toExpand.getCompose().getInclude().get(0).setVersion("2"); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + + } + + @Test + public void testExpandInlineVsAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalVs_v1); + assertNotNull(myLocalVs_v2); + + myLocalVs_v1.setId(""); + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs_v1) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + myLocalVs_v2.setId(""); + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs_v2) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId_v1); + assertNotNull(myLocalValueSetId_v2); + + // Validate ValueSet v1 + Parameters respParam = ourClient + .operation() + .onInstance(myLocalValueSetId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + // Validate ValueSet v2 + respParam = ourClient + .operation() + .onInstance(myLocalValueSetId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsCanonicalAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId_v1); + assertNotNull(myLocalValueSetId_v2); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType(URL_MY_VALUE_SET)) + .execute(); + + // Canonical expand should only return most recently updated version, v2. + assertEquals(1, respParam.getParameter().size()); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandValueSetBasedOnCodeSystemWithChangedUrlAndVersion() { + + CodeSystem cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo1"); + cs.setVersion("1"); + cs.addConcept().setCode("foo1").setDisplay("foo1"); + ourClient.update().resource(cs).execute(); + + ValueSet vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo1").setVersion("1").addConcept().setCode("foo1"); + ourClient.update().resource(vs).execute(); + + ValueSet expanded = ourClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + + // Update the CodeSystem Version and Codes + cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo2"); + cs.setVersion("2"); + cs.addConcept().setCode("foo2").setDisplay("foo2"); + ourClient.update().resource(cs).execute(); + + vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo2").setVersion("2").addConcept().setCode("foo2"); + ourClient.update().resource(vs).execute(); + + expanded = ourClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + } + + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(); + + CodeSystem codeSystem_v1 = myCodeSystemDao.read(myExtensionalCsId_v1); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v1)); + CodeSystem codeSystem_v2 = myCodeSystemDao.read(myExtensionalCsId_v2); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v2)); + + ValueSet valueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v1)); + ValueSet valueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v2)); + + String initialValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + String initialValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); + + ValueSet updatedValueSet_v1 = valueSet_v1; + updatedValueSet_v1.setName(valueSet_v1.getName().concat(" - MODIFIED")); + persistSingleValueSet(updatedValueSet_v1, HttpVerb.PUT); + updatedValueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v1)); + + String updatedValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v1,"1", myExtensionalVsIdOnResourceTable_v1); + + ValueSet updatedValueSet_v2 = valueSet_v2; + updatedValueSet_v2.setName(valueSet_v2.getName().concat(" - MODIFIED")); + persistSingleValueSet(updatedValueSet_v2, HttpVerb.PUT); + updatedValueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v2)); + + String updatedValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v2,"2", myExtensionalVsIdOnResourceTable_v2); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(updatedValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(updatedValueSetName_v2, codeSystem_v2); + } + + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(); + + CodeSystem codeSystem_v1 = myCodeSystemDao.read(myExtensionalCsId_v1); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v1)); + CodeSystem codeSystem_v2 = myCodeSystemDao.read(myExtensionalCsId_v2); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v2)); + + ValueSet valueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v1)); + ValueSet valueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v2)); + + String initialValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + String initialValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); + + ValueSet updatedValueSet_v1 = valueSet_v1; + updatedValueSet_v1.setName(valueSet_v1.getName().concat(" - MODIFIED")); + + String url = ourClient.getServerBase().concat("/").concat(myExtensionalVsId_v1.getValueAsString()); + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(url) + .setResource(updatedValueSet_v1) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(url); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + ourClient.transaction().withBundle(bundle).execute(); + + updatedValueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v1)); + + String updatedValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + + ValueSet updatedValueSet_v2 = valueSet_v2; + updatedValueSet_v2.setName(valueSet_v2.getName().concat(" - MODIFIED")); + + url = ourClient.getServerBase().concat("/").concat(myExtensionalVsId_v2.getValueAsString()); + bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(url) + .setResource(updatedValueSet_v2) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(url); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + ourClient.transaction().withBundle(bundle).execute(); + + updatedValueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v2)); + + String updatedValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(updatedValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(updatedValueSetName_v2, codeSystem_v2); + + } + + private void validateTermValueSetNotExpanded(String theValueSetName, String theVersion, Long theId) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(theId); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", theVersion); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + }); + } + + private void validateTermValueSetExpandedAndChildrenV1(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable_v1); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "1"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } + + private void validateTermValueSetExpandedAndChildrenV2(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable_v2); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "2"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration v2", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter v2", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum v2", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum v2", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } +/* + @Test + public void testValidateCodeOperationByCodeAndSystemInstance() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnType() throws IOException { + createLocalCs(); + createLocalVsWithIncludeConcept(); + + String url = ourServerBase + + "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + + UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + + "&code=AA"; + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + + @Test + public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { + createLocalCs(); + createLocalVsWithIncludeConcept(); + + String url = ourServerBase + + "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + + UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + + "&code=AA"; + + ourLog.info("* Requesting: {}", url); + + HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/fhir+json"); + try (CloseableHttpResponse response = ourHttpClient.execute(request)) { + String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); + ourLog.info(respString); + + Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + } + + @Test + public void testValidateCodeOperationByCodeAndSystemType() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } + + @Test + public void testValidateCodeOperationNoValueSetProvided() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.", e.getMessage()); + } + } + + @Test + public void testValidateCodeOperationOnInstanceWithIsAExpansion() throws IOException { + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://mycs"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + cs.setStatus(Enumerations.PublicationStatus.ACTIVE); + ConceptDefinitionComponent parentA = cs.addConcept().setCode("ParentA").setDisplay("Parent A"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); + myCodeSystemDao.create(cs); + + ValueSet vs = new ValueSet(); + vs.setUrl("http://myvs"); + vs.getCompose() + .addInclude() + .setSystem("http://mycs") + .addFilter() + .setOp(FilterOperator.ISA) + .setProperty("concept") + .setValue("ParentA"); + IIdType vsId = myValueSetDao.create(vs).getId().toUnqualifiedVersionless(); + + HttpGet expandGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$expand?_pretty=true"); + try (CloseableHttpResponse status = ourHttpClient.execute(expandGet)) { + String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", response); + } + + HttpGet validateCodeGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=ChildAA&_pretty=true"); + try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet)) { + String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", response); + Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); + assertEquals(true, output.getParameterBool("result")); + } + + HttpGet validateCodeGet2 = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=FOO&_pretty=true"); + try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet2)) { + String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", response); + Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); + assertEquals(false, output.getParameterBool("result")); + } + + } +*/ + + @Test + public void testCreateDuplicatValueSetVersion() { + createExternalCsAndLocalVs(); + try { + persistLocalVs(createLocalVs(URL_MY_CODE_SYSTEM, "1")); + fail(); + } catch (UnprocessableEntityException theE) { + assertThat(theE.getMessage(), containsString("Can not create multiple ValueSet resources with ValueSet.url \"" + URL_MY_VALUE_SET + "\" and ValueSet.version \"1\", already have one with resource ID: ")); + } + + } + + @AfterEach + public void afterResetPreExpansionDefault() { + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); + } + + public CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA"); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA"); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB"); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB"); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + return codeSystem; + } + + public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion(theCodeSystemVersion); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A" + theCodeSystemVersion); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA" + theCodeSystemVersion); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA" + theCodeSystemVersion); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB" + theCodeSystemVersion); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB" + theCodeSystemVersion); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B" + theCodeSystemVersion); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", theCodeSystemVersion, cs, table); + return codeSystem; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java index 485d02df39b..f5e28967d36 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java @@ -89,8 +89,8 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi @Test public void testLookupOnExternalCodeMultiVersion() { - ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "1"); - ResourceProviderR4ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "2"); + ResourceProviderR4ValueSetVersionedTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "1"); + ResourceProviderR4ValueSetVersionedTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermCodeSystemStorageSvc, mySrd, "2"); // First test with no version specified (should return from last version created) Parameters respParam = myClient diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 3d20a791145..358cd5eb2eb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -1098,38 +1098,4 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { return codeSystem; } - public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl(URL_MY_CODE_SYSTEM); - codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - codeSystem.setVersion(theCodeSystemVersion); - IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); - - ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); - - TermCodeSystemVersion cs = new TermCodeSystemVersion(); - cs.setResource(table); - - TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A" + theCodeSystemVersion); - cs.getConcepts().add(parentA); - - TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA" + theCodeSystemVersion); - parentA.addChild(childAA, RelationshipTypeEnum.ISA); - - TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA" + theCodeSystemVersion); - childAA.addChild(childAAA, RelationshipTypeEnum.ISA); - - TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB" + theCodeSystemVersion); - childAA.addChild(childAAB, RelationshipTypeEnum.ISA); - - TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB" + theCodeSystemVersion); - parentA.addChild(childAB, RelationshipTypeEnum.ISA); - - TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B" + theCodeSystemVersion); - cs.getConcepts().add(parentB); - - theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", theCodeSystemVersion, cs, table); - return codeSystem; - } - } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java index 3dab91a168e..a57291d387e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.Charsets; @@ -1339,6 +1340,19 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide } */ + + @Test + public void testCreateDuplicatValueSetVersion() { + createExternalCsAndLocalVs(); + try { + persistLocalVs(createLocalVs(URL_MY_CODE_SYSTEM, "1")); + fail(); + } catch (UnprocessableEntityException theE) { + assertThat(theE.getMessage(), containsString("Can not create multiple ValueSet resources with ValueSet.url \"" + URL_MY_VALUE_SET + "\" and ValueSet.version \"1\", already have one with resource ID: ")); + } + + } + @AfterEach public void afterResetPreExpansionDefault() { myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); @@ -1378,7 +1392,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide return codeSystem; } - public CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { + public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setVersion(theCodeSystemVersion); From b32c4f01a835386024c0254a549acc78640d4703 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 22 Sep 2020 18:46:16 -0400 Subject: [PATCH 41/71] Fixed lookupCode implementation in InMemoryTerminologyServerValidationSupport. --- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 7 +++- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 7 +++- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 7 +++- ...oryTerminologyServerValidationSupport.java | 39 +++++++++++++++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index c42f4125ce1..31873e04da2 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 @@ -121,7 +121,12 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao nextCodeList = new ArrayList<>(); String system = nextInclude.getSystem(); - String systemVersion = nextInclude.getVersion(); if (isNotBlank(system)) { if (theWantSystem != null && !theWantSystem.equals(system)) { @@ -499,7 +530,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu if (theWantCode != null) { if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, system)) { - LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode, systemVersion); + LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode); if (lookup != null && lookup.isFound()) { CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() .addConcept() From 673274a966580af4f02ad902514bc912bd973816 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:18:18 -0400 Subject: [PATCH 42/71] Added invalid test cases for CodeSystem/$validate-code R3 --- .../ResourceProviderDstu3CodeSystemTest.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index 132bdd8fe15..8dc57145104 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -6,11 +6,9 @@ import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.transaction.annotation.Transactional; @@ -332,6 +330,19 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst return ourCtx.newJsonParser().parseResource(theType, loadResource(theFilename)); } + @Test + public void testValidateCodeOperation() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType("https://url")); + inParams.addParameter().setName("code").setValue(new CodeType("1")); + try { + ourClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Invalid request: The FHIR endpoint on this server does not know how to handle POST operation[CodeSystem/$validate-code] with parameters [[]]", e.getMessage()); + } + } } From 20661c7a2b906e67b17585fa9a2d5b72086520bf Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:19:43 -0400 Subject: [PATCH 43/71] Removed unused import --- .../uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 29200610dae..eccfdac0ffc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -52,8 +52,6 @@ import java.util.Date; import java.util.List; import java.util.Set; -import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull; -import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem; From b6d86dc5f2ef3f819e5c7c7b09b0cde5629260ac Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:39:20 -0400 Subject: [PATCH 44/71] Added fail(); in the invalid test cases --- .../r4/ResourceProviderR4CodeSystemValidationTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java index e92450d6dd2..c4d705303fc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.provider.r4; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; @@ -131,6 +132,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.",e.getMessage()); } @@ -160,6 +162,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate.",e.getMessage()); } @@ -175,6 +178,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: $validate-code can only validate (code) OR (coding) OR (codeableConcept)",e.getMessage()); } @@ -189,6 +193,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); } @@ -237,6 +242,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); } @@ -367,6 +373,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv try { myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); + fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage()); } From e6a669444d4278b98fde5c30a9ece182eba19af0 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Tue, 22 Sep 2020 20:51:20 -0400 Subject: [PATCH 45/71] Added invalid test cases for DSTU3 --- .../CommonCodeSystemsTerminologyService.java | 11 +---------- .../CommonCodeSystemsTerminologyServiceTest.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index cfea4d2ac89..41170364e85 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -335,14 +335,6 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { public static String getCodeSystemUrl(@Nonnull IBaseResource theCodeSystem) { String url; switch (theCodeSystem.getStructureFhirVersionEnum()) { - case DSTU2_HL7ORG: { - url = ((CodeSystem) theCodeSystem).getUrl(); - break; - } - case DSTU3: { - url = ((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem).getUrl(); - break; - } case R4: { url = ((org.hl7.fhir.r4.model.CodeSystem) theCodeSystem).getUrl(); break; @@ -351,8 +343,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { url = ((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem).getUrl(); break; } - case DSTU2: - case DSTU2_1: + case DSTU3: default: throw new IllegalArgumentException("Can not handle version: " + theCodeSystem.getStructureFhirVersionEnum()); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java index 1be573dd025..b3cb3984ee9 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java @@ -5,6 +5,8 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; + import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ValueSet; @@ -13,6 +15,9 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; + +import javax.annotation.Nonnull; public class CommonCodeSystemsTerminologyServiceTest { @@ -114,4 +119,14 @@ public class CommonCodeSystemsTerminologyServiceTest { assertEquals(null, cs); } + @Test + public void testFetchCodeSystemUrlDstu3() { + try { + CommonCodeSystemsTerminologyService.getCodeSystemUrl(new org.hl7.fhir.dstu3.model.CodeSystem()); + + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Can not handle version: DSTU3", e.getMessage()); + } + } } From 3d3d013d72e6f4d7ca6665897c79641157486b2a Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Wed, 23 Sep 2020 09:19:06 -0400 Subject: [PATCH 46/71] Changed import of IdType to r5. --- .../jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 1c13173d589..122149c6f8e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -29,7 +29,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeType; From ceb5ec63847c2bee832027ea93edd902a5356270 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 23 Sep 2020 16:42:29 -0400 Subject: [PATCH 47/71] Use canonical identifiers for code systems rather than specifying version separately. --- .../context/support/IValidationSupport.java | 13 -- .../api/dao/IFhirResourceDaoCodeSystem.java | 5 - .../jpa/api/dao/IFhirResourceDaoValueSet.java | 4 - .../jpa/dao/FhirResourceDaoValueSetDstu2.java | 20 -- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 27 +-- .../dstu3/FhirResourceDaoValueSetDstu3.java | 52 ++--- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 27 +-- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 49 ++--- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 29 +-- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 50 ++--- ...aseJpaResourceProviderCodeSystemDstu3.java | 15 +- .../BaseJpaResourceProviderValueSetDstu3.java | 4 +- .../BaseJpaResourceProviderCodeSystemR4.java | 18 +- .../r4/BaseJpaResourceProviderValueSetR4.java | 4 +- .../BaseJpaResourceProviderCodeSystemR5.java | 15 +- .../r5/BaseJpaResourceProviderValueSetR5.java | 4 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 182 +++++++++++------- .../jpa/term/IValueSetConceptAccumulator.java | 4 +- .../uhn/fhir/jpa/term/TermReadSvcDstu3.java | 7 +- .../ca/uhn/fhir/jpa/term/TermReadSvcR4.java | 7 +- .../jpa/term/ValueSetConceptAccumulator.java | 12 +- ...ansionComponentWithConceptAccumulator.java | 63 +++--- .../uhn/fhir/jpa/term/api/ITermReadSvc.java | 4 - .../FhirResourceDaoValueSetDstu2Test.java | 22 --- .../FhirResourceDaoDstu3TerminologyTest.java | 4 +- .../r4/ResourceProviderR4ValueSetTest.java | 17 ++ ...sourceProviderR4ValueSetVersionedTest.java | 33 +++- .../jpa/term/TerminologySvcImplDstu3Test.java | 3 +- .../ValueSetExpansionR4ElasticsearchIT.java | 2 +- .../jpa/term/ValueSetExpansionR4Test.java | 2 +- .../support/BaseValidationSupportWrapper.java | 5 - .../support/CachingValidationSupport.java | 6 - .../CommonCodeSystemsTerminologyService.java | 6 - ...oryTerminologyServerValidationSupport.java | 38 ---- .../support/ValidationSupportChain.java | 10 - .../FhirInstanceValidatorR4Test.java | 2 +- 36 files changed, 300 insertions(+), 465 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index 047ed241729..49739e62b1e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -212,19 +212,6 @@ public interface IValidationSupport { return null; } - /** - * Look up a code using the system, system version and code value - * - * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to - * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. - * @param theSystem The CodeSystem URL - * @param theCode The code - * @param theSystemVersion The CodeSystem version - */ - default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theSystemVersion) { - return null; - } - /** * Returns true if the given valueset can be validated by the given * validation support module diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index 2c00c792b49..ba957e00542 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -42,11 +42,6 @@ public interface IFhirResourceDaoCodeSystem ext SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, RequestDetails theRequestDetails); - @Nonnull - IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CD theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails); - - SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails); - class SubsumesResult { private final ConceptSubsumptionOutcome myOutcome; diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoValueSet.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoValueSet.java index def9ad33fc5..a044e2e262b 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoValueSet.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoValueSet.java @@ -38,12 +38,8 @@ public interface IFhirResourceDaoValueSet exten T expandByIdentifier(String theUri, String theFilter); - T expandByIdentifier(String theUri, String theValueSetVersion, String theFilter); - T expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount); - T expandByIdentifier(String theUri, String theValueSetVersion, String theFilter, int theOffset, int theCount); - void purgeCaches(); IValidationSupport.CodeValidationResult validateCode(IPrimitiveType theValueSetIdentifier, IIdType theId, IPrimitiveType theCode, IPrimitiveType theSystem, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails); 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 6069ebcd335..77525d07b26 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 @@ -174,21 +174,11 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) { if (theSystem != null && theSystem.startsWith("http://hl7.org/fhir/")) { @@ -244,11 +234,6 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, CodingDt theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - throw new UnsupportedOperationException(); - } - @Override public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, CodingDt theCoding, RequestDetails theRequest) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); @@ -294,11 +279,6 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CodingDt theCodingA, CodingDt theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - throw new UnsupportedOperationException(); - } - @Override @PostConstruct public void postConstruct() { 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 31873e04da2..3df67dd6bea 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 @@ -85,12 +85,6 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { - return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); - } - - @Nonnull - @Override - public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -104,29 +98,19 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null; - String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null; - return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion); - } - @Override public SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 0ebb0a93764..637d19630b6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -113,7 +113,21 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { - return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); - } - - @Nonnull - @Override - public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -99,30 +93,20 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null; - String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null; - return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion); - } - @Override public SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java index aa0a17ec883..fd6d02f5c56 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java @@ -95,7 +95,20 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao throw new InvalidRequestException("URI must not be blank or missing"); } - return doExpand(createSourceValueSet(theUri, null, theFilter)); + ValueSet source = new ValueSet(); + source.setUrl(theUri); + + source.getCompose().addInclude().addValueSet(theUri); + + if (isNotBlank(theFilter)) { + ConceptSetComponent include = source.getCompose().addInclude(); + ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + filter.setProperty("display"); + filter.setOp(FilterOperator.EQUAL); + filter.setValue(theFilter); + } + + return doExpand(source); // if (defaultValueSet != null) { // source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet)); @@ -111,26 +124,15 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao } @Override - public ValueSet expandByIdentifier(String theUri, String theValueSetVersion, String theFilter) { + public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { if (isBlank(theUri)) { throw new InvalidRequestException("URI must not be blank or missing"); } - return doExpand(createSourceValueSet(theUri, theValueSetVersion, theFilter)); - } - - private ValueSet createSourceValueSet(String theUri, String theValueSetVersion, String theFilter) { ValueSet source = new ValueSet(); source.setUrl(theUri); - if (theValueSetVersion != null) { - source.setVersion(theValueSetVersion); - } - if (theValueSetVersion != null) { - source.getCompose().addInclude().addValueSet(theUri + "|" +theValueSetVersion); - } else { - source.getCompose().addInclude().addValueSet(theUri); - } + source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { ValueSet.ConceptSetComponent include = source.getCompose().addInclude(); @@ -139,25 +141,8 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao filter.setOp(ValueSet.FilterOperator.EQUAL); filter.setValue(theFilter); } - return source; - } - @Override - public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { - if (isBlank(theUri)) { - throw new InvalidRequestException("URI must not be blank or missing"); - } - - return doExpand(createSourceValueSet(theUri,null,theFilter), theOffset, theCount); - } - - @Override - public ValueSet expandByIdentifier(String theUri, String theValueSetVersion, String theFilter, int theOffset, int theCount) { - if (isBlank(theUri)) { - throw new InvalidRequestException("URI must not be blank or missing"); - } - - return doExpand(createSourceValueSet(theUri,theValueSetVersion,theFilter), theOffset, theCount); + return doExpand(source, theOffset, theCount); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index f6ebf2cbb66..d18ab4ac9bf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -82,12 +82,6 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCode, IPrimitiveType theSystem, Coding theCoding, RequestDetails theRequestDetails) { - return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); - } - - @Nonnull - @Override - public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType theCode, IPrimitiveType theSystem, Coding theCoding, IPrimitiveType theVersion, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; @@ -101,30 +95,20 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, IPrimitiveType theVersion, RequestDetails theRequestDetails) { - String codingBVersion = theCodingB != null ? theCodingB.getVersion() : null; - String codingAVersion = theCodingA != null ? theCodingA.getVersion() : null; - return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, codingAVersion, codingBVersion); - } - @Override public SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 7f6002d31ad..6036e99ef2b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -95,7 +95,20 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao throw new InvalidRequestException("URI must not be blank or missing"); } - return doExpand(createSourceValueSet(theUri, null, theFilter)); + ValueSet source = new ValueSet(); + source.setUrl(theUri); + + source.getCompose().addInclude().addValueSet(theUri); + + if (isNotBlank(theFilter)) { + ConceptSetComponent include = source.getCompose().addInclude(); + ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + filter.setProperty("display"); + filter.setOp(Enumerations.FilterOperator.EQUAL); + filter.setValue(theFilter); + } + + return doExpand(source); // if (defaultValueSet != null) { // source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet)); @@ -111,27 +124,15 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao } @Override - public ValueSet expandByIdentifier(String theUri, String theValueSetUri, String theFilter) { + public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { if (isBlank(theUri)) { throw new InvalidRequestException("URI must not be blank or missing"); } - return doExpand(createSourceValueSet(theUri, theValueSetUri, theFilter)); - - } - - private ValueSet createSourceValueSet(String theUri, String theValueSetVersion, String theFilter) { ValueSet source = new ValueSet(); source.setUrl(theUri); - if (theValueSetVersion != null) { - source.setVersion(theValueSetVersion); - } - if (theValueSetVersion != null) { - source.getCompose().addInclude().addValueSet(theUri + "|" +theValueSetVersion); - } else { - source.getCompose().addInclude().addValueSet(theUri); - } + source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { ValueSet.ConceptSetComponent include = source.getCompose().addInclude(); @@ -140,25 +141,8 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao filter.setOp(Enumerations.FilterOperator.EQUAL); filter.setValue(theFilter); } - return source; - } - @Override - public ValueSet expandByIdentifier(String theUri, String theFilter, int theOffset, int theCount) { - if (isBlank(theUri)) { - throw new InvalidRequestException("URI must not be blank or missing"); - } - - return doExpand(createSourceValueSet(theUri, null, theFilter), theOffset, theCount); - } - - @Override - public ValueSet expandByIdentifier(String theUri, String theValueSetVersion, String theFilter, int theOffset, int theCount) { - if (isBlank(theUri)) { - throw new InvalidRequestException("URI must not be blank or missing"); - } - - return doExpand(createSourceValueSet(theUri, theValueSetVersion, theFilter), theOffset, theCount); + return doExpand(source, theOffset, theCount); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 49a1a2b9fd8..9c54199e262 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -63,7 +63,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); IValidationSupport.LookupCodeResult result; if (theVersion != null) { - result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theRequestDetails); } else { result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); } @@ -98,11 +98,16 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); IFhirResourceDaoCodeSystem.SubsumesResult result; - if (theVersion != null || (theCodingA != null && theCodingA.hasVersion()) || (theCodingB != null &&theCodingB.hasVersion())) { - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); - } else { - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + if (theVersion != null) { + theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); } + if (theCodingA != null && theCodingA.hasVersion()) { + theCodingA.setSystem(theCodingA.getSystemElement().asStringValue() + "|" + theCodingA.getVersion()); + } + if (theCodingB != null && theCodingB.hasVersion()) { + theCodingB.setSystem(theCodingB.getSystemElement().asStringValue() + "|" + theCodingB.getVersion()); + } + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java index 2bfb61dc0ef..cc865b70c7d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java @@ -102,7 +102,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst return dao.expand(theId, toFilterString(theFilter), offset, count, theRequestDetails); } else if (haveIdentifier) { if (haveValueSetVersion) { - return dao.expandByIdentifier(url.getValue(), theValueSetVersion.getValue(), toFilterString(theFilter), offset, count); + return dao.expandByIdentifier(url.getValue() + "|" + theValueSetVersion.getValue(), toFilterString(theFilter), offset, count); } else { return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter), offset, count); } @@ -114,7 +114,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst return dao.expand(theId, toFilterString(theFilter), theRequestDetails); } else if (haveIdentifier) { if (haveValueSetVersion) { - return dao.expandByIdentifier(url.getValue(), theValueSetVersion.getValue(), toFilterString(theFilter)); + return dao.expandByIdentifier(url.getValue() + "|" + theValueSetVersion.getValue(), toFilterString(theFilter)); } else { return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter)); } 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 43320798757..a54a645ec59 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 @@ -65,7 +65,10 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); IValidationSupport.LookupCodeResult result; if (theVersion != null) { - result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theRequestDetails); + } else if (theCoding != null && theCoding.hasVersion()) { + Coding codingWithVersion = new Coding(theCoding.getSystem() + "|" + theCoding.getVersion(), theCoding.getCode(), theCoding.getDisplay()); + result = dao.lookupCode(theCode, theSystem, codingWithVersion, theRequestDetails); } else { result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); } @@ -98,11 +101,16 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); IFhirResourceDaoCodeSystem.SubsumesResult result; - if (theVersion != null || (theCodingA != null && theCodingA.hasVersion()) || (theCodingB != null &&theCodingB.hasVersion())) { - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); - } else { - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + if (theVersion != null) { + theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); } + if (theCodingA != null && theCodingA.hasVersion()) { + theCodingA.setSystem(theCodingA.getSystemElement().asStringValue() + "|" + theCodingA.getVersion()); + } + if (theCodingB != null && theCodingB.hasVersion()) { + theCodingB.setSystem(theCodingB.getSystemElement().asStringValue() + "|" + theCodingB.getVersion()); + } + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java index 6c32c0def2f..b8b6637fcf7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java @@ -94,7 +94,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoCodeSystem) getDao(); IValidationSupport.LookupCodeResult result; if (theVersion != null) { - result = dao.lookupCode(theCode, theSystem, theCoding, theVersion, theRequestDetails); + result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theRequestDetails); } else { result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); } @@ -98,11 +98,16 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoCodeSystem) getDao(); IFhirResourceDaoCodeSystem.SubsumesResult result; - if (theVersion != null || (theCodingA != null && theCodingA.hasVersion()) || (theCodingB != null &&theCodingB.hasVersion())) { - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theVersion, theRequestDetails); - } else { - result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); + if (theVersion != null) { + theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); } + if (theCodingA != null && theCodingA.hasVersion()) { + theCodingA.setSystem(theCodingA.getSystemElement().asStringValue() + "|" + theCodingA.getVersion()); + } + if (theCodingB != null && theCodingB.hasVersion()) { + theCodingB.setSystem(theCodingB.getSystemElement().asStringValue() + "|" + theCodingB.getVersion()); + } + result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java index b5d2e93f8f4..79fb1f7d922 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java @@ -94,7 +94,7 @@ public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5 designations = theConcept.getDesignations(); if (StringUtils.isNotEmpty(codeSystemVersion)) { - addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, codeSystemVersion, code, display); + addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem + "|" + codeSystemVersion, code, display); } else { addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, code, display); } } private void addCodeIfNotAlreadyAdded(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, Collection theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCodeSystemVersion, String theCode, String theDisplay) { - if (isNoneBlank(theCodeSystem, theCode)) { - if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode + "|" + theCodeSystemVersion)) { - theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCodeSystemVersion, theCode, theDisplay, theDesignations); + if (StringUtils.isNotEmpty(theCodeSystemVersion)) { + if (isNoneBlank(theCodeSystem, theCode)) { + if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode + "|" + theCodeSystemVersion)) { + theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem + "|" + theCodeSystemVersion, theCode, theDisplay, theDesignations); + theCodeCounter.incrementAndGet(); + } + + if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode + "|" + theCodeSystemVersion)) { + theValueSetCodeAccumulator.excludeConcept(theCodeSystem + "|" + theCodeSystemVersion, theCode); + theCodeCounter.decrementAndGet(); + } + } + } else { + if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode)) { + theValueSetCodeAccumulator.includeConceptWithDesignations(theCodeSystem, theCode, theDisplay, theDesignations); theCodeCounter.incrementAndGet(); } - if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode + "|" + theCodeSystemVersion)) { - theValueSetCodeAccumulator.excludeConcept(theCodeSystem, theCodeSystemVersion, theCode); + if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode)) { + theValueSetCodeAccumulator.excludeConcept(theCodeSystem, theCode); theCodeCounter.decrementAndGet(); } } @@ -829,7 +841,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Filters */ - handleFilters(bool, theSystem, codeSystemVersion, qb, theIncludeOrExclude); + String canonicalSystem; + if (codeSystemVersion != null) { + canonicalSystem = theSystem + "|" + codeSystemVersion; + } else { + canonicalSystem = theSystem; + } + handleFilters(bool, canonicalSystem, qb, theIncludeOrExclude); Query luceneQuery = bool.createQuery(); @@ -938,15 +956,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void handleFilters(BooleanJunction theBool, String theSystem, String theSystemVersion, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { + private void handleFilters(BooleanJunction theBool, String theCanonicalSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { if (theIncludeOrExclude.getFilter().size() > 0) { for (ValueSet.ConceptSetFilterComponent nextFilter : theIncludeOrExclude.getFilter()) { - handleFilter(theSystem, theSystemVersion, theQb, theBool, nextFilter); + handleFilter(theCanonicalSystem, theQb, theBool, nextFilter); } } } - private void handleFilter(String theSystem, String theSystemVersion, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { if (isBlank(theFilter.getValue()) && theFilter.getOp() == null && isBlank(theFilter.getProperty())) { return; } @@ -962,7 +980,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "concept": case "code": - handleFilterConceptAndCode(theSystem, theSystemVersion, theQb, theBool, theFilter); + handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter); break; case "parent": case "child": @@ -971,11 +989,11 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "ancestor": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestor(theSystem, theSystemVersion, theBool, theFilter); + handleFilterLoincAncestor(theSystem, theBool, theFilter); break; case "descendant": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincDescendant(theSystem, theSystemVersion, theBool, theFilter); + handleFilterLoincDescendant(theSystem, theBool, theFilter); break; case "copyright": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); @@ -988,8 +1006,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } private void isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) { - if (!isCodeSystemLoinc(theSystem)) { - throw new InvalidRequestException("Invalid filter, property " + theProperty + " is LOINC-specific and cannot be used with system: " + theSystem); + String systemUrl = getSystemFromCanonicalUrl(theSystem); + if (!isCodeSystemLoinc(systemUrl)) { + throw new InvalidRequestException("Invalid filter, property " + theProperty + " is LOINC-specific and cannot be used with system: " + systemUrl); } } @@ -1025,8 +1044,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { bool.must(textQuery); } - private void handleFilterConceptAndCode(String theSystem, String theSystemVersion, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - TermConcept code = findCode(theSystem, theFilter.getValue(), theSystemVersion) + private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + TermConcept code = findCode(theSystem, theFilter.getValue()) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theFilter.getValue())); if (theFilter.getOp() == ValueSet.FilterOperator.ISA) { @@ -1071,41 +1090,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincAncestor(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincAncestor(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterAncestorEqual(theSystem, theSystemVersion, theBool, theFilter); + addLoincFilterAncestorEqual(theSystem, theBool, theFilter); break; case IN: - addLoincFilterAncestorIn(theSystem, theSystemVersion, theBool, theFilter); + addLoincFilterAncestorIn(theSystem, theBool, theFilter); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterAncestorEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - addLoincFilterAncestorEqual(theSystem, theSystemVersion, theBool, theFilter.getProperty(), theFilter.getValue()); + private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterAncestorEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); } - private void addLoincFilterAncestorEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, String theProperty, String theValue) { - List terms = getAncestorTerms(theSystem, theSystemVersion, theProperty, theValue); + private void addLoincFilterAncestorEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getAncestorTerms(theSystem, theProperty, theValue); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterAncestorIn(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterAncestorIn(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getAncestorTerms(theSystem, theSystemVersion, theFilter.getProperty(), value)); + terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value)); } theBool.must(new TermsQuery(terms)); } - private List getAncestorTerms(String theSystem, String theSystemVersion, String theProperty, String theValue) { + private List getAncestorTerms(String theSystem, String theProperty, String theValue) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue, theSystemVersion) + TermConcept code = findCode(theSystem, theValue) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); retVal.add(new Term("myParentPids", "" + code.getId())); @@ -1115,41 +1134,41 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void handleFilterLoincDescendant(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilterLoincDescendant(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { switch (theFilter.getOp()) { case EQUAL: - addLoincFilterDescendantEqual(theSystem, theSystemVersion, theBool, theFilter); + addLoincFilterDescendantEqual(theSystem, theBool, theFilter); break; case IN: - addLoincFilterDescendantIn(theSystem, theSystemVersion, theBool, theFilter); + addLoincFilterDescendantIn(theSystem, theBool, theFilter); break; default: throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterDescendantEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - addLoincFilterDescendantEqual(theSystem, theSystemVersion, theBool, theFilter.getProperty(), theFilter.getValue()); + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); } - private void addLoincFilterDescendantEqual(String theSystem, String theSystemVersion, BooleanJunction theBool, String theProperty, String theValue) { - List terms = getDescendantTerms(theSystem, theSystemVersion, theProperty, theValue); + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getDescendantTerms(theSystem, theProperty, theValue); theBool.must(new TermsQuery(terms)); } - private void addLoincFilterDescendantIn(String theSystem, String theSystemVersion, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterDescendantIn(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { - terms.addAll(getDescendantTerms(theSystem, theSystemVersion, theFilter.getProperty(), value)); + terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value)); } theBool.must(new TermsQuery(terms)); } - private List getDescendantTerms(String theSystem, String theSystemVersion, String theProperty, String theValue) { + private List getDescendantTerms(String theSystem, String theProperty, String theValue) { List retVal = new ArrayList<>(); - TermConcept code = findCode(theSystem, theValue, theSystemVersion) + TermConcept code = findCode(theSystem, theValue) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + Constants.codeSystemWithDefaultDescription(theSystem) + "}" + theValue)); String[] parentPids = code.getParentPidsAsString().split(" "); @@ -1398,11 +1417,6 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Override public Optional findCode(String theCodeSystem, String theCode) { - return findCode(theCodeSystem, theCode, null); - } - - @Override - public Optional findCode(String theCodeSystem, String theCode, String theVersion) { /* * Loading concepts without a transaction causes issues later on some * platforms (e.g. PSQL) so this transactiontemplate is here to make @@ -1410,8 +1424,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { */ TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_MANDATORY); + return txTemplate.execute(t -> { - TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem, theVersion); + TermCodeSystemVersion csv = getCurrentCodeSystemVersion(theCodeSystem); if (csv == null) { return Optional.empty(); } @@ -1420,17 +1435,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nullable - private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri, String theVersion) { - StringBuilder key = new StringBuilder(theUri); - if (theVersion != null) { - key.append("|").append(theVersion); - } + private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri) { + String myVersion = getVersionFromCanonicalUrl(theUri); + String key = theUri; TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), t -> myTxTemplate.execute(tx -> { TermCodeSystemVersion csv = null; - TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theUri); + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(getSystemFromCanonicalUrl(theUri)); if (cs != null) { - if (theVersion != null) { - csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(cs.getPid(), theVersion); + if (myVersion != null) { + csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(cs.getPid(), myVersion); } else if (cs.getCurrentVersion() != null) { csv = cs.getCurrentVersion(); } @@ -1447,6 +1460,27 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return retVal; } + private String getVersionFromCanonicalUrl(String theUri) { + String retVal = null; + if (StringUtils.isNotEmpty((theUri))) { + int versionSeparator = theUri.lastIndexOf('|'); + if (versionSeparator != -1) { + retVal = theUri.substring(versionSeparator + 1); + } + } + return retVal; + } + + private String getSystemFromCanonicalUrl(String theUri) { + String retVal = theUri; + if (StringUtils.isNotEmpty((theUri))){ + int versionSeparator = theUri.lastIndexOf('|'); + if (versionSeparator != -1) { + retVal = theUri.substring(0, versionSeparator); + } + } + return retVal; + } @Transactional(propagation = Propagation.REQUIRED) @Override @@ -1869,14 +1903,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { - return subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, null, null, null); - } - - @Override - @Transactional - public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion, String theCodingAVersion, String theCodingBVersion) { - VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA, theSystemVersion, theCodingAVersion); - VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB, theSystemVersion, theCodingBVersion); + VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); + VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); if (!StringUtils.equals(conceptA.getSystem(), conceptB.getSystem())) { throw new InvalidRequestException("Unable to test subsumption across different code systems"); @@ -1886,10 +1914,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { throw new InvalidRequestException("Unable to test subsumption across different code system versions"); } - TermConcept codeA = findCode(conceptA.getSystem(), conceptA.getCode(), conceptA.getSystemVersion()) + String codeACanonicalUrl; + if (StringUtils.isNotEmpty(conceptA.getSystemVersion())) { + codeACanonicalUrl = conceptA.getSystem() + "|" + conceptA.getSystemVersion(); + } else { + codeACanonicalUrl = conceptA.getSystem(); + } + TermConcept codeA = findCode(codeACanonicalUrl, conceptA.getCode()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptA)); - TermConcept codeB = findCode(conceptB.getSystem(), conceptB.getCode(), conceptB.getSystemVersion()) + String codeBCanonicalUrl; + if (StringUtils.isNotEmpty(conceptB.getSystemVersion())) { + codeBCanonicalUrl = conceptB.getSystem() + "|" + conceptB.getSystemVersion(); + } else { + codeBCanonicalUrl = conceptB.getSystem(); + } + TermConcept codeB = findCode(codeBCanonicalUrl, conceptB.getCode()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptB)); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -1908,10 +1948,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { protected abstract ValueSet toCanonicalValueSet(IBaseResource theValueSet); - protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode, String theVersion) { + protected IValidationSupport.LookupCodeResult lookupCode(String theSystem, String theCode) { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); return txTemplate.execute(t -> { - Optional codeOpt = findCode(theSystem, theCode, theVersion); + Optional codeOpt = findCode(theSystem, theCode); if (codeOpt.isPresent()) { TermConcept code = codeOpt.get(); @@ -2456,14 +2496,14 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @NotNull - private VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType, IPrimitiveType theSystemVersionType, String theCodingVersionType) { + private VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType) { String code = theCodeType != null ? theCodeType.getValueAsString() : null; - String system = theSystemType != null ? theSystemType.getValueAsString() : null; - String systemVersion = theSystemVersionType != null ? theSystemVersionType.getValueAsString() : null; + String system = theSystemType != null ? getSystemFromCanonicalUrl(theSystemType.getValueAsString()): null; + String systemVersion = theSystemType != null ? getVersionFromCanonicalUrl(theSystemType.getValueAsString()): null; if (theCodingType != null) { code = theCodingType.getCode(); - system = theCodingType.getSystem(); - systemVersion = theCodingVersionType; + system = getSystemFromCanonicalUrl(theCodingType.getSystem()); + systemVersion = getVersionFromCanonicalUrl(theCodingType.getSystem()); } return new VersionIndependentConcept(system, code, null, systemVersion); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java index f3b7dd24c5d..34bca1b9d4a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java @@ -33,11 +33,11 @@ public interface IValueSetConceptAccumulator { void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, @Nullable Collection theDesignations); - void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, @Nullable Collection theDesignations); +// void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, @Nullable Collection theDesignations); void excludeConcept(String theSystem, String theCode); - void excludeConcept(String theSystem, String theSystemVersion, String theCode); +// void excludeConcept(String theSystem, String theSystemVersion, String theCode); @Nullable default Integer getCapacityRemaining() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java index 0056824d586..6b266ae52f3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java @@ -149,12 +149,7 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { - return super.lookupCode(theSystem, theCode, null); - } - - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - return super.lookupCode(theSystem, theCode, theVersion); + return super.lookupCode(theSystem, theCode); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java index 96243af965a..2075725dc2f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java @@ -89,14 +89,9 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4 return (CodeSystem) theCodeSystem; } - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - return super.lookupCode(theSystem, theCode, theVersion); - } - @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { - return super.lookupCode(theSystem, theCode, null); + return super.lookupCode(theSystem, theCode); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java index 30345a91def..fda53d03c5e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java @@ -71,7 +71,7 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } @Override - public void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, Collection theDesignations) { + public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { TermValueSetConcept concept = saveConcept(theSystem, theCode, theDisplay); if (theDesignations != null) { for (TermConceptDesignation designation : theDesignations) { @@ -80,18 +80,8 @@ public class ValueSetConceptAccumulator implements IValueSetConceptAccumulator { } } - @Override - public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { - includeConceptWithDesignations(theSystem, null, theCode, theDisplay, theDesignations); - } - @Override public void excludeConcept(String theSystem, String theCode) { - excludeConcept(theSystem, null, theCode); - } - - @Override - public void excludeConcept(String theSystem, String theSystemVersion, String theCode) { if (isAnyBlank(theSystem, theCode)) { return; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java index b3af6a3ca15..6005d871f3a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; import ca.uhn.fhir.model.api.annotation.Block; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.HapiExtensions; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.ValueSet; @@ -67,21 +68,18 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V public void includeConcept(String theSystem, String theCode, String theDisplay) { incrementConceptsCount(); ValueSet.ValueSetExpansionContainsComponent contains = this.addContains(); - contains.setSystem(theSystem); + setSystemAndVersion(theSystem, contains); contains.setCode(theCode); contains.setDisplay(theDisplay); } @Override - public void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, Collection theDesignations) { + public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { incrementConceptsCount(); ValueSet.ValueSetExpansionContainsComponent contains = this.addContains(); - contains.setSystem(theSystem); + setSystemAndVersion(theSystem, contains); contains.setCode(theCode); contains.setDisplay(theDisplay); - if (theSystemVersion != null) { - contains.setVersion(theSystemVersion); - } if (theDesignations != null) { for (TermConceptDesignation termConceptDesignation : theDesignations) { contains @@ -96,28 +94,28 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V } } - @Override - public void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, Collection theDesignations) { - this.includeConceptWithDesignations(theSystem, null, theCode, theDisplay, theDesignations); - } - - @Override - public void excludeConcept(String theSystem, String theSystemVersion, String theCode) { - this - .getContains() - .removeIf(t -> - theSystem.equals(t.getSystem()) && - theCode.equals(t.getCode()) && - theSystemVersion.equals(t.getVersion())); - } - @Override public void excludeConcept(String theSystem, String theCode) { - this - .getContains() - .removeIf(t -> - theSystem.equals(t.getSystem()) && - theCode.equals(t.getCode())); + String excludeSystem; + String excludeSystemVersion; + int versionSeparator = theSystem.indexOf("|"); + if(versionSeparator > -1) { + excludeSystemVersion = theSystem.substring(versionSeparator + 1); + excludeSystem = theSystem.substring(0, versionSeparator); + } else { + excludeSystem = theSystem; + excludeSystemVersion = null; + } + if (excludeSystemVersion != null) { + this.getContains().removeIf(t -> + excludeSystem.equals(t.getSystem()) && + theCode.equals(t.getCode()) && + excludeSystemVersion.equals(t.getVersion())); + } else { + this.getContains().removeIf(t -> + theSystem.equals(t.getSystem()) && + theCode.equals(t.getCode())); + } } private void incrementConceptsCount() { @@ -126,4 +124,17 @@ public class ValueSetExpansionComponentWithConceptAccumulator extends ValueSet.V throw new ExpansionTooCostlyException(msg); } } + + private void setSystemAndVersion(String theSystemAndVersion, ValueSet.ValueSetExpansionContainsComponent myComponent) { + if (StringUtils.isNotEmpty((theSystemAndVersion))) { + int versionSeparator = theSystemAndVersion.lastIndexOf('|'); + if (versionSeparator != -1) { + myComponent.setVersion(theSystemAndVersion.substring(versionSeparator + 1)); + myComponent.setSystem(theSystemAndVersion.substring(0,versionSeparator)); + } else { + myComponent.setSystem(theSystemAndVersion); + } + } + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java index 124cf2d6450..bfac6ae33d9 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 @@ -73,8 +73,6 @@ public interface ITermReadSvc extends IValidationSupport { List expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet); - Optional findCode(String theCodeSystem, String theCode, String theVersion); - Optional findCode(String theCodeSystem, String theCode); Set findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode); @@ -105,8 +103,6 @@ public interface ITermReadSvc extends IValidationSupport { IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB); - IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB, IPrimitiveType theSystemVersion, String theCodingAVersion, String theCodingBVersion); - void preExpandDeferredValueSetsToTerminologyTables(); /** diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java index 32d705e43aa..79a19c89a8a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java @@ -233,26 +233,4 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { assertThat(resp, not(containsString(""))); } - @Test - public void unsupportedMethodsThrowException() { - IFhirResourceDaoCodeSystem testFhirResourceDaoValueSet = new FhirResourceDaoValueSetDstu2(); - // Multi-version lookupCode method - try { - testFhirResourceDaoValueSet.lookupCode(null, null, null, null, null); - fail(); - } catch (UnsupportedOperationException theE) { - // Success - } - - // Multi-version subsumes method - try { - testFhirResourceDaoValueSet.subsumes(null, null, null, null, null, null, null); - fail(); - } catch (UnsupportedOperationException theE) { - // Success - } - - - } - } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 7c21676735e..6c7f36b8ea6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -524,7 +524,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { myTerminologyDeferredStorageSvc.saveDeferred(); myTerminologyDeferredStorageSvc.saveDeferred(); - IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM), null,null, mySrd); + IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM), null, mySrd); assertEquals(true, lookupResults.isFound()); ValueSet vs = new ValueSet(); @@ -715,7 +715,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); - IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null,null, mySrd); + IValidationSupport.LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd); assertEquals(true, outcome.isFound()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 358cd5eb2eb..e49548c259d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -56,6 +56,7 @@ import java.util.Optional; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsStringIgnoringCase; @@ -246,6 +247,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); assertEquals(1, page.getContent().size()); @@ -300,6 +302,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); assertEquals(1, page.getContent().size()); @@ -361,6 +364,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); assertEquals(1, page.getContent().size()); @@ -745,6 +749,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { String initialValueSetName = valueSet.getName(); validateTermValueSetNotExpanded(initialValueSetName); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); validateTermValueSetExpandedAndChildren(initialValueSetName, codeSystem); @@ -774,6 +779,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { String initialValueSetName = valueSet.getName(); validateTermValueSetNotExpanded(initialValueSetName); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); validateTermValueSetExpandedAndChildren(initialValueSetName, codeSystem); @@ -1059,6 +1065,17 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } + private boolean clearDeferredStorageQueue() { + + if(!myTerminologyDeferredStorageSvc.isStorageQueueEmpty()) { + myTerminologyDeferredStorageSvc.saveAllDeferred(); + return false; + } else { + return true; + } + + } + @AfterEach public void afterResetPreExpansionDefault() { myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java index a57291d387e..88381769748 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java @@ -52,6 +52,7 @@ import java.util.Optional; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsStringIgnoringCase; @@ -331,6 +332,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); assertEquals(2, page.getContent().size()); @@ -426,6 +428,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); @@ -538,6 +541,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); @@ -930,6 +934,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); String initialValueSetName_v2 = valueSet_v2.getName(); validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + await().until(() -> clearDeferredStorageQueue()); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); @@ -979,6 +984,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide String initialValueSetName_v2 = valueSet_v2.getName(); validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + await().until(() -> clearDeferredStorageQueue()); validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); @@ -1068,7 +1074,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("8450-9", concept.getCode()); assertEquals("Systolic blood pressure--expiration", concept.getDisplay()); assertEquals(2, concept.getDesignations().size()); @@ -1090,7 +1096,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("11378-7", concept.getCode()); assertEquals("Systolic blood pressure at First encounter", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1100,7 +1106,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("8491-3", concept.getCode()); assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay()); assertEquals(1, concept.getDesignations().size()); @@ -1115,7 +1121,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("8492-1", concept.getCode()); assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1141,7 +1147,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("8450-9", concept.getCode()); assertEquals("Systolic blood pressure--expiration v2", concept.getDisplay()); assertEquals(2, concept.getDesignations().size()); @@ -1163,7 +1169,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("11378-7", concept.getCode()); assertEquals("Systolic blood pressure at First encounter v2", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1173,7 +1179,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("8491-3", concept.getCode()); assertEquals("Systolic blood pressure 1 hour minimum v2", concept.getDisplay()); assertEquals(1, concept.getDesignations().size()); @@ -1188,7 +1194,7 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("8492-1", concept.getCode()); assertEquals("Systolic blood pressure 8 hour minimum v2", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1341,6 +1347,17 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide } */ + private boolean clearDeferredStorageQueue() { + + if(!myTerminologyDeferredStorageSvc.isStorageQueueEmpty()) { + myTerminologyDeferredStorageSvc.saveAllDeferred(); + return false; + } else { + return true; + } + + } + @Test public void testCreateDuplicatValueSetVersion() { createExternalCsAndLocalVs(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index 0ffe92d13bb..ba515681dc3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -63,7 +63,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); codeSystem.setName("SYSTEM NAME"); - codeSystem.setVersion("SYSTEM VERSION"); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new); @@ -107,7 +106,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentB = new TermConcept(cs, "ParentB"); cs.getConcepts().add(parentB); - myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), CS_URL, "SYSTEM NAME", null, cs, table); myTerminologyDeferredStorageSvc.saveAllDeferred(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java index e0d35c85fc1..84f0f663b9c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4ElasticsearchIT.java @@ -195,7 +195,7 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { include.setSystem(CS_URL); myTermSvc.expandValueSet(null, vs, myValueSetCodeAccumulator); - verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), anyString(), nullable(String.class), anyCollection()); + verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index c99339b1243..40850b23eaf 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -904,7 +904,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { include.setSystem(CS_URL); myTermSvc.expandValueSet(null, vs, myValueSetCodeAccumulator); - verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), anyString(), nullable(String.class), anyCollection()); + verify(myValueSetCodeAccumulator, times(9)).includeConceptWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection()); } @Test diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java index 3c6c10f9fb3..cc04a447d10 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java @@ -58,11 +58,6 @@ public class BaseValidationSupportWrapper extends BaseValidationSupport { return myWrap.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl); } - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion); - } - @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java index 53c15009f11..829b9977ad7 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java @@ -83,12 +83,6 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple return loadFromCache(myValidateCodeCache, key, t -> super.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl)); } - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - String key = "lookupCode " + theSystem + " " + theCode + " " + theVersion; - return loadFromCache(myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion)); - } - @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { String key = "lookupCode " + theSystem + " " + theCode; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index 51dfe5dd86b..b447c39e160 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 @@ -145,12 +145,6 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { } - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - // For now Common Code Systems will not be versioned. - return lookupCode(theValidationSupportContext, theSystem, theCode); - } - @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 6b3d3dd135e..3fca6d034ac 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -308,44 +308,6 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu .setMessage(message); } - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - // The following code mostly duplicates the validateCode method, differing in that it includes the code system version in the generated ValueSet resource. - IBaseResource vs; - switch (myCtx.getVersion().getVersion()) { - case DSTU3: - vs = new org.hl7.fhir.dstu3.model.ValueSet() - .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(theSystem).setVersion(theVersion))); - break; - case R4: - vs = new org.hl7.fhir.r4.model.ValueSet() - .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(theSystem).setVersion(theVersion))); - break; - case R5: - vs = new org.hl7.fhir.r5.model.ValueSet() - .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(theSystem).setVersion(theVersion))); - break; - case DSTU2_HL7ORG: - case DSTU2: - case DSTU2_1: - default: - throw new IllegalArgumentException("Can not handle version: " + myCtx.getVersion().getVersion()); - } - - ValueSetExpansionOutcome valueSetExpansionOutcome = expandValueSet(theValidationSupportContext, null, vs); - if (valueSetExpansionOutcome == null) { - return null; - } - - IBaseResource expansion = valueSetExpansionOutcome.getValueSet(); - - return validateCodeInExpandedValueSet(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, expansion).asLookupCodeResult(theSystem, theCode); - - } - @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { return validateCode(theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null).asLookupCodeResult(theSystem, theCode); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index ac7da03cd07..841038fd4b7 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -257,16 +257,6 @@ public class ValidationSupportChain implements IValidationSupport { return null; } - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theVersion) { - for (IValidationSupport next : myChain) { - if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) { - return next.lookupCode(theValidationSupportContext, theSystem, theCode, theVersion); - } - } - return null; - } - @Override public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { for (IValidationSupport next : myChain) { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index d5055423b05..48d5cb31eb7 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -254,7 +254,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(mockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> { + when(mockSupport.lookupCode(any(), any(), any())).thenAnswer(t -> { String system = t.getArgument(1, String.class); String code = t.getArgument(2, String.class); if (myValidConcepts.contains(system + "___" + code)) { From dffc02a12600ff670d4278e1e9fe94f481516586 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 23 Sep 2020 16:51:18 -0400 Subject: [PATCH 48/71] Missing fixes for DSTU3 and R5. --- .../provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java | 3 +++ .../jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 9c54199e262..9c522e5cd81 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -64,6 +64,9 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD IValidationSupport.LookupCodeResult result; if (theVersion != null) { result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theRequestDetails); + } else if (theCoding != null && theCoding.hasVersion()) { + Coding codingWithVersion = new Coding(theCoding.getSystem() + "|" + theCoding.getVersion(), theCoding.getCode(), theCoding.getDisplay()); + result = dao.lookupCode(theCode, theSystem, codingWithVersion, theRequestDetails); } else { result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 126c44a3b80..693ab7f1c69 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -66,6 +66,9 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 Date: Wed, 23 Sep 2020 17:17:07 -0400 Subject: [PATCH 49/71] Additional cleanup and migration tasks. --- .../main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java | 2 +- .../uhn/fhir/jpa/term/IValueSetConceptAccumulator.java | 4 ---- .../dao/dstu2/FhirResourceDaoValueSetDstu2Test.java | 5 +---- .../dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java | 2 +- .../ResourceProviderDstu3ValueSetVersionedTest.java | 6 ------ .../jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java | 10 +++++++--- 6 files changed, 10 insertions(+), 19 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java index 4cbe867d6cf..d83e6cb3366 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java @@ -46,7 +46,7 @@ public class TermValueSet implements Serializable { public static final int MAX_EXPANSION_STATUS_LENGTH = 50; public static final int MAX_NAME_LENGTH = 200; public static final int MAX_URL_LENGTH = 200; - static final int MAX_VER_LENGTH = 200; + public static final int MAX_VER_LENGTH = 200; @Id() @SequenceGenerator(name = "SEQ_VALUESET_PID", sequenceName = "SEQ_VALUESET_PID") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java index 34bca1b9d4a..db098111343 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java @@ -33,12 +33,8 @@ public interface IValueSetConceptAccumulator { void includeConceptWithDesignations(String theSystem, String theCode, String theDisplay, @Nullable Collection theDesignations); -// void includeConceptWithDesignations(String theSystem, String theSystemVersion, String theCode, String theDisplay, @Nullable Collection theDesignations); - void excludeConcept(String theSystem, String theCode); -// void excludeConcept(String theSystem, String theSystemVersion, String theCode); - @Nullable default Integer getCapacityRemaining() { return null; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java index 79a19c89a8a..5c3b320ecbc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java @@ -1,8 +1,6 @@ package ca.uhn.fhir.jpa.dao.dstu2; import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.resource.ValueSet; @@ -10,7 +8,6 @@ import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.UriDt; -import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -25,7 +22,6 @@ import static org.hamcrest.Matchers.stringContainsInOrder; 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 static org.junit.jupiter.api.Assertions.fail; public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { @@ -233,4 +229,5 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { assertThat(resp, not(containsString(""))); } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 6c7f36b8ea6..70d144cbe00 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -524,7 +524,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { myTerminologyDeferredStorageSvc.saveDeferred(); myTerminologyDeferredStorageSvc.saveDeferred(); - IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM), null, mySrd); + IValidationSupport.LookupCodeResult lookupResults = myCodeSystemDao.lookupCode(new StringType("childAA"), new StringType(URL_MY_CODE_SYSTEM),null, mySrd); assertEquals(true, lookupResults.isFound()); ValueSet vs = new ValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java index 416699c7201..0238206b51f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java @@ -178,12 +178,6 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv } -// private void createExternalCsAndLocalVsWithUnknownCode() { -// String codeSystemUrl = createExternalCs(); - -// createLocalVsWithUnknownCode(codeSystemUrl); -// } - private void createLocalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); 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 e770e370586..86aaa437274 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.TermValueSet; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask; @@ -142,10 +143,13 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { //EMPI Target Type empiLink.addColumn("20200727.1","TARGET_TYPE").nullable().type(ColumnTypeEnum.STRING, EmpiLink.TARGET_TYPE_LENGTH); - //Term CodeSystem Version + //Term CodeSystem Version and Term ValueSet Version Builder.BuilderWithTableName trmCodeSystemVer = version.onTable("TRM_CODESYSTEM_VER"); - trmCodeSystemVer.addIndex("20200916.1", "IDX_CODESYSTEM_AND_VER").unique(true).withColumns("CODESYSTEM_PID", "CS_VERSION_ID"); - + trmCodeSystemVer.addIndex("20200923.1", "IDX_CODESYSTEM_AND_VER").unique(true).withColumns("CODESYSTEM_PID", "CS_VERSION_ID"); + Builder.BuilderWithTableName trmValueSet = version.onTable("TRM_VALUESET"); + trmValueSet.addColumn("20200923.2", "VER").nullable().type(ColumnTypeEnum.STRING, TermValueSet.MAX_VER_LENGTH); + trmValueSet.dropIndex("20200923.3", "IDX_VALUESET_URL"); + trmValueSet.addIndex("20200923.4", "IDX_VALUESET_URL").unique(true).withColumns("URL", "VAR"); } protected void init510_20200725() { From 5fe46be41cb8d0f455f3c5a32f3bc4a23fbaa2f2 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Thu, 24 Sep 2020 18:51:47 -0400 Subject: [PATCH 50/71] Validate code changes and new tests. --- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 6 +- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 6 +- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 6 +- .../BaseJpaResourceProviderValueSetDstu3.java | 16 +- .../BaseJpaResourceProviderCodeSystemR4.java | 9 - .../r4/BaseJpaResourceProviderValueSetR4.java | 16 +- .../BaseJpaResourceProviderCodeSystemR5.java | 9 - .../r5/BaseJpaResourceProviderValueSetR5.java | 18 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 150 +- .../uhn/fhir/jpa/term/TermReadSvcDstu2.java | 14 +- .../uhn/fhir/jpa/term/TermReadSvcDstu3.java | 7 + .../ca/uhn/fhir/jpa/term/TermReadSvcR4.java | 6 + .../ca/uhn/fhir/jpa/term/TermReadSvcR5.java | 7 + ...rceProviderDstu3ValueSetVersionedTest.java | 471 +++-- ...sourceProviderR4ValueSetVersionedTest.java | 405 +++-- ...sourceProviderR5ValueSetVersionedTest.java | 1549 +++++++++++++++++ .../CommonCodeSystemsTerminologyService.java | 24 + ...oryTerminologyServerValidationSupport.java | 254 ++- 18 files changed, 2472 insertions(+), 501 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java 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 3df67dd6bea..16f219a6f4c 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 @@ -100,7 +100,11 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao dao = (IFhirResourceDaoValueSet) getDao(); - IValidationSupport.CodeValidationResult result = dao.validateCode(url, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + UriType valueSetIdentifier; + if (theValueSetVersion != null) { + valueSetIdentifier = new UriType(theValueSetUrl.getValue() + "|" + theValueSetVersion); + } else { + valueSetIdentifier = theValueSetUrl; + } + UriType codeSystemIdentifier; + if (theSystemVersion != null) { + codeSystemIdentifier = new UriType(theSystem.getValue() + "|" + theSystemVersion); + } else { + codeSystemIdentifier = theSystem; + } + IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, 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/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index a54a645ec59..a3e16a7fb3c 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 @@ -66,9 +66,6 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoValueSet) getDao(); - IValidationSupport.CodeValidationResult result = dao.validateCode(theValueSetUrl, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + UriType valueSetIdentifier; + if (theValueSetVersion != null) { + valueSetIdentifier = new UriType(theValueSetUrl.getValue() + "|" + theValueSetVersion); + } else { + valueSetIdentifier = theValueSetUrl; + } + UriType codeSystemIdentifier; + if (theSystemVersion != null) { + codeSystemIdentifier = new UriType(theSystem.getValue() + "|" + theSystemVersion); + } else { + codeSystemIdentifier = theSystem; + } + IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, 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/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 693ab7f1c69..91c0028d718 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -66,9 +66,6 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoValueSet) getDao(); - IValidationSupport.CodeValidationResult result = dao.validateCode(theValueSetUrl, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails); + UriType valueSetIdentifier; + if (theValueSetVersion != null) { + valueSetIdentifier = new UriType(theValueSetUrl.getValue() + "|" + theValueSetVersion); + } else { + valueSetIdentifier = theValueSetUrl; + } + UriType codeSystemIdentifier; + if (theSystemVersion != null) { + codeSystemIdentifier = new UriType(theSystem.getValue() + "|" + theSystemVersion); + } else { + codeSystemIdentifier = theSystem; + } + IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails); return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index d977c49cb1b..8d3912a5bbb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -651,22 +651,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { */ private Boolean expandValueSetHandleIncludeOrExclude(@Nullable ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull) { - String system = theIncludeOrExclude.getSystem(); - boolean hasSystem = isNotBlank(system); + String includeOrExcludeSystemUrl = theIncludeOrExclude.getSystem(); + boolean hasSystem = isNotBlank(includeOrExcludeSystemUrl); boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0; if (hasSystem) { - if (theWantConceptOrNull != null && theWantConceptOrNull.getSystem() != null && !system.equals(theWantConceptOrNull.getSystem())) { + if (theWantConceptOrNull != null && theWantConceptOrNull.getSystem() != null && !includeOrExcludeSystemUrl.equals(theWantConceptOrNull.getSystem())) { return false; } - ourLog.debug("Starting {} expansion around CodeSystem: {}", (theAdd ? "inclusion" : "exclusion"), system); + ourLog.debug("Starting {} expansion around CodeSystem: {}", (theAdd ? "inclusion" : "exclusion"), includeOrExcludeSystemUrl); - TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(includeOrExcludeSystemUrl); if (cs != null) { - return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs); + return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, includeOrExcludeSystemUrl, cs); } else { @@ -679,7 +679,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } // No CodeSystem matching the URL found in the database. - CodeSystem codeSystemFromContext = fetchCanonicalCodeSystemFromCompleteContext(system); + CodeSystem codeSystemFromContext = fetchCanonicalCodeSystemFromCompleteContext(includeOrExcludeSystemUrl); if (codeSystemFromContext == null) { // This is a last ditch effort.. We don't have a CodeSystem resource for the desired CS, and we don't have @@ -704,7 +704,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { for (VersionIndependentConcept next : includedConcepts) { String nextSystem = next.getSystem(); if (nextSystem == null) { - nextSystem = system; + nextSystem = includeOrExcludeSystemUrl; } LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode()); @@ -720,7 +720,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", system); + String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", includeOrExcludeSystemUrl); if (provideExpansionOptions(theExpansionOptions).isFailOnMissingCodeSystem()) { throw new PreconditionFailedException(msg); } else { @@ -735,12 +735,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { for (ValueSet.ConceptReferenceComponent next : theIncludeOrExclude.getConcept()) { String nextCode = next.getCode(); if (theWantConceptOrNull == null || theWantConceptOrNull.getCode().equals(nextCode)) { - if (isNoneBlank(system, nextCode) && !theAddedCodes.contains(system + "|" + nextCode)) { + if (isNoneBlank(includeOrExcludeSystemUrl, nextCode) && !theAddedCodes.contains(includeOrExcludeSystemUrl + "|" + nextCode)) { CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode); if (code != null) { String display = code.getDisplay(); - addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, system, nextCode, display); + addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, includeOrExcludeSystemUrl, nextCode, display); } } @@ -748,7 +748,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } else { List concept = codeSystemFromContext.getConcept(); - addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, system, concept, theAdd, theWantConceptOrNull); + addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, includeOrExcludeSystemUrl, concept, theAdd, theWantConceptOrNull); } return false; @@ -841,13 +841,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /* * Filters */ - String canonicalSystem; + String codeSystemIdentifier; if (codeSystemVersion != null) { - canonicalSystem = theSystem + "|" + codeSystemVersion; + codeSystemIdentifier = theSystem + "|" + codeSystemVersion; } else { - canonicalSystem = theSystem; + codeSystemIdentifier = theSystem; } - handleFilters(bool, canonicalSystem, qb, theIncludeOrExclude); + handleFilters(bool, codeSystemIdentifier, qb, theIncludeOrExclude); Query luceneQuery = bool.createQuery(); @@ -956,15 +956,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void handleFilters(BooleanJunction theBool, String theCanonicalSystem, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { + private void handleFilters(BooleanJunction theBool, String theCodeSystemIdentifier, QueryBuilder theQb, ValueSet.ConceptSetComponent theIncludeOrExclude) { if (theIncludeOrExclude.getFilter().size() > 0) { for (ValueSet.ConceptSetFilterComponent nextFilter : theIncludeOrExclude.getFilter()) { - handleFilter(theCanonicalSystem, theQb, theBool, nextFilter); + handleFilter(theCodeSystemIdentifier, theQb, theBool, nextFilter); } } } - private void handleFilter(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void handleFilter(String theCodeSystemIdentifier, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { if (isBlank(theFilter.getValue()) && theFilter.getOp() == null && isBlank(theFilter.getProperty())) { return; } @@ -980,23 +980,23 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { break; case "concept": case "code": - handleFilterConceptAndCode(theSystem, theQb, theBool, theFilter); + handleFilterConceptAndCode(theCodeSystemIdentifier, theQb, theBool, theFilter); break; case "parent": case "child": - isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + isCodeSystemLoingOrThrowInvalidRequestException(theCodeSystemIdentifier, theFilter.getProperty()); handleFilterLoincParentChild(theBool, theFilter); break; case "ancestor": - isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestor(theSystem, theBool, theFilter); + isCodeSystemLoingOrThrowInvalidRequestException(theCodeSystemIdentifier, theFilter.getProperty()); + handleFilterLoincAncestor(theCodeSystemIdentifier, theBool, theFilter); break; case "descendant": - isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincDescendant(theSystem, theBool, theFilter); + isCodeSystemLoingOrThrowInvalidRequestException(theCodeSystemIdentifier, theFilter.getProperty()); + handleFilterLoincDescendant(theCodeSystemIdentifier, theBool, theFilter); break; case "copyright": - isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + isCodeSystemLoingOrThrowInvalidRequestException(theCodeSystemIdentifier, theFilter.getProperty()); handleFilterLoincCopyright(theBool, theFilter); break; default: @@ -1005,8 +1005,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) { - String systemUrl = getSystemFromCanonicalUrl(theSystem); + private void isCodeSystemLoingOrThrowInvalidRequestException(String theSystemIdentifier, String theProperty) { + String systemUrl = getUrlFromIdentifier(theSystemIdentifier); if (!isCodeSystemLoinc(systemUrl)) { throw new InvalidRequestException("Invalid filter, property " + theProperty + " is LOINC-specific and cannot be used with system: " + systemUrl); } @@ -1435,12 +1435,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nullable - private TermCodeSystemVersion getCurrentCodeSystemVersion(String theUri) { - String myVersion = getVersionFromCanonicalUrl(theUri); - String key = theUri; + private TermCodeSystemVersion getCurrentCodeSystemVersion(String theCodeSystemIdentifier) { + String myVersion = getVersionFromIdentifier(theCodeSystemIdentifier); + String key = theCodeSystemIdentifier; TermCodeSystemVersion retVal = myCodeSystemCurrentVersionCache.get(key.toString(), t -> myTxTemplate.execute(tx -> { TermCodeSystemVersion csv = null; - TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(getSystemFromCanonicalUrl(theUri)); + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(getUrlFromIdentifier(theCodeSystemIdentifier)); if (cs != null) { if (myVersion != null) { csv = myCodeSystemVersionDao.findByCodeSystemPidAndVersion(cs.getPid(), myVersion); @@ -1460,7 +1460,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return retVal; } - private String getVersionFromCanonicalUrl(String theUri) { + private String getVersionFromIdentifier(String theUri) { String retVal = null; if (StringUtils.isNotEmpty((theUri))) { int versionSeparator = theUri.lastIndexOf('|'); @@ -1471,7 +1471,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return retVal; } - private String getSystemFromCanonicalUrl(String theUri) { + private String getUrlFromIdentifier(String theUri) { String retVal = theUri; if (StringUtils.isNotEmpty((theUri))){ int versionSeparator = theUri.lastIndexOf('|'); @@ -1777,15 +1777,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public CodeValidationResult validateCode(ConceptValidationOptions theOptions, IIdType theValueSetId, String theValueSetUrl, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) { + public CodeValidationResult validateCode(ConceptValidationOptions theOptions, IIdType theValueSetId, String theValueSetIdentifier, String theCodeSystemIdentifierToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseDatatype theCodingToValidate, IBaseDatatype theCodeableConceptToValidate) { - CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept); + CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConceptToValidate); boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0; - Coding coding = toCanonicalCoding(theCoding); - boolean haveCoding = coding != null && coding.isEmpty() == false; + Coding canonicalCodingToValidate = toCanonicalCoding(theCodingToValidate); + boolean haveCoding = canonicalCodingToValidate != null && canonicalCodingToValidate.isEmpty() == false; - boolean haveCode = theCode != null && theCode.isEmpty() == false; + boolean haveCode = theCodeToValidate != null && theCodeToValidate.isEmpty() == false; if (!haveCodeableConcept && !haveCoding && !haveCode) { throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate"); @@ -1794,38 +1794,54 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { throw new InvalidRequestException("$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)"); } - boolean haveIdentifierParam = isNotBlank(theValueSetUrl); - String valueSetUrl; + boolean haveIdentifierParam = isNotBlank(theValueSetIdentifier); + String valueSetIdentifier; if (theValueSetId != null) { IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").read(theValueSetId); - valueSetUrl = CommonCodeSystemsTerminologyService.getValueSetUrl(valueSet); + StringBuilder valueSetIdentifierBuilder = new StringBuilder(CommonCodeSystemsTerminologyService.getValueSetUrl(valueSet)); + String valueSetVersion = CommonCodeSystemsTerminologyService.getValueSetVersion(valueSet); + if (valueSetVersion != null) { + valueSetIdentifierBuilder.append("|").append(valueSetVersion); + } + valueSetIdentifier = valueSetIdentifierBuilder.toString(); + } else if (haveIdentifierParam) { - valueSetUrl = theValueSetUrl; + valueSetIdentifier = theValueSetIdentifier; } else { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); } ValidationSupportContext validationContext = new ValidationSupportContext(provideValidationSupport()); - String code = theCode; - String system = theSystem; - String display = theDisplay; + String codeValueToValidate = theCodeToValidate; + String codeSystemIdentifierValueToValidate = theCodeSystemIdentifierToValidate; + String codeDisplayValueToValidate = theDisplayToValidate; if (haveCodeableConcept) { for (int i = 0; i < codeableConcept.getCoding().size(); i++) { Coding nextCoding = codeableConcept.getCoding().get(i); - CodeValidationResult nextValidation = validateCode(validationContext, theOptions, nextCoding.getSystem(), nextCoding.getCode(), nextCoding.getDisplay(), valueSetUrl); + String codeSystemIdentifier; + if (nextCoding.hasVersion()) { + codeSystemIdentifier = nextCoding.getSystem() + "|" + nextCoding.getVersion(); + } else { + codeSystemIdentifier = nextCoding.getSystem(); + } + CodeValidationResult nextValidation = validateCode(validationContext, theOptions, codeSystemIdentifier, nextCoding.getCode(), nextCoding.getDisplay(), valueSetIdentifier); if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) { return nextValidation; } } } else if (haveCoding) { - system = coding.getSystem(); - code = coding.getCode(); - display = coding.getDisplay(); + if (canonicalCodingToValidate.hasVersion()) { + codeSystemIdentifierValueToValidate = canonicalCodingToValidate.getSystem() + "|" + canonicalCodingToValidate.getVersion(); + } else { + codeSystemIdentifierValueToValidate = canonicalCodingToValidate.getSystem(); + } + codeValueToValidate = canonicalCodingToValidate.getCode(); + codeDisplayValueToValidate = canonicalCodingToValidate.getDisplay(); } - return validateCode(validationContext, theOptions, system, code, display, valueSetUrl); + return validateCode(validationContext, theOptions, codeSystemIdentifierValueToValidate, codeValueToValidate, codeDisplayValueToValidate, valueSetIdentifier); } private boolean isNotSafeToPreExpandValueSets() { @@ -1914,22 +1930,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { throw new InvalidRequestException("Unable to test subsumption across different code system versions"); } - String codeACanonicalUrl; + String codeASystemIdentifier; if (StringUtils.isNotEmpty(conceptA.getSystemVersion())) { - codeACanonicalUrl = conceptA.getSystem() + "|" + conceptA.getSystemVersion(); + codeASystemIdentifier = conceptA.getSystem() + "|" + conceptA.getSystemVersion(); } else { - codeACanonicalUrl = conceptA.getSystem(); + codeASystemIdentifier = conceptA.getSystem(); } - TermConcept codeA = findCode(codeACanonicalUrl, conceptA.getCode()) + TermConcept codeA = findCode(codeASystemIdentifier, conceptA.getCode()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptA)); - String codeBCanonicalUrl; + String codeBSystemIdentifier; if (StringUtils.isNotEmpty(conceptB.getSystemVersion())) { - codeBCanonicalUrl = conceptB.getSystem() + "|" + conceptB.getSystemVersion(); + codeBSystemIdentifier = conceptB.getSystem() + "|" + conceptB.getSystemVersion(); } else { - codeBCanonicalUrl = conceptB.getSystem(); + codeBSystemIdentifier = conceptB.getSystem(); } - TermConcept codeB = findCode(codeBCanonicalUrl, conceptB.getCode()) + TermConcept codeB = findCode(codeBSystemIdentifier, conceptB.getCode()) .orElseThrow(() -> new InvalidRequestException("Unknown code: " + conceptB)); FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); @@ -2405,6 +2421,9 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Nullable protected abstract Coding toCanonicalCoding(@Nullable IBaseDatatype theCoding); + @Nullable + protected abstract Coding toCanonicalCoding(@Nullable IBaseCoding theCoding); + @Nullable protected abstract CodeableConcept toCanonicalCodeableConcept(@Nullable IBaseDatatype theCodeableConcept); @@ -2496,14 +2515,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @NotNull - private VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theSystemType, IBaseCoding theCodingType) { + private VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theCodeSystemIdentifierType, IBaseCoding theCodingType) { String code = theCodeType != null ? theCodeType.getValueAsString() : null; - String system = theSystemType != null ? getSystemFromCanonicalUrl(theSystemType.getValueAsString()): null; - String systemVersion = theSystemType != null ? getVersionFromCanonicalUrl(theSystemType.getValueAsString()): null; + String system = theCodeSystemIdentifierType != null ? getUrlFromIdentifier(theCodeSystemIdentifierType.getValueAsString()): null; + String systemVersion = theCodeSystemIdentifierType != null ? getVersionFromIdentifier(theCodeSystemIdentifierType.getValueAsString()): null; if (theCodingType != null) { - code = theCodingType.getCode(); - system = getSystemFromCanonicalUrl(theCodingType.getSystem()); - systemVersion = getVersionFromCanonicalUrl(theCodingType.getSystem()); + Coding canonicalizedCoding = toCanonicalCoding(theCodingType); + code = canonicalizedCoding.getCode(); + system = canonicalizedCoding.getSystem(); + systemVersion = canonicalizedCoding.getVersion(); } return new VersionIndependentConcept(system, code, null, systemVersion); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java index 7f15699e201..e0f0ef269b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.util.VersionIndependentConcept; +import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; @@ -148,6 +149,17 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { return retVal; } + @Nullable + @Override + protected Coding toCanonicalCoding(@Nullable IBaseCoding theCoding) { + Coding retVal = null; + if (theCoding != null) { + CodingDt coding = (CodingDt) theCoding; + retVal = new Coding(coding.getSystem(), coding.getCode(), coding.getDisplay()); + } + return retVal; + } + @Nullable @Override protected CodeableConcept toCanonicalCodeableConcept(@Nullable IBaseDatatype theCodeableConcept) { @@ -157,7 +169,7 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { CodeableConceptDt cc = (CodeableConceptDt) theCodeableConcept; outcome.setText(cc.getText()); for (CodingDt next : cc.getCoding()) { - outcome.addCoding(toCanonicalCoding(next)); + outcome.addCoding(toCanonicalCoding((IBaseDatatype)next)); } } return outcome; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java index 6b266ae52f3..4ab16d142d9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java @@ -20,6 +20,7 @@ import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.validation.ValidationOptions; @@ -112,6 +113,12 @@ public class TermReadSvcDstu3 extends BaseTermReadSvcImpl implements IValidation return VersionConvertor_30_40.convertCoding((org.hl7.fhir.dstu3.model.Coding) theCoding); } + @Override + @Nullable + protected org.hl7.fhir.r4.model.Coding toCanonicalCoding(IBaseCoding theCoding) { + return VersionConvertor_30_40.convertCoding((org.hl7.fhir.dstu3.model.Coding) theCoding); + } + @Override @Nullable protected org.hl7.fhir.r4.model.CodeableConcept toCanonicalCodeableConcept(IBaseDatatype theCoding) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java index 2075725dc2f..9cfe7c533b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR4.java @@ -8,6 +8,7 @@ import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4; import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; +import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; @@ -107,6 +108,11 @@ public class TermReadSvcR4 extends BaseTermReadSvcImpl implements ITermReadSvcR4 return (Coding) theCoding; } + @Override + protected Coding toCanonicalCoding(IBaseCoding theCoding) { + return (Coding) theCoding; + } + @Override protected CodeableConcept toCanonicalCodeableConcept(IBaseDatatype theCodeableConcept) { return (CodeableConcept) theCodeableConcept; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java index 2fbda8d9d88..8303aea044e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; import ca.uhn.fhir.util.ValidateUtil; import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.convertors.conv40_50.CodeSystem40_50; +import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.CodeSystem; @@ -111,6 +112,12 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup return VersionConvertor_40_50.convertCoding((Coding) theCoding); } + @Override + @Nullable + protected org.hl7.fhir.r4.model.Coding toCanonicalCoding(IBaseCoding theCoding) { + return VersionConvertor_40_50.convertCoding((Coding) theCoding); + } + @Override @Nullable protected org.hl7.fhir.r4.model.CodeableConcept toCanonicalCodeableConcept(IBaseDatatype theCoding) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java index 0238206b51f..b92cf6566d1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java @@ -29,6 +29,10 @@ import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; import org.hl7.fhir.dstu3.model.codesystems.HttpVerb; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.data.domain.PageRequest; @@ -49,6 +53,7 @@ import static org.hamcrest.Matchers.containsStringIgnoringCase; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -116,21 +121,16 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv private void loadAndPersistValueSet() throws IOException { ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); - persistTwoVersionsOfValueSet(valueSet, HttpVerb.POST); - } - - @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void persistTwoVersionsOfValueSet(ValueSet theValueSet, HttpVerb theVerb) { - theValueSet.setVersion("1"); - theValueSet.setId("ValueSet/vs1"); - theValueSet.getCompose().getInclude().get(0).setVersion("1"); - myExtensionalVsId_v1 = persistSingleValueSet(theValueSet, theVerb); + valueSet.setVersion("1"); + valueSet.setId("ValueSet/vs1"); + valueSet.getCompose().getInclude().get(0).setVersion("1"); + myExtensionalVsId_v1 = persistSingleValueSet(valueSet, HttpVerb.POST); myExtensionalVsIdOnResourceTable_v1 = myValueSetDao.readEntity(myExtensionalVsId_v1, null).getId(); - theValueSet.setVersion("2"); - theValueSet.setId("ValueSet/vs2"); - theValueSet.getCompose().getInclude().get(0).setVersion("2"); - myExtensionalVsId_v2 = persistSingleValueSet(theValueSet, theVerb); + valueSet.setVersion("2"); + valueSet.setId("ValueSet/vs2"); + valueSet.getCompose().getInclude().get(0).setVersion("2"); + myExtensionalVsId_v2 = persistSingleValueSet(valueSet, HttpVerb.POST); myExtensionalVsIdOnResourceTable_v2 = myValueSetDao.readEntity(myExtensionalVsId_v2, null).getId(); } @@ -138,6 +138,8 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv private IIdType persistSingleValueSet(ValueSet theValueSet, HttpVerb theVerb) { final IIdType[] vsId = new IIdType[1]; switch (theVerb) { + case GET: + break; case POST: new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override @@ -154,6 +156,8 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv } }); break; + case DELETE: + case NULL: default: throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb); } @@ -1053,7 +1057,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("8450-9", concept.getCode()); assertEquals("Systolic blood pressure--expiration", concept.getDisplay()); assertEquals(2, concept.getDesignations().size()); @@ -1075,7 +1079,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("11378-7", concept.getCode()); assertEquals("Systolic blood pressure at First encounter", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1085,7 +1089,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("8491-3", concept.getCode()); assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay()); assertEquals(1, concept.getDesignations().size()); @@ -1100,7 +1104,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|1", concept.getSystem()); assertEquals("8492-1", concept.getCode()); assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1126,7 +1130,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv TermValueSetConcept concept = termValueSet.getConcepts().get(0); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("8450-9", concept.getCode()); assertEquals("Systolic blood pressure--expiration v2", concept.getDisplay()); assertEquals(2, concept.getDesignations().size()); @@ -1148,7 +1152,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv concept = termValueSet.getConcepts().get(1); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("11378-7", concept.getCode()); assertEquals("Systolic blood pressure at First encounter v2", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); @@ -1158,7 +1162,7 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv concept = termValueSet.getConcepts().get(22); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("8491-3", concept.getCode()); assertEquals("Systolic blood pressure 1 hour minimum v2", concept.getDisplay()); assertEquals(1, concept.getDesignations().size()); @@ -1173,161 +1177,16 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv concept = termValueSet.getConcepts().get(23); ourLog.info("Concept:\n" + concept.toString()); - assertEquals("http://acme.org", concept.getSystem()); + assertEquals("http://acme.org|2", concept.getSystem()); assertEquals("8492-1", concept.getCode()); assertEquals("Systolic blood pressure 8 hour minimum v2", concept.getDisplay()); assertEquals(0, concept.getDesignations().size()); assertEquals(23, concept.getOrder()); }); } -/* - @Test - public void testValidateCodeOperationByCodeAndSystemInstance() throws Exception { - loadAndPersistCodeSystemAndValueSet(); - - Parameters respParam = ourClient - .operation() - .onInstance(myExtensionalVsId_v1) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8495-4")) - .andParameter("system", new UriType("http://acme.org")) - .execute(); - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } @Test - public void testValidateCodeOperationByCodeAndSystemInstanceOnType() throws IOException { - createLocalCs(); - createLocalVsWithIncludeConcept(); - - String url = ourServerBase + - "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + - UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + - "&code=AA"; - - HttpGet request = new HttpGet(url); - request.addHeader("Accept", "application/fhir+json"); - try (CloseableHttpResponse response = ourHttpClient.execute(request)) { - String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); - ourLog.info(respString); - - Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); - assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } - } - - @Test - public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { - createLocalCs(); - createLocalVsWithIncludeConcept(); - - String url = ourServerBase + - "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + - UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + - "&code=AA"; - - ourLog.info("* Requesting: {}", url); - - HttpGet request = new HttpGet(url); - request.addHeader("Accept", "application/fhir+json"); - try (CloseableHttpResponse response = ourHttpClient.execute(request)) { - String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); - ourLog.info(respString); - - Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); - assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } - } - - @Test - public void testValidateCodeOperationByCodeAndSystemType() throws Exception { - loadAndPersistCodeSystemAndValueSet(); - - Parameters respParam = ourClient - .operation() - .onInstance(myExtensionalVsId_v1) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8450-9")) - .andParameter("system", new UriType("http://acme.org")) - .execute(); - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } - - @Test - public void testValidateCodeOperationNoValueSetProvided() throws Exception { - loadAndPersistCodeSystemAndValueSet(); - - try { - ourClient - .operation() - .onType(ValueSet.class) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8450-9")) - .andParameter("system", new UriType("http://acme.org")) - .execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.", e.getMessage()); - } - } - - @Test - public void testValidateCodeOperationOnInstanceWithIsAExpansion() throws IOException { - CodeSystem cs = new CodeSystem(); - cs.setUrl("http://mycs"); - cs.setContent(CodeSystemContentMode.COMPLETE); - cs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); - cs.setStatus(Enumerations.PublicationStatus.ACTIVE); - ConceptDefinitionComponent parentA = cs.addConcept().setCode("ParentA").setDisplay("Parent A"); - parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); - myCodeSystemDao.create(cs); - - ValueSet vs = new ValueSet(); - vs.setUrl("http://myvs"); - vs.getCompose() - .addInclude() - .setSystem("http://mycs") - .addFilter() - .setOp(FilterOperator.ISA) - .setProperty("concept") - .setValue("ParentA"); - IIdType vsId = myValueSetDao.create(vs).getId().toUnqualifiedVersionless(); - - HttpGet expandGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$expand?_pretty=true"); - try (CloseableHttpResponse status = ourHttpClient.execute(expandGet)) { - String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); - ourLog.info("Response: {}", response); - } - - HttpGet validateCodeGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=ChildAA&_pretty=true"); - try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet)) { - String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); - ourLog.info("Response: {}", response); - Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); - assertEquals(true, output.getParameterBool("result")); - } - - HttpGet validateCodeGet2 = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=FOO&_pretty=true"); - try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet2)) { - String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); - ourLog.info("Response: {}", response); - Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); - assertEquals(false, output.getParameterBool("result")); - } - - } -*/ - - @Test - public void testCreateDuplicatValueSetVersion() { + public void testCreateDuplicateValueSetVersion() { createExternalCsAndLocalVs(); try { persistLocalVs(createLocalVs(URL_MY_CODE_SYSTEM, "1")); @@ -1338,6 +1197,284 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv } + @Test + public void testValidateCodeOperationByCodeAndSystem() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // With correct system version specified. Should pass. + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationOnInstanceByCodeAndSystem() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // With correct system version specified. Should pass. + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationByCoding() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Coding codingToValidate_v1 = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + codingToValidate_v1.setVersion("1"); + + Coding codingToValidate_v2 = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum v2"); + codingToValidate_v2.setVersion("2"); + + // With correct system version specified. Should pass. + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationByCodeableConcept() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Coding codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + codingToValidate.setVersion("1"); + CodeableConcept codeableConceptToValidate_v1 = new CodeableConcept(codingToValidate); + + codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum v2"); + codingToValidate.setVersion("2"); + CodeableConcept codeableConceptToValidate_v2 = new CodeableConcept(codingToValidate); + + // With correct system version specified. Should pass. + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + @AfterEach public void afterResetPreExpansionDefault() { myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java index 88381769748..613f33351ac 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java @@ -13,12 +13,11 @@ import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; -import org.apache.commons.io.Charsets; +import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -29,6 +28,8 @@ import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; 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.Enumerations; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; @@ -59,6 +60,7 @@ import static org.hamcrest.Matchers.containsStringIgnoringCase; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -126,21 +128,16 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide private void loadAndPersistValueSet() throws IOException { ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); - persistTwoVersionsOfValueSet(valueSet, HttpVerb.POST); - } - - @SuppressWarnings("EnumSwitchStatementWhichMissesCases") - private void persistTwoVersionsOfValueSet(ValueSet theValueSet, HttpVerb theVerb) { - theValueSet.setVersion("1"); - theValueSet.setId("ValueSet/vs1"); - theValueSet.getCompose().getInclude().get(0).setVersion("1"); - myExtensionalVsId_v1 = persistSingleValueSet(theValueSet, theVerb); + valueSet.setVersion("1"); + valueSet.setId("ValueSet/vs1"); + valueSet.getCompose().getInclude().get(0).setVersion("1"); + myExtensionalVsId_v1 = persistSingleValueSet(valueSet, HttpVerb.POST); myExtensionalVsIdOnResourceTable_v1 = myValueSetDao.readEntity(myExtensionalVsId_v1, null).getId(); - theValueSet.setVersion("2"); - theValueSet.setId("ValueSet/vs2"); - theValueSet.getCompose().getInclude().get(0).setVersion("2"); - myExtensionalVsId_v2 = persistSingleValueSet(theValueSet, theVerb); + valueSet.setVersion("2"); + valueSet.setId("ValueSet/vs2"); + valueSet.getCompose().getInclude().get(0).setVersion("2"); + myExtensionalVsId_v2 = persistSingleValueSet(valueSet, HttpVerb.POST); myExtensionalVsIdOnResourceTable_v2 = myValueSetDao.readEntity(myExtensionalVsId_v2, null).getId(); } @@ -148,6 +145,9 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide private IIdType persistSingleValueSet(ValueSet theValueSet, HttpVerb theVerb) { final IIdType[] vsId = new IIdType[1]; switch (theVerb) { + case GET: + case HEAD: + break; case POST: new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { @Override @@ -164,6 +164,9 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide } }); break; + case DELETE: + case PATCH: + case NULL: default: throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb); } @@ -188,12 +191,6 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide } -// private void createExternalCsAndLocalVsWithUnknownCode() { -// String codeSystemUrl = createExternalCs(); - -// createLocalVsWithUnknownCode(codeSystemUrl); -// } - private void createLocalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); @@ -250,27 +247,6 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide return myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); } - private void createLocalVsWithUnknownCode(String codeSystemUrl) { - myLocalVs_v1 = new ValueSet(); - myLocalVs_v1.setUrl(URL_MY_VALUE_SET); - myLocalVs_v1.setVersion("1"); - ConceptSetComponent include = myLocalVs_v1.getCompose().addInclude(); - include.setSystem(codeSystemUrl); - include.setSystem("1"); - include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); - myLocalValueSetId_v1 = myValueSetDao.create(myLocalVs_v1, mySrd).getId().toUnqualifiedVersionless(); - - myLocalVs_v2 = new ValueSet(); - myLocalVs_v2.setUrl(URL_MY_VALUE_SET); - myLocalVs_v2.setVersion("2"); - include = myLocalVs_v2.getCompose().addInclude(); - include.setSystem(codeSystemUrl); - include.setSystem("2"); - include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO"); - myLocalValueSetId_v2 = myValueSetDao.create(myLocalVs_v2, mySrd).getId().toUnqualifiedVersionless(); - - } - @Test public void testExpandById() throws Exception { loadAndPersistCodeSystemAndValueSet(); @@ -1201,151 +1177,284 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide assertEquals(23, concept.getOrder()); }); } -/* + @Test - public void testValidateCodeOperationByCodeAndSystemInstance() throws Exception { + public void testValidateCodeOperationByCodeAndSystem() throws Exception { loadAndPersistCodeSystemAndValueSet(); + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationOnInstanceByCodeAndSystem() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // With correct system version specified. Should pass. Parameters respParam = myClient .operation() .onInstance(myExtensionalVsId_v1) .named("validate-code") .withParameter(Parameters.class, "code", new CodeType("8495-4")) .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) .execute(); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - @Test - public void testValidateCodeOperationByCodeAndSystemInstanceOnType() throws IOException { - createLocalCs(); - createLocalVsWithIncludeConcept(); + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .execute(); - String url = ourServerBase + - "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + - UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + - "&code=AA"; + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); - HttpGet request = new HttpGet(url); - request.addHeader("Accept", "application/fhir+json"); - try (CloseableHttpResponse response = ourHttpClient.execute(request)) { - String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); - ourLog.info(respString); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); - assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } - } - - @Test - public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { - createLocalCs(); - createLocalVsWithIncludeConcept(); - - String url = ourServerBase + - "/ValueSet/" + myLocalValueSetId_v1.getIdPart() + "/$validate-code?system=" + - UrlUtil.escapeUrlParam(URL_MY_CODE_SYSTEM) + - "&code=AA"; - - ourLog.info("* Requesting: {}", url); - - HttpGet request = new HttpGet(url); - request.addHeader("Accept", "application/fhir+json"); - try (CloseableHttpResponse response = ourHttpClient.execute(request)) { - String respString = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8); - ourLog.info(respString); - - Parameters respParam = myFhirCtx.newJsonParser().parseResource(Parameters.class, respString); - assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - } - } - - @Test - public void testValidateCodeOperationByCodeAndSystemType() throws Exception { - loadAndPersistCodeSystemAndValueSet(); - - Parameters respParam = myClient + // With incorrect version specified. Should fail. + respParam = myClient .operation() .onInstance(myExtensionalVsId_v1) .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .withParameter(Parameters.class, "code", new CodeType("8495-4")) .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationByCoding() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Coding codingToValidate_v1 = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + codingToValidate_v1.setVersion("1"); + + Coding codingToValidate_v2 = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum v2"); + codingToValidate_v2.setVersion("2"); + + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) .execute(); String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + } @Test - public void testValidateCodeOperationNoValueSetProvided() throws Exception { + public void testValidateCodeOperationByCodeableConcept() throws Exception { loadAndPersistCodeSystemAndValueSet(); - try { - myClient - .operation() - .onType(ValueSet.class) - .named("validate-code") - .withParameter(Parameters.class, "code", new CodeType("8450-9")) - .andParameter("system", new UriType("http://acme.org")) - .execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.", e.getMessage()); - } - } + Coding codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + codingToValidate.setVersion("1"); + CodeableConcept codeableConceptToValidate_v1 = new CodeableConcept(codingToValidate); - @Test - public void testValidateCodeOperationOnInstanceWithIsAExpansion() throws IOException { - CodeSystem cs = new CodeSystem(); - cs.setUrl("http://mycs"); - cs.setContent(CodeSystemContentMode.COMPLETE); - cs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); - cs.setStatus(Enumerations.PublicationStatus.ACTIVE); - ConceptDefinitionComponent parentA = cs.addConcept().setCode("ParentA").setDisplay("Parent A"); - parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); - myCodeSystemDao.create(cs); + codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum v2"); + codingToValidate.setVersion("2"); + CodeableConcept codeableConceptToValidate_v2 = new CodeableConcept(codingToValidate); - ValueSet vs = new ValueSet(); - vs.setUrl("http://myvs"); - vs.getCompose() - .addInclude() - .setSystem("http://mycs") - .addFilter() - .setOp(FilterOperator.ISA) - .setProperty("concept") - .setValue("ParentA"); - IIdType vsId = myValueSetDao.create(vs).getId().toUnqualifiedVersionless(); + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); - HttpGet expandGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$expand?_pretty=true"); - try (CloseableHttpResponse status = ourHttpClient.execute(expandGet)) { - String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); - ourLog.info("Response: {}", response); - } + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); - HttpGet validateCodeGet = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=ChildAA&_pretty=true"); - try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet)) { - String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); - ourLog.info("Response: {}", response); - Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); - assertEquals(true, output.getParameterBool("result")); - } + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - HttpGet validateCodeGet2 = new HttpGet(ourServerBase + "/ValueSet/" + vsId.getIdPart() + "/$validate-code?system=http://mycs&code=FOO&_pretty=true"); - try (CloseableHttpResponse status = ourHttpClient.execute(validateCodeGet2)) { - String response = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); - ourLog.info("Response: {}", response); - Parameters output = myFhirCtx.newXmlParser().parseResource(Parameters.class, response); - assertEquals(false, output.getParameterBool("result")); - } + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); } -*/ private boolean clearDeferredStorageQueue() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java new file mode 100644 index 00000000000..59eead8a901 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java @@ -0,0 +1,1549 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetConcept; +import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode; +import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r5.model.Enumerations.FilterOperator; +import org.hl7.fhir.r4.model.codesystems.HttpVerb; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.Optional; + +import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM; +import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class ResourceProviderR5ValueSetVersionedTest extends BaseResourceProviderR5Test { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR5ValueSetVersionedTest.class); + private IIdType myExtensionalCsId_v1; + private IIdType myExtensionalCsId_v2; + private IIdType myExtensionalVsId_v1; + private IIdType myExtensionalVsId_v2; + private IIdType myLocalValueSetId_v1; + private IIdType myLocalValueSetId_v2; + private Long myExtensionalVsIdOnResourceTable_v1; + private Long myExtensionalVsIdOnResourceTable_v2; + private ValueSet myLocalVs_v1; + private ValueSet myLocalVs_v2; + + private void loadAndPersistCodeSystemAndValueSet() throws IOException { + loadAndPersistCodeSystem(); + loadAndPersistValueSet(); + } + + private void loadAndPersistCodeSystemAndValueSetWithDesignations() throws IOException { + loadAndPersistCodeSystemWithDesignations(); + loadAndPersistValueSet(); + } + + private void loadAndPersistCodeSystem() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + persistCodeSystem(codeSystem); + } + + private void loadAndPersistCodeSystemWithDesignations() throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-with-designations.xml"); + persistCodeSystem(codeSystem); + } + + private void persistCodeSystem(CodeSystem theCodeSystem) { + theCodeSystem.setId("CodeSystem/cs1"); + theCodeSystem.setVersion("1"); + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myExtensionalCsId_v1 = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myExtensionalCsId_v1, null).getId(); + + theCodeSystem.setId("CodeSystem/cs2"); + theCodeSystem.setVersion("2"); + for(ConceptDefinitionComponent conceptDefinitionComponent : theCodeSystem.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v2"); + } + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + myExtensionalCsId_v2 = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless(); + } + }); + myCodeSystemDao.readEntity(myExtensionalCsId_v2, null).getId(); + + } + + private void loadAndPersistValueSet() throws IOException { + ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + valueSet.setVersion("1"); + valueSet.setId("ValueSet/vs1"); + valueSet.getCompose().getInclude().get(0).setVersion("1"); + myExtensionalVsId_v1 = persistSingleValueSet(valueSet, HttpVerb.POST); + myExtensionalVsIdOnResourceTable_v1 = myValueSetDao.readEntity(myExtensionalVsId_v1, null).getId(); + + valueSet.setVersion("2"); + valueSet.setId("ValueSet/vs2"); + valueSet.getCompose().getInclude().get(0).setVersion("2"); + myExtensionalVsId_v2 = persistSingleValueSet(valueSet, HttpVerb.POST); + myExtensionalVsIdOnResourceTable_v2 = myValueSetDao.readEntity(myExtensionalVsId_v2, null).getId(); + + } + + private IIdType persistSingleValueSet(ValueSet theValueSet, HttpVerb theVerb) { + final IIdType[] vsId = new IIdType[1]; + switch (theVerb) { + case GET: + case HEAD: + break; + case POST: + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + vsId[0] = myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + }); + break; + case PUT: + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { + vsId[0] = myValueSetDao.update(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + }); + break; + case DELETE: + case PATCH: + case NULL: + default: + throw new IllegalArgumentException("HTTP verb is not supported: " + theVerb); + } + return vsId[0]; + } + + private String createExternalCs(String theCodeSystemVersion) { + IFhirResourceDao codeSystemDao = myCodeSystemDao; + IResourceTableDao resourceTableDao = myResourceTableDao; + + return createExternalCs(codeSystemDao, resourceTableDao, myTermCodeSystemStorageSvc, mySrd, theCodeSystemVersion).getUrl(); + } + + private void createExternalCsAndLocalVs() { + String codeSystemUrl = createExternalCs("1"); + myLocalVs_v1 = createLocalVs(codeSystemUrl, "1"); + myLocalValueSetId_v1 = persistLocalVs(myLocalVs_v1); + + codeSystemUrl = createExternalCs("2"); + myLocalVs_v2 = createLocalVs(codeSystemUrl, "2"); + myLocalValueSetId_v2 = persistLocalVs(myLocalVs_v2); + + } + + private void createLocalCs() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystemContentMode.COMPLETE); + codeSystem + .addConcept().setCode("A").setDisplay("Code A") + .addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") + .addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) + ) + .addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); + codeSystem + .addConcept().setCode("B").setDisplay("Code B") + .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) + .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); + myCodeSystemDao.create(codeSystem, mySrd); + } + + private void createLocalVsWithIncludeConcept() { + myLocalVs_v1 = new ValueSet(); + myLocalVs_v1.setUrl(URL_MY_VALUE_SET); + myLocalVs_v1.setVersion("1"); + ConceptSetComponent include = myLocalVs_v1.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.setVersion("1"); + include.addConcept().setCode("A").setDisplay("A v1"); + include.addConcept().setCode("AA").setDisplay("AA v1"); + myLocalValueSetId_v1 = myValueSetDao.create(myLocalVs_v1, mySrd).getId().toUnqualifiedVersionless(); + + myLocalVs_v2 = new ValueSet(); + myLocalVs_v2.setUrl(URL_MY_VALUE_SET); + myLocalVs_v2.setVersion("2"); + include = myLocalVs_v2.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.setVersion("2"); + include.addConcept().setCode("A").setDisplay("A v2"); + include.addConcept().setCode("AA").setDisplay("AA v2"); + myLocalValueSetId_v2 = myValueSetDao.create(myLocalVs_v2, mySrd).getId().toUnqualifiedVersionless(); + + } + + private ValueSet createLocalVs(String theCodeSystemUrl, String theValueSetVersion) { + ValueSet myLocalVs = new ValueSet(); + myLocalVs.setUrl(URL_MY_VALUE_SET); + myLocalVs.setVersion(theValueSetVersion); + ConceptSetComponent include = myLocalVs.getCompose().addInclude(); + include.setSystem(theCodeSystemUrl); + include.setVersion(theValueSetVersion); + include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA"); + return myLocalVs; + + } + + private IIdType persistLocalVs(ValueSet theValueSet) { + return myValueSetDao.create(theValueSet, mySrd).getId().toUnqualifiedVersionless(); + } + + @Test + public void testExpandById() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Test with v1 of ValueSet + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + // Test with v2 of ValueSet + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + } + + @Test + public void testExpandByIdWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Verify v1 ValueSet + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + // Verify v2 ValueSet + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + assertThat(resp, containsString("")); + + } + + @Test + public void testExpandByIdWithFilter() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Verify ValueSet v1 + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + // Verify ValueSet v2 + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + } + + @Test + public void testExpandByIdWithFilterWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Validate ValueSet v1 + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + // Validate ValueSet v2 + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("expand") + .withParameter(Parameters.class, "filter", new StringType("first")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, containsString("")); + assertThat(resp, not(containsString(""))); + + } + + @Test + public void testExpandByUrlAndVersion() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithBogusVersion() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + try { + myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("3")) + .execute(); + } catch (ResourceNotFoundException e) { + assertEquals(404, e.getStatusCode()); + assertEquals("HTTP 404 Not Found: Unknown ValueSet: http%3A%2F%2Fwww.healthintersections.com.au%2Ffhir%2FValueSet%2Fextensional-case-2%7C3", e.getMessage()); + } + } + + @Test + public void testExpandByUrlWithPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + await().until(() -> clearDeferredStorageQueue()); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + Slice page = myTermValueSetDao.findByExpansionStatus(PageRequest.of(0, 10), TermValueSetPreExpansionStatusEnum.EXPANDED); + assertEquals(2, page.getContent().size()); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByUrlWithPreExpansionAndBogusVersion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSet(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + try { + myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("3")) + .execute(); + } catch (ResourceNotFoundException e) { + assertEquals(404, e.getStatusCode()); + assertEquals("HTTP 404 Not Found: Unknown ValueSet: http%3A%2F%2Fwww.healthintersections.com.au%2Ffhir%2FValueSet%2Fextensional-case-2%7C3", e.getMessage()); + } + } + + @Test + public void testExpandByValueSet() throws IOException { + loadAndPersistCodeSystem(); + + // Test with no version specified + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last updated. + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 1 specified. + toExpand.setVersion("1"); + toExpand.setId("ValueSet/vs1"); + toExpand.getCompose().getInclude().get(0).setVersion("1"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v1. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 2 specified. + toExpand.setVersion("2"); + toExpand.setId("ValueSet/vs2"); + toExpand.getCompose().getInclude().get(0).setVersion("2"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + + @Test + public void testExpandByValueSetWithPreExpansion() throws IOException { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystem(); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + // Test with no version specified + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last updated. + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 1 specified. + toExpand.setVersion("1"); + toExpand.setId("ValueSet/vs1"); + toExpand.getCompose().getInclude().get(0).setVersion("1"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v1. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Test with version 2 specified. + toExpand.setVersion("2"); + toExpand.setId("ValueSet/vs2"); + toExpand.getCompose().getInclude().get(0).setVersion("2"); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + + } + + @Test + public void testExpandInlineVsAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalVs_v1); + assertNotNull(myLocalVs_v2); + + myLocalVs_v1.setId(""); + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs_v1) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + myLocalVs_v2.setId(""); + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", myLocalVs_v2) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId_v1); + assertNotNull(myLocalValueSetId_v2); + + // Validate ValueSet v1 + Parameters respParam = myClient + .operation() + .onInstance(myLocalValueSetId_v1) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + // Validate ValueSet v2 + respParam = myClient + .operation() + .onInstance(myLocalValueSetId_v2) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandLocalVsCanonicalAgainstExternalCs() { + createExternalCsAndLocalVs(); + assertNotNull(myLocalValueSetId_v1); + assertNotNull(myLocalValueSetId_v2); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType(URL_MY_VALUE_SET)) + .execute(); + + // Canonical expand should only return most recently updated version, v2. + assertEquals(1, respParam.getParameter().size()); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, containsStringIgnoringCase("")); + assertThat(resp, not(containsStringIgnoringCase(""))); + + } + + @Test + public void testExpandValueSetBasedOnCodeSystemWithChangedUrlAndVersion() { + + CodeSystem cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo1"); + cs.setVersion("1"); + cs.addConcept().setCode("foo1").setDisplay("foo1"); + myClient.update().resource(cs).execute(); + + ValueSet vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo1").setVersion("1").addConcept().setCode("foo1"); + myClient.update().resource(vs).execute(); + + ValueSet expanded = myClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + + // Update the CodeSystem Version and Codes + cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo2"); + cs.setVersion("2"); + cs.addConcept().setCode("foo2").setDisplay("foo2"); + myClient.update().resource(cs).execute(); + + vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo2").setVersion("2").addConcept().setCode("foo2"); + myClient.update().resource(vs).execute(); + + expanded = myClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + } + + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(); + + CodeSystem codeSystem_v1 = myCodeSystemDao.read(myExtensionalCsId_v1); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v1)); + CodeSystem codeSystem_v2 = myCodeSystemDao.read(myExtensionalCsId_v2); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v2)); + + ValueSet valueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v1)); + ValueSet valueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v2)); + + String initialValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + String initialValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + await().until(() -> clearDeferredStorageQueue()); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); + + ValueSet updatedValueSet_v1 = valueSet_v1; + updatedValueSet_v1.setName(valueSet_v1.getName().concat(" - MODIFIED")); + persistSingleValueSet(updatedValueSet_v1, HttpVerb.PUT); + updatedValueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v1)); + + String updatedValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v1,"1", myExtensionalVsIdOnResourceTable_v1); + + ValueSet updatedValueSet_v2 = valueSet_v2; + updatedValueSet_v2.setName(valueSet_v2.getName().concat(" - MODIFIED")); + persistSingleValueSet(updatedValueSet_v2, HttpVerb.PUT); + updatedValueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v2)); + + String updatedValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v2,"2", myExtensionalVsIdOnResourceTable_v2); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(updatedValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(updatedValueSetName_v2, codeSystem_v2); + } + + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(); + + CodeSystem codeSystem_v1 = myCodeSystemDao.read(myExtensionalCsId_v1); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v1)); + CodeSystem codeSystem_v2 = myCodeSystemDao.read(myExtensionalCsId_v2); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem_v2)); + + ValueSet valueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v1)); + ValueSet valueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet_v2)); + + String initialValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + String initialValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(initialValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + await().until(() -> clearDeferredStorageQueue()); + validateTermValueSetExpandedAndChildrenV1(initialValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(initialValueSetName_v2, codeSystem_v2); + + ValueSet updatedValueSet_v1 = valueSet_v1; + updatedValueSet_v1.setName(valueSet_v1.getName().concat(" - MODIFIED")); + + String url = myClient.getServerBase().concat("/").concat(myExtensionalVsId_v1.getValueAsString()); + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(url) + .setResource(updatedValueSet_v1) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(url); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + myClient.transaction().withBundle(bundle).execute(); + + updatedValueSet_v1 = myValueSetDao.read(myExtensionalVsId_v1); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v1)); + + String updatedValueSetName_v1 = valueSet_v1.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v1, "1", myExtensionalVsIdOnResourceTable_v1); + + ValueSet updatedValueSet_v2 = valueSet_v2; + updatedValueSet_v2.setName(valueSet_v2.getName().concat(" - MODIFIED")); + + url = myClient.getServerBase().concat("/").concat(myExtensionalVsId_v2.getValueAsString()); + bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(url) + .setResource(updatedValueSet_v2) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(url); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + myClient.transaction().withBundle(bundle).execute(); + + updatedValueSet_v2 = myValueSetDao.read(myExtensionalVsId_v2); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet_v2)); + + String updatedValueSetName_v2 = valueSet_v2.getName(); + validateTermValueSetNotExpanded(updatedValueSetName_v2, "2", myExtensionalVsIdOnResourceTable_v2); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildrenV1(updatedValueSetName_v1, codeSystem_v1); + validateTermValueSetExpandedAndChildrenV2(updatedValueSetName_v2, codeSystem_v2); + + } + + private void validateTermValueSetNotExpanded(String theValueSetName, String theVersion, Long theId) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(theId); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", theVersion); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + }); + } + + private void validateTermValueSetExpandedAndChildrenV1(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable_v1); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "1"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|1", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|1", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|1", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|1", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } + + private void validateTermValueSetExpandedAndChildrenV2(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(() -> { + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable_v2); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findTermValueSetByUrlAndVersion("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", "2"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|2", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration v2", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|2", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter v2", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|2", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum v2", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org|2", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum v2", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } + + @Test + public void testValidateCodeOperationByCodeAndSystem() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationOnInstanceByCodeAndSystem() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v1) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onInstance(myExtensionalVsId_v2) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeType("8495-4")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("systemVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationByCoding() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Coding codingToValidate_v1 = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + codingToValidate_v1.setVersion("1"); + + Coding codingToValidate_v2 = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum v2"); + codingToValidate_v2.setVersion("2"); + + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + @Test + public void testValidateCodeOperationByCodeableConcept() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Coding codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + codingToValidate.setVersion("1"); + CodeableConcept codeableConceptToValidate_v1 = new CodeableConcept(codingToValidate); + + codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum v2"); + codingToValidate.setVersion("2"); + CodeableConcept codeableConceptToValidate_v2 = new CodeableConcept(codingToValidate); + + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + // With incorrect version specified. Should fail. + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v1) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "codeableConcept", codeableConceptToValidate_v2) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertFalse(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + private boolean clearDeferredStorageQueue() { + + if(!myTermDeferredStorageSvc.isStorageQueueEmpty()) { + myTermDeferredStorageSvc.saveAllDeferred(); + return false; + } else { + return true; + } + + } + + @Test + public void testCreateDuplicatValueSetVersion() { + createExternalCsAndLocalVs(); + try { + persistLocalVs(createLocalVs(URL_MY_CODE_SYSTEM, "1")); + fail(); + } catch (UnprocessableEntityException theE) { + assertThat(theE.getMessage(), containsString("Can not create multiple ValueSet resources with ValueSet.url \"" + URL_MY_VALUE_SET + "\" and ValueSet.version \"1\", already have one with resource ID: ")); + } + + } + + @AfterEach + public void afterResetPreExpansionDefault() { + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); + } + + public CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion("SYSTEM VERSION"); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA"); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA"); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB"); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB"); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + return codeSystem; + } + + public static CodeSystem createExternalCs(IFhirResourceDao theCodeSystemDao, IResourceTableDao theResourceTableDao, ITermCodeSystemStorageSvc theTermCodeSystemStorageSvc, ServletRequestDetails theRequestDetails, String theCodeSystemVersion) { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setVersion(theCodeSystemVersion); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified(); + + ResourceTable table = theResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A" + theCodeSystemVersion); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA" + theCodeSystemVersion); + parentA.addChild(childAA, RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA" + theCodeSystemVersion); + childAA.addChild(childAAA, RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB" + theCodeSystemVersion); + childAA.addChild(childAAB, RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB" + theCodeSystemVersion); + parentA.addChild(childAB, RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B" + theCodeSystemVersion); + cs.getConcepts().add(parentB); + + theTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", theCodeSystemVersion, cs, table); + return codeSystem; + } + +} 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 b447c39e160..4028f33c450 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,30 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { return url; } + public static String getValueSetVersion(@Nonnull IBaseResource theValueSet) { + String version; + switch (theValueSet.getStructureFhirVersionEnum()) { + case DSTU3: { + version = ((org.hl7.fhir.dstu3.model.ValueSet) theValueSet).getVersion(); + break; + } + case R4: { + version = ((org.hl7.fhir.r4.model.ValueSet) theValueSet).getVersion(); + break; + } + case R5: { + version = ((org.hl7.fhir.r5.model.ValueSet) theValueSet).getVersion(); + break; + } + case DSTU2: + case DSTU2_HL7ORG: + case DSTU2_1: + default: + throw new IllegalArgumentException("Can not handle version: " + theValueSet.getStructureFhirVersionEnum()); + } + return version; + } + private static HashMap buildUspsCodes() { HashMap uspsCodes = new HashMap<>(); uspsCodes.put("AK", "Alaska"); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 3fca6d034ac..4704912f7fb 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -91,23 +91,23 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu return new ValueSetExpansionOutcome(expansion, null); } - private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystem, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { org.hl7.fhir.r5.model.ValueSet expansionR5; switch (theValueSetToExpand.getStructureFhirVersionEnum()) { case DSTU2: { - expansionR5 = expandValueSetDstu2(theValidationSupportContext, (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSetToExpand, theWantSystem, theWantCode); + expansionR5 = expandValueSetDstu2(theValidationSupportContext, (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); break; } case DSTU2_HL7ORG: { - expansionR5 = expandValueSetDstu2Hl7Org(theValidationSupportContext, (ValueSet) theValueSetToExpand, theWantSystem, theWantCode); + expansionR5 = expandValueSetDstu2Hl7Org(theValidationSupportContext, (ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); break; } case DSTU3: { - expansionR5 = expandValueSetDstu3(theValidationSupportContext, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystem, theWantCode); + expansionR5 = expandValueSetDstu3(theValidationSupportContext, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); break; } case R4: { - expansionR5 = expandValueSetR4(theValidationSupportContext, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystem, theWantCode); + expansionR5 = expandValueSetR4(theValidationSupportContext, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); break; } case R5: { @@ -119,20 +119,17 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu throw new IllegalArgumentException("Can not handle version: " + myCtx.getVersion().getVersion()); } - if (expansionR5 == null) { - return null; - } return expansionR5; } @Override public CodeValidationResult - validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { - org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystem, theCode); + validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemIdentifier, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { + org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemIdentifier, theCode); if (expansion == null) { return null; } - return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, expansion); + return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemIdentifier, theCode, theDisplay, expansion); } @@ -145,26 +142,59 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu return null; } } else { + String codeSystemUrl; + String codeSystemVersion = null; + int codeSystemVersionIndex = theCodeSystem.indexOf("|"); + if (codeSystemVersionIndex > -1) { + codeSystemUrl = theCodeSystem.substring(0, codeSystemVersionIndex); + codeSystemVersion = theCodeSystem.substring(codeSystemVersionIndex + 1); + } else { + codeSystemUrl = theCodeSystem; + } switch (myCtx.getVersion().getVersion()) { case DSTU2_HL7ORG: - vs = new org.hl7.fhir.dstu2.model.ValueSet() - .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + if (codeSystemVersion != null) { + vs = new org.hl7.fhir.dstu2.model.ValueSet() + .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); + } else { + vs = new org.hl7.fhir.dstu2.model.ValueSet() + .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + } break; case DSTU3: - vs = new org.hl7.fhir.dstu3.model.ValueSet() - .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + if (codeSystemVersion != null) { + vs = new org.hl7.fhir.dstu3.model.ValueSet() + .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); + } else { + vs = new org.hl7.fhir.dstu3.model.ValueSet() + .setCompose(new org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + } break; case R4: - vs = new org.hl7.fhir.r4.model.ValueSet() - .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + if (codeSystemVersion != null) { + vs = new org.hl7.fhir.r4.model.ValueSet() + .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); + } else { + vs = new org.hl7.fhir.r4.model.ValueSet() + .setCompose(new org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + } break; case R5: - vs = new org.hl7.fhir.r5.model.ValueSet() - .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + if (codeSystemVersion != null) { + vs = new org.hl7.fhir.r5.model.ValueSet() + .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); + } else { + vs = new org.hl7.fhir.r5.model.ValueSet() + .setCompose(new org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); + } break; case DSTU2: case DSTU2_1: @@ -184,39 +214,39 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } - private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, IBaseResource theExpansion) { + private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemIdentifierToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion) { assert theExpansion != null; boolean caseSensitive = true; - IBaseResource system = null; - if (!theOptions.isInferSystem() && isNotBlank(theCodeSystem)) { - system = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystem); + IBaseResource codeSystemToValidateResource = null; + if (!theOptions.isInferSystem() && isNotBlank(theCodeSystemIdentifierToValidate)) { + codeSystemToValidateResource = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystemIdentifierToValidate); } - List codes = new ArrayList<>(); + List codesInValueSetExpansion = new ArrayList<>(); switch (theExpansion.getStructureFhirVersionEnum()) { case DSTU2_HL7ORG: { ValueSet expansionVs = (ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesDstu2(contains, codes); + flattenAndConvertCodesDstu2(contains, codesInValueSetExpansion); break; } case DSTU3: { org.hl7.fhir.dstu3.model.ValueSet expansionVs = (org.hl7.fhir.dstu3.model.ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesDstu3(contains, codes); + flattenAndConvertCodesDstu3(contains, codesInValueSetExpansion); break; } case R4: { org.hl7.fhir.r4.model.ValueSet expansionVs = (org.hl7.fhir.r4.model.ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesR4(contains, codes); + flattenAndConvertCodesR4(contains, codesInValueSetExpansion); break; } case R5: { org.hl7.fhir.r5.model.ValueSet expansionVs = (org.hl7.fhir.r5.model.ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesR5(contains, codes); + flattenAndConvertCodesR5(contains, codesInValueSetExpansion); break; } case DSTU2: @@ -225,37 +255,37 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu throw new IllegalArgumentException("Can not handle version: " + myCtx.getVersion().getVersion()); } - String codeSystemName = null; - String codeSystemVersion = null; - String codeSystemContentMode = null; - if (system != null) { - switch (system.getStructureFhirVersionEnum()) { + String codeSystemResourceName = null; + String codeSystemResourceVersion = null; + String codeSystemResourceContentMode = null; + if (codeSystemToValidateResource != null) { + switch (codeSystemToValidateResource.getStructureFhirVersionEnum()) { case DSTU2_HL7ORG: { caseSensitive = true; break; } case DSTU3: { - org.hl7.fhir.dstu3.model.CodeSystem systemDstu3 = (org.hl7.fhir.dstu3.model.CodeSystem) system; + org.hl7.fhir.dstu3.model.CodeSystem systemDstu3 = (org.hl7.fhir.dstu3.model.CodeSystem) codeSystemToValidateResource; caseSensitive = systemDstu3.getCaseSensitive(); - codeSystemName = systemDstu3.getName(); - codeSystemVersion = systemDstu3.getVersion(); - codeSystemContentMode = systemDstu3.getContentElement().getValueAsString(); + codeSystemResourceName = systemDstu3.getName(); + codeSystemResourceVersion = systemDstu3.getVersion(); + codeSystemResourceContentMode = systemDstu3.getContentElement().getValueAsString(); break; } case R4: { - org.hl7.fhir.r4.model.CodeSystem systemR4 = (org.hl7.fhir.r4.model.CodeSystem) system; + org.hl7.fhir.r4.model.CodeSystem systemR4 = (org.hl7.fhir.r4.model.CodeSystem) codeSystemToValidateResource; caseSensitive = systemR4.getCaseSensitive(); - codeSystemName = systemR4.getName(); - codeSystemVersion = systemR4.getVersion(); - codeSystemContentMode = systemR4.getContentElement().getValueAsString(); + codeSystemResourceName = systemR4.getName(); + codeSystemResourceVersion = systemR4.getVersion(); + codeSystemResourceContentMode = systemR4.getContentElement().getValueAsString(); break; } case R5: { - CodeSystem systemR5 = (CodeSystem) system; + CodeSystem systemR5 = (CodeSystem) codeSystemToValidateResource; caseSensitive = systemR5.getCaseSensitive(); - codeSystemName = systemR5.getName(); - codeSystemVersion = systemR5.getVersion(); - codeSystemContentMode = systemR5.getContentElement().getValueAsString(); + codeSystemResourceName = systemR5.getName(); + codeSystemResourceVersion = systemR5.getVersion(); + codeSystemResourceContentMode = systemR5.getContentElement().getValueAsString(); break; } case DSTU2: @@ -265,29 +295,40 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } } - for (VersionIndependentConcept nextExpansionCode : codes) { + String codeSystemUrlToValidate=null; + String codeSystemVersionToValidate=null; + if (theCodeSystemIdentifierToValidate != null) { + int versionIndex = theCodeSystemIdentifierToValidate.indexOf("|"); + if (versionIndex > -1) { + codeSystemUrlToValidate = theCodeSystemIdentifierToValidate.substring(0, versionIndex); + codeSystemVersionToValidate = theCodeSystemIdentifierToValidate.substring(versionIndex+1); + } else { + codeSystemUrlToValidate = theCodeSystemIdentifierToValidate; + } + } + for (VersionIndependentConcept nextExpansionCode : codesInValueSetExpansion) { boolean codeMatches; if (caseSensitive) { - codeMatches = defaultString(theCode).equals(nextExpansionCode.getCode()); + codeMatches = defaultString(theCodeToValidate).equals(nextExpansionCode.getCode()); } else { - codeMatches = defaultString(theCode).equalsIgnoreCase(nextExpansionCode.getCode()); + codeMatches = defaultString(theCodeToValidate).equalsIgnoreCase(nextExpansionCode.getCode()); } if (codeMatches) { - if (theOptions.isInferSystem() || nextExpansionCode.getSystem().equals(theCodeSystem)) { - if (!theOptions.isValidateDisplay() || (isBlank(nextExpansionCode.getDisplay()) || isBlank(theDisplay) || nextExpansionCode.getDisplay().equals(theDisplay))) { + if (theOptions.isInferSystem() || (nextExpansionCode.getSystem().equals(codeSystemUrlToValidate) && (codeSystemVersionToValidate == null || codeSystemVersionToValidate.equals(nextExpansionCode.getSystemVersion())))) { + if (!theOptions.isValidateDisplay() || (isBlank(nextExpansionCode.getDisplay()) || isBlank(theDisplayToValidate) || nextExpansionCode.getDisplay().equals(theDisplayToValidate))) { return new CodeValidationResult() - .setCode(theCode) + .setCode(theCodeToValidate) .setDisplay(nextExpansionCode.getDisplay()) - .setCodeSystemName(codeSystemName) - .setCodeSystemVersion(codeSystemVersion); + .setCodeSystemName(codeSystemResourceName) + .setCodeSystemVersion(codeSystemResourceVersion); } else { return new CodeValidationResult() .setSeverity(IssueSeverity.ERROR) .setDisplay(nextExpansionCode.getDisplay()) - .setMessage("Concept Display \"" + theDisplay + "\" does not match expected \"" + nextExpansionCode.getDisplay() + "\"") - .setCodeSystemName(codeSystemName) - .setCodeSystemVersion(codeSystemVersion); + .setMessage("Concept Display \"" + theDisplayToValidate + "\" does not match expected \"" + nextExpansionCode.getDisplay() + "\"") + .setCodeSystemName(codeSystemResourceName) + .setCodeSystemVersion(codeSystemResourceVersion); } } } @@ -295,12 +336,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu ValidationMessage.IssueSeverity severity; String message; - if ("fragment".equals(codeSystemContentMode)) { + if ("fragment".equals(codeSystemResourceContentMode)) { severity = ValidationMessage.IssueSeverity.WARNING; - message = "Unknown code in fragment CodeSystem '" + (isNotBlank(theCodeSystem) ? theCodeSystem + "#" : "") + theCode + "'"; + message = "Unknown code in fragment CodeSystem '" + (isNotBlank(theCodeSystemIdentifierToValidate) ? theCodeSystemIdentifierToValidate + "#" : "") + theCodeToValidate + "'"; } else { severity = ValidationMessage.IssueSeverity.ERROR; - message = "Unknown code '" + (isNotBlank(theCodeSystem) ? theCodeSystem + "#" : "") + theCode + "'"; + message = "Unknown code '" + (isNotBlank(theCodeSystemIdentifierToValidate) ? theCodeSystemIdentifierToValidate + "#" : "") + theCodeToValidate + "'"; } return new CodeValidationResult() @@ -314,7 +355,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { Function codeSystemLoader = t -> { org.hl7.fhir.dstu2.model.ValueSet codeSystem = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); CodeSystem retVal = new CodeSystem(); @@ -327,12 +368,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu }; org.hl7.fhir.r5.model.ValueSet input = ValueSet10_50.convertValueSet(theInput); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); return (output); } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser(); IParser parserHapi = FhirContext.forCached(FhirVersionEnum.DSTU2).newJsonParser(); @@ -355,7 +396,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(theInput)); org.hl7.fhir.r5.model.ValueSet input = ValueSet10_50.convertValueSet(valueSetRi); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); return (output); } @@ -404,7 +445,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { Function codeSystemLoader = t -> { org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); return CodeSystem30_50.convertCodeSystem(codeSystem); @@ -415,12 +456,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu }; org.hl7.fhir.r5.model.ValueSet input = ValueSet30_50.convertValueSet(theInput); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); return (output); } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystem, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { Function codeSystemLoader = t -> { org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); return CodeSystem40_50.convertCodeSystem(codeSystem); @@ -431,7 +472,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu }; org.hl7.fhir.r5.model.ValueSet input = ValueSet40_50.convertValueSet(theInput); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystem, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); return (output); } @@ -444,12 +485,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, Function theCodeSystemLoader, Function theValueSetLoader, @Nullable String theWantSystem, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, Function theCodeSystemLoader, Function theValueSetLoader, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { Set concepts = new HashSet<>(); try { - expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystem, theWantCode); - expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false, theWantSystem, theWantCode); + expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystemIdentifier, theWantCode); + expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false, theWantSystemIdentifier, theWantCode); } catch (ExpansionCouldNotBeCompletedInternallyException e) { return null; } @@ -460,23 +501,46 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu contains.setSystem(next.getSystem()); contains.setCode(next.getCode()); contains.setDisplay(next.getDisplay()); + contains.setVersion(next.getSystemVersion()); } return retVal; } - private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set theConcepts, Function theCodeSystemLoader, Function theValueSetLoader, List theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystem, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { + private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set theConcepts, Function theCodeSystemLoader, Function theValueSetLoader, List theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { + String wantSystemUrl = null; + String wantSystemVersion = null; + if (theWantSystemIdentifier != null) { + int versionIndex = theWantSystemIdentifier.indexOf("|"); + if (versionIndex > -1) { + wantSystemUrl = theWantSystemIdentifier.substring(0,versionIndex); + wantSystemVersion = theWantSystemIdentifier.substring(versionIndex+1); + } else { + wantSystemUrl = theWantSystemIdentifier; + } + } + for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) { List nextCodeList = new ArrayList<>(); - String system = nextInclude.getSystem(); - if (isNotBlank(system)) { + String includeOrExcludeConceptSystemUrl = nextInclude.getSystem(); + String includeOrExcludeConceptSystemVersion = nextInclude.getVersion(); + if (isNotBlank(includeOrExcludeConceptSystemUrl)) { - if (theWantSystem != null && !theWantSystem.equals(system)) { + if (wantSystemUrl != null && !wantSystemUrl.equals(includeOrExcludeConceptSystemUrl)) { continue; } - CodeSystem codeSystem = theCodeSystemLoader.apply(system); + if (wantSystemVersion != null && !wantSystemVersion.equals(includeOrExcludeConceptSystemVersion)) { + continue; + } + + CodeSystem includeOrExcludeSystemResource; + if (includeOrExcludeConceptSystemVersion != null) { + includeOrExcludeSystemResource = theCodeSystemLoader.apply(includeOrExcludeConceptSystemUrl + "|" + includeOrExcludeConceptSystemVersion); + } else { + includeOrExcludeSystemResource = theCodeSystemLoader.apply(includeOrExcludeConceptSystemUrl); + } Set wantCodes; if (nextInclude.getConcept().isEmpty()) { @@ -488,18 +552,18 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } boolean ableToHandleCode = false; - if (codeSystem == null || codeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) { + if (includeOrExcludeSystemResource == null || includeOrExcludeSystemResource.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT) { if (theWantCode != null) { - if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, system)) { - LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, system, theWantCode); + if (theValidationSupportContext.getRootValidationSupport().isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) { + LookupCodeResult lookup = theValidationSupportContext.getRootValidationSupport().lookupCode(theValidationSupportContext, includeOrExcludeConceptSystemUrl, theWantCode); if (lookup != null && lookup.isFound()) { CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() .addConcept() .setCode(theWantCode) .setDisplay(lookup.getCodeDisplay()); List codesList = Collections.singletonList(conceptDefinition); - addCodes(system, codesList, nextCodeList, wantCodes); + addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes); ableToHandleCode = true; } } else if (theComposeListIsInclude) { @@ -514,7 +578,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu * enumerate a set of good codes for them is a nice compromise there. */ for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent next : theComposeList) { - if (Objects.equals(next.getSystem(), theWantSystem)) { + if (Objects.equals(next.getSystem(), theWantSystemIdentifier)) { Optional matchingEnumeratedConcept = next.getConcept().stream().filter(t -> Objects.equals(t.getCode(), theWantCode)).findFirst(); if (matchingEnumeratedConcept.isPresent()) { CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() @@ -522,7 +586,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu .setCode(theWantCode) .setDisplay(matchingEnumeratedConcept.get().getDisplay()); List codesList = Collections.singletonList(conceptDefinition); - addCodes(system, codesList, nextCodeList, wantCodes); + addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, codesList, nextCodeList, wantCodes); ableToHandleCode = true; break; } @@ -540,8 +604,8 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu throw new ExpansionCouldNotBeCompletedInternallyException(); } - if (codeSystem != null && codeSystem.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) { - addCodes(system, codeSystem.getConcept(), nextCodeList, wantCodes); + if (includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() != CodeSystem.CodeSystemContentMode.NOTPRESENT) { + addCodes(includeOrExcludeConceptSystemUrl, includeOrExcludeConceptSystemVersion, includeOrExcludeSystemResource.getConcept(), nextCodeList, wantCodes); } } @@ -549,12 +613,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu for (CanonicalType nextValueSetInclude : nextInclude.getValueSet()) { org.hl7.fhir.r5.model.ValueSet vs = theValueSetLoader.apply(nextValueSetInclude.getValueAsString()); if (vs != null) { - org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theCodeSystemLoader, theValueSetLoader, theWantSystem, theWantCode); + org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theCodeSystemLoader, theValueSetLoader, theWantSystemIdentifier, theWantCode); if (subExpansion == null) { throw new ExpansionCouldNotBeCompletedInternallyException(); } for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) { - nextCodeList.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); + nextCodeList.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); } } } @@ -569,14 +633,14 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } - private void addCodes(String theSystem, List theSource, List theTarget, Set theCodeFilter) { + private void addCodes(String theCodeSystemUrl, String theCodeSystemVersion, List theSource, List theTarget, Set theCodeFilter) { for (CodeSystem.ConceptDefinitionComponent next : theSource) { if (isNotBlank(next.getCode())) { if (theCodeFilter == null || theCodeFilter.contains(next.getCode())) { - theTarget.add(new VersionIndependentConcept(theSystem, next.getCode(), next.getDisplay())); + theTarget.add(new VersionIndependentConcept(theCodeSystemUrl, next.getCode(), next.getDisplay(), theCodeSystemVersion)); } } - addCodes(theSystem, next.getConcept(), theTarget, theCodeFilter); + addCodes(theCodeSystemUrl, theCodeSystemVersion, next.getConcept(), theTarget, theCodeFilter); } } @@ -593,21 +657,21 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu private static void flattenAndConvertCodesDstu3(List theInput, List theVersionIndependentConcepts) { for (org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); + theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); flattenAndConvertCodesDstu3(next.getContains(), theVersionIndependentConcepts); } } private static void flattenAndConvertCodesR4(List theInput, List theVersionIndependentConcepts) { for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); + theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); flattenAndConvertCodesR4(next.getContains(), theVersionIndependentConcepts); } } private static void flattenAndConvertCodesR5(List theInput, List theVersionIndependentConcepts) { for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); + theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); flattenAndConvertCodesR5(next.getContains(), theVersionIndependentConcepts); } } From 2bceacd9a6cacae20259445d7e7961072fe8e017 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Fri, 25 Sep 2020 08:51:53 -0400 Subject: [PATCH 51/71] Update loader to ensure that ConceptMap and ValueSet Ids are unique for each version loaded. --- .../dstu3/BaseJpaResourceProviderCodeSystemDstu3.java | 9 --------- .../ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java | 4 +++- .../term/loinc/BaseLoincTop2000LabResultsHandler.java | 3 ++- .../uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java | 2 +- .../jpa/term/loinc/LoincDocumentOntologyHandler.java | 4 +++- .../uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java | 5 +++-- .../fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java | 3 ++- .../term/loinc/LoincIeeeMedicalDeviceCodeHandler.java | 3 ++- .../jpa/term/loinc/LoincImagingDocumentCodeHandler.java | 3 ++- .../fhir/jpa/term/loinc/LoincParentGroupFileHandler.java | 3 ++- .../term/loinc/LoincPartRelatedCodeMappingHandler.java | 3 ++- .../fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java | 5 +++-- .../jpa/term/loinc/LoincUniversalOrderSetHandler.java | 3 ++- 13 files changed, 27 insertions(+), 23 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index 9c522e5cd81..b4ede03ec9a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -64,9 +64,6 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD IValidationSupport.LookupCodeResult result; if (theVersion != null) { result = dao.lookupCode(theCode, new UriType(theSystem.getValue() + "|" + theVersion), theCoding, theRequestDetails); - } else if (theCoding != null && theCoding.hasVersion()) { - Coding codingWithVersion = new Coding(theCoding.getSystem() + "|" + theCoding.getVersion(), theCoding.getCode(), theCoding.getDisplay()); - result = dao.lookupCode(theCode, theSystem, codingWithVersion, theRequestDetails); } else { result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); } @@ -104,12 +101,6 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD if (theVersion != null) { theSystem = new UriType(theSystem.asStringValue() + "|" + theVersion.toString()); } - if (theCodingA != null && theCodingA.hasVersion()) { - theCodingA.setSystem(theCodingA.getSystemElement().asStringValue() + "|" + theCodingA.getVersion()); - } - if (theCodingB != null && theCodingB.hasVersion()) { - theCodingB.setSystem(theCodingB.getSystemElement().asStringValue() + "|" + theCodingB.getVersion()); - } result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails); return (Parameters) result.toParameters(theRequestDetails.getFhirContext()); } finally { 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 45d18b792dc..c9c75772799 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; - private final Properties myUploadProperties; + final Properties myUploadProperties; BaseLoincHandler(Map theCode2Concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { myValueSets = theValueSets; @@ -191,6 +191,8 @@ public abstract class BaseLoincHandler implements IRecordHandler { String version = null; if (isNotBlank(theVersionPropertyName)) { version = myUploadProperties.getProperty(theVersionPropertyName); + } else { + version = myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); } ValueSet vs; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java index 075b33bba8f..655024f10d0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class BaseLoincTop2000LabResultsHandler extends BaseLoincHandler implements IRecordHandler { @@ -41,7 +42,7 @@ public class BaseLoincTop2000LabResultsHandler extends BaseLoincHandler implemen public BaseLoincTop2000LabResultsHandler(Map theCode2concept, List theValueSets, String theValueSetId, String theValueSetUri, String theValueSetName, List theConceptMaps, Properties theUploadProperties) { super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); - myValueSetId = theValueSetId; + myValueSetId = theValueSetId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); myValueSetUri = theValueSetUri; myValueSetName = theValueSetName; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java index 1fc56a37780..81fb84cf35b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java @@ -50,7 +50,7 @@ public class LoincAnswerListHandler extends BaseLoincHandler { public void accept(CSVRecord theRecord) { // this is the code for the list (will repeat) - String answerListId = trim(theRecord.get("AnswerListId")); + String answerListId = trim(theRecord.get("AnswerListId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); String answerListName = trim(theRecord.get("AnswerListName")); String answerListOid = trim(theRecord.get("AnswerListOID")); String externallyDefined = trim(theRecord.get("ExtDefinedYN")); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java index 01b90342432..dc719db9f77 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincDocumentOntologyHandler extends BaseLoincHandler implements IRecordHandler { @@ -57,7 +58,8 @@ public class LoincDocumentOntologyHandler extends BaseLoincHandler implements IR String partName = trim(theRecord.get("PartName")); // RSNA Codes VS - ValueSet vs = getValueSet(DOCUMENT_ONTOLOGY_CODES_VS_ID, DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME, null); + ValueSet vs = getValueSet(DOCUMENT_ONTOLOGY_CODES_VS_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()), + DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME, null); addCodeAsIncludeToValueSet(vs, ITermLoaderSvc.LOINC_URI, loincNumber, null); // Part Properties diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java index dfb92317cdc..71631d837fd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincGroupFileHandler extends BaseLoincHandler implements IRecordHandler { @@ -43,7 +44,7 @@ public class LoincGroupFileHandler extends BaseLoincHandler implements IRecordHa @Override public void accept(CSVRecord theRecord) { //"ParentGroupId","GroupId","Group","Archetype","Status","VersionFirstReleased" - String parentGroupId = trim(theRecord.get("ParentGroupId")); + String parentGroupId = trim(theRecord.get("ParentGroupId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); String groupId = trim(theRecord.get("GroupId")); String groupName = trim(theRecord.get("Group")); @@ -55,7 +56,7 @@ public class LoincGroupFileHandler extends BaseLoincHandler implements IRecordHa // Create group to set its name (terms are added in a different // handler) - getValueSet(groupId, VS_URI_PREFIX + groupId, groupName, null); + getValueSet(groupId, VS_URI_PREFIX + groupId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()), groupName, null); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java index 5403795a2fd..2f07872ce0c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincGroupTermsFileHandler extends BaseLoincHandler implements IRecordHandler { @@ -42,7 +43,7 @@ public class LoincGroupTermsFileHandler extends BaseLoincHandler implements IRec @Override public void accept(CSVRecord theRecord) { //"Category","GroupId","Archetype","LoincNumber","LongCommonName" - String groupId = trim(theRecord.get("GroupId")); + String groupId = trim(theRecord.get("GroupId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); String loincNumber = trim(theRecord.get("LoincNumber")); ValueSet valueSet = getValueSet(groupId, LoincGroupFileHandler.VS_URI_PREFIX + groupId, null, null); 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 75bf1fbc2b6..687ef77c977 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 @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implements IRecordHandler { @@ -61,7 +62,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implemen String targetCodeSystemUri = ITermLoaderSvc.IEEE_11073_10101_URI; addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LOINC_IEEE_CM_ID) + .setConceptMapId(LOINC_IEEE_CM_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(LOINC_IEEE_CM_URI) .setConceptMapName(LOINC_IEEE_CM_NAME) .setSourceCodeSystem(sourceCodeSystemUri) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java index b82ce5a05de..9235c34bffa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java @@ -31,11 +31,12 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincImagingDocumentCodeHandler extends BaseLoincHandler implements IRecordHandler { - public static final String VS_ID = "loinc-imaging-document-codes"; + public final String VS_ID = "loinc-imaging-document-codes" + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); public static final String VS_URI = "http://loinc.org/vs/loinc-imaging-document-codes"; public static final String VS_NAME = "LOINC Imaging Document Codes"; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java index a32ded361e8..7c75809e8e7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincParentGroupFileHandler extends BaseLoincHandler implements IRecordHandler { @@ -41,7 +42,7 @@ public class LoincParentGroupFileHandler extends BaseLoincHandler implements IRe @Override public void accept(CSVRecord theRecord) { // "ParentGroupId","ParentGroup","Status" - String parentGroupId = trim(theRecord.get("ParentGroupId")); + String parentGroupId = trim(theRecord.get("ParentGroupId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); String parentGroupName = trim(theRecord.get("ParentGroup")); getValueSet(parentGroupId, LoincGroupFileHandler.VS_URI_PREFIX + parentGroupId, parentGroupName, null); 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 5fefd31a549..52f48052b5e 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 @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; @@ -128,7 +129,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme addConceptMapEntry( new ConceptMapping() - .setConceptMapId(loincPartMapId) + .setConceptMapId(loincPartMapId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(loincPartMapUri) .setConceptMapName(loincPartMapName) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) 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 690b7effd73..061dabcd4ac 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 @@ -31,6 +31,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.*; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; @@ -174,7 +175,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor if (isNotBlank(rid)) { addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID) + .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) @@ -191,7 +192,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor if (isNotBlank(rpid)) { addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID) + .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_NAME) .setSourceCodeSystem(ITermLoaderSvc.LOINC_URI) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java index dd20a25285a..abf6470ffd6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java @@ -29,11 +29,12 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.*; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincUniversalOrderSetHandler extends BaseLoincHandler implements IRecordHandler { - public static final String VS_ID = "loinc-universal-order-set"; + public final String VS_ID = "loinc-universal-order-set" + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); public static final String VS_URI = "http://loinc.org/vs/loinc-universal-order-set"; public static final String VS_NAME = "LOINC Universal Order Set"; From 1dc4a533d61fd4b9573777e97a7bed714ba1cf70 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Fri, 25 Sep 2020 21:43:15 -0400 Subject: [PATCH 52/71] Fixes to terminology loader. --- .../api/dao/IFhirResourceDaoCodeSystem.java | 2 ++ .../fhir/jpa/term/BaseTermReadSvcImpl.java | 5 ++++- .../uhn/fhir/jpa/term/TermLoaderSvcImpl.java | 16 ++++++++++++---- .../BaseLoincTop2000LabResultsHandler.java | 9 +++++++-- .../term/loinc/LoincAnswerListHandler.java | 11 +++++++++-- .../loinc/LoincDocumentOntologyHandler.java | 12 +++++++++--- .../jpa/term/loinc/LoincGroupFileHandler.java | 19 +++++++++++++++---- .../loinc/LoincGroupTermsFileHandler.java | 12 ++++++++++-- .../LoincIeeeMedicalDeviceCodeHandler.java | 12 ++++++++++-- .../LoincImagingDocumentCodeHandler.java | 14 +++++++++++--- .../loinc/LoincParentGroupFileHandler.java | 13 ++++++++++--- .../LoincPartRelatedCodeMappingHandler.java | 10 +++++++++- .../term/loinc/LoincRsnaPlaybookHandler.java | 16 ++++++++++++++-- .../loinc/LoincUniversalOrderSetHandler.java | 14 +++++++++++--- .../term/loinc/LoincUploadPropertiesEnum.java | 2 +- .../jpa/term/loinc/loincupload.properties | 2 +- .../term/TerminologyLoaderSvcLoincTest.java | 6 +++--- .../tasks/HapiFhirJpaMigrationTasks.java | 2 +- 18 files changed, 139 insertions(+), 38 deletions(-) diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index ba957e00542..a1ff88aa09c 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -42,6 +42,8 @@ public interface IFhirResourceDaoCodeSystem ext SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, CD theCodingA, CD theCodingB, RequestDetails theRequestDetails); + IValidationSupport.CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType theCodeSystemUrl, IPrimitiveType theVersion, IPrimitiveType theCode, IPrimitiveType theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails); + class SubsumesResult { private final ConceptSubsumptionOutcome myOutcome; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index fb1683a33ce..4364e647b3e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -129,6 +129,7 @@ import org.quartz.JobExecutionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -147,7 +148,9 @@ import javax.persistence.PersistenceContextType; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Fetch; import javax.persistence.criteria.Join; +import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.validation.constraints.NotNull; @@ -2706,7 +2709,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { CriteriaQuery query = criteriaBuilder.createQuery(TermConcept.class); Root root = query.from(TermConcept.class); - Fetch systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER); + Fetch systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER); Join systemVersionJoin = (Join)systemVersionFetch; Fetch systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER); Join systemJoin = (Join)systemFetch; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index 5e452989081..f1e53b28d53 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -480,7 +480,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { IOUtils.closeQuietly(theDescriptors); - valueSets.add(getValueSetLoincAll()); + valueSets.add(getValueSetLoincAll(theUploadProperties)); for (Entry next : code2concept.entrySet()) { TermConcept nextConcept = next.getValue(); @@ -499,12 +499,20 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { return new UploadStatistics(conceptCount, target); } - private ValueSet getValueSetLoincAll() { + private ValueSet getValueSetLoincAll(Properties theUploadProperties) { ValueSet retVal = new ValueSet(); - retVal.setId("loinc-all"); + String codeSystemVersionId = theUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + String valueSetId; + if (codeSystemVersionId != null) { + valueSetId = "loinc-all" + "-" + codeSystemVersionId; + } else { + valueSetId = "loinc-all"; + codeSystemVersionId = "1.0.0"; + } + retVal.setId(valueSetId); retVal.setUrl("http://loinc.org/vs"); - retVal.setVersion("1.0.0"); + retVal.setVersion(codeSystemVersionId); retVal.setName("All LOINC codes"); retVal.setStatus(Enumerations.PublicationStatus.ACTIVE); retVal.setDate(new Date()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java index 655024f10d0..1193952de78 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java @@ -31,7 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class BaseLoincTop2000LabResultsHandler extends BaseLoincHandler implements IRecordHandler { @@ -42,7 +42,12 @@ public class BaseLoincTop2000LabResultsHandler extends BaseLoincHandler implemen public BaseLoincTop2000LabResultsHandler(Map theCode2concept, List theValueSets, String theValueSetId, String theValueSetUri, String theValueSetName, List theConceptMaps, Properties theUploadProperties) { super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); - myValueSetId = theValueSetId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String versionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (versionId != null) { + myValueSetId = theValueSetId + "-" + versionId; + } else { + myValueSetId = theValueSetId; + } myValueSetUri = theValueSetUri; myValueSetName = theValueSetName; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java index 81fb84cf35b..7c1ad4ba123 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java @@ -50,7 +50,7 @@ public class LoincAnswerListHandler extends BaseLoincHandler { public void accept(CSVRecord theRecord) { // this is the code for the list (will repeat) - String answerListId = trim(theRecord.get("AnswerListId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String answerListId = trim(theRecord.get("AnswerListId")); String answerListName = trim(theRecord.get("AnswerListName")); String answerListOid = trim(theRecord.get("AnswerListOID")); String externallyDefined = trim(theRecord.get("ExtDefinedYN")); @@ -74,7 +74,14 @@ public class LoincAnswerListHandler extends BaseLoincHandler { } // Answer list ValueSet - ValueSet vs = getValueSet(answerListId, "http://loinc.org/vs/" + answerListId, answerListName, LOINC_ANSWERLIST_VERSION.getCode()); + String valueSetId; + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (codeSystemVersionId != null) { + valueSetId = answerListId + "-" + codeSystemVersionId; + } else { + valueSetId = answerListId; + } + ValueSet vs = getValueSet(valueSetId, "http://loinc.org/vs/" + answerListId, answerListName, LOINC_ANSWERLIST_VERSION.getCode()); if (vs.getIdentifier().isEmpty()) { vs.addIdentifier() .setSystem("urn:ietf:rfc:3986") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java index dc719db9f77..23f410230d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java @@ -33,7 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincDocumentOntologyHandler extends BaseLoincHandler implements IRecordHandler { @@ -58,8 +58,14 @@ public class LoincDocumentOntologyHandler extends BaseLoincHandler implements IR String partName = trim(theRecord.get("PartName")); // RSNA Codes VS - ValueSet vs = getValueSet(DOCUMENT_ONTOLOGY_CODES_VS_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()), - DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME, null); + String valueSetId; + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (codeSystemVersionId != null) { + valueSetId = DOCUMENT_ONTOLOGY_CODES_VS_ID + "-" + codeSystemVersionId; + } else { + valueSetId = DOCUMENT_ONTOLOGY_CODES_VS_ID; + } + ValueSet vs = getValueSet(valueSetId, DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME, null); addCodeAsIncludeToValueSet(vs, ITermLoaderSvc.LOINC_URI, loincNumber, null); // Part Properties diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java index 71631d837fd..7a082edc806 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupFileHandler.java @@ -30,7 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincGroupFileHandler extends BaseLoincHandler implements IRecordHandler { @@ -44,11 +44,22 @@ public class LoincGroupFileHandler extends BaseLoincHandler implements IRecordHa @Override public void accept(CSVRecord theRecord) { //"ParentGroupId","GroupId","Group","Archetype","Status","VersionFirstReleased" - String parentGroupId = trim(theRecord.get("ParentGroupId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String parentGroupId = trim(theRecord.get("ParentGroupId")); String groupId = trim(theRecord.get("GroupId")); String groupName = trim(theRecord.get("Group")); - ValueSet parentValueSet = getValueSet(parentGroupId, VS_URI_PREFIX + parentGroupId, null, null); + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + String parentGroupValueSetId; + String groupValueSetId; + if (codeSystemVersionId != null) { + parentGroupValueSetId = parentGroupId + "-" + codeSystemVersionId; + groupValueSetId = groupId + "-" + codeSystemVersionId; + } else { + parentGroupValueSetId = parentGroupId; + groupValueSetId = groupId; + } + + ValueSet parentValueSet = getValueSet(parentGroupValueSetId, VS_URI_PREFIX + parentGroupId, null, null); parentValueSet .getCompose() .getIncludeFirstRep() @@ -56,7 +67,7 @@ public class LoincGroupFileHandler extends BaseLoincHandler implements IRecordHa // Create group to set its name (terms are added in a different // handler) - getValueSet(groupId, VS_URI_PREFIX + groupId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()), groupName, null); + getValueSet(groupValueSetId, VS_URI_PREFIX + groupId, groupName, null); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java index 2f07872ce0c..8704110b4ab 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincGroupTermsFileHandler.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; @@ -43,10 +44,17 @@ public class LoincGroupTermsFileHandler extends BaseLoincHandler implements IRec @Override public void accept(CSVRecord theRecord) { //"Category","GroupId","Archetype","LoincNumber","LongCommonName" - String groupId = trim(theRecord.get("GroupId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String groupId = trim(theRecord.get("GroupId")); + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + String valueSetId; + if (codeSystemVersionId != null) { + valueSetId = groupId + "-" + codeSystemVersionId; + } else { + valueSetId = groupId; + } String loincNumber = trim(theRecord.get("LoincNumber")); - ValueSet valueSet = getValueSet(groupId, LoincGroupFileHandler.VS_URI_PREFIX + groupId, null, null); + ValueSet valueSet = getValueSet(valueSetId, LoincGroupFileHandler.VS_URI_PREFIX + groupId, null, null); addCodeAsIncludeToValueSet(valueSet, ITermLoaderSvc.LOINC_URI, loincNumber, null); } 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 abe9d8d16a7..96d310bd066 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 @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.trim; @@ -61,9 +62,16 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implemen // LOINC Part -> IEEE 11073:10101 Mappings String sourceCodeSystemUri = ITermLoaderSvc.LOINC_URI; String targetCodeSystemUri = ITermLoaderSvc.IEEE_11073_10101_URI; + String conceptMapId; + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (codeSystemVersionId != null) { + conceptMapId = LOINC_IEEE_CM_ID + "-" + codeSystemVersionId; + } else { + conceptMapId = LOINC_IEEE_CM_ID; + } addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LOINC_IEEE_CM_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) + .setConceptMapId(conceptMapId) .setConceptMapUri(LOINC_IEEE_CM_URI) .setConceptMapVersion(loincIeeeCmVersion) .setConceptMapName(LOINC_IEEE_CM_NAME) @@ -74,7 +82,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implemen .setTargetCode(ieeeCode) .setTargetDisplay(ieeeDisplayName) .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL), - CM_COPYRIGHT); + CM_COPYRIGHT); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java index 9235c34bffa..055689aa432 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java @@ -31,12 +31,12 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincImagingDocumentCodeHandler extends BaseLoincHandler implements IRecordHandler { - public final String VS_ID = "loinc-imaging-document-codes" + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + public static final String VS_ID_BASE = "loinc-imaging-document-codes"; public static final String VS_URI = "http://loinc.org/vs/loinc-imaging-document-codes"; public static final String VS_NAME = "LOINC Imaging Document Codes"; @@ -49,7 +49,15 @@ public class LoincImagingDocumentCodeHandler extends BaseLoincHandler implements String loincNumber = trim(theRecord.get("LOINC_NUM")); String displayName = trim(theRecord.get("LONG_COMMON_NAME")); - ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME,null); + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + String valueSetId; + if (codeSystemVersionId != null) { + valueSetId = VS_ID_BASE + "-" + codeSystemVersionId; + } else { + valueSetId = VS_ID_BASE; + } + + ValueSet valueSet = getValueSet(valueSetId, VS_URI, VS_NAME,null); addCodeAsIncludeToValueSet(valueSet, ITermLoaderSvc.LOINC_URI, loincNumber, displayName); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java index 7c75809e8e7..0455e7fb8d3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincParentGroupFileHandler.java @@ -30,7 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincParentGroupFileHandler extends BaseLoincHandler implements IRecordHandler { @@ -42,10 +42,17 @@ public class LoincParentGroupFileHandler extends BaseLoincHandler implements IRe @Override public void accept(CSVRecord theRecord) { // "ParentGroupId","ParentGroup","Status" - String parentGroupId = trim(theRecord.get("ParentGroupId")) + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + String parentGroupId = trim(theRecord.get("ParentGroupId")); + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + String valueSetId; + if (codeSystemVersionId != null) { + valueSetId = parentGroupId + "-" + codeSystemVersionId; + } else { + valueSetId = parentGroupId; + } String parentGroupName = trim(theRecord.get("ParentGroup")); - getValueSet(parentGroupId, LoincGroupFileHandler.VS_URI_PREFIX + parentGroupId, parentGroupName, null); + getValueSet(valueSetId, LoincGroupFileHandler.VS_URI_PREFIX + parentGroupId, parentGroupName, null); } 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 9f2251153b7..bb3f5ac295e 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 @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; @@ -129,10 +130,17 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme loincPartMapName = "Unknown Mapping"; break; } + String conceptMapId; + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (codeSystemVersionId != null) { + conceptMapId = loincPartMapId + "-" + codeSystemVersionId; + } else { + conceptMapId = loincPartMapId; + } addConceptMapEntry( new ConceptMapping() - .setConceptMapId(loincPartMapId + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) + .setConceptMapId(conceptMapId) .setConceptMapUri(loincPartMapUri) .setConceptMapVersion(loincPartMapVersion) .setConceptMapName(loincPartMapName) 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 de060f2aba5..1d52d71d1f4 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 @@ -31,6 +31,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.*; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; @@ -174,11 +175,22 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor code.addPropertyCoding(loincCodePropName, ITermLoaderSvc.LOINC_URI, partNumber, partName); } + String partConceptMapId; + String termConceptMapId; + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + if (codeSystemVersionId != null) { + partConceptMapId = LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID + "-" + codeSystemVersionId; + termConceptMapId = LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID + "-" + codeSystemVersionId; + } else { + partConceptMapId = LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID; + termConceptMapId = LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID; + } + // LOINC Part -> Radlex RID code mappings if (isNotBlank(rid)) { addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) + .setConceptMapId(partConceptMapId) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI) .setConceptMapVersion(loincRsnaCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME) @@ -196,7 +208,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecor if (isNotBlank(rpid)) { addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode())) + .setConceptMapId(termConceptMapId) .setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI) .setConceptMapVersion(loincRsnaCmVersion) .setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_NAME) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java index abf6470ffd6..e51b5ec45ee 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java @@ -29,12 +29,12 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.*; -import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONCEPTMAP_VERSION; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static org.apache.commons.lang3.StringUtils.trim; public class LoincUniversalOrderSetHandler extends BaseLoincHandler implements IRecordHandler { - public final String VS_ID = "loinc-universal-order-set" + "-" + myUploadProperties.getProperty(LOINC_CONCEPTMAP_VERSION.getCode()); + public static final String VS_ID_BASE = "loinc-universal-order-set"; public static final String VS_URI = "http://loinc.org/vs/loinc-universal-order-set"; public static final String VS_NAME = "LOINC Universal Order Set"; @@ -48,7 +48,15 @@ public class LoincUniversalOrderSetHandler extends BaseLoincHandler implements I String displayName = trim(theRecord.get("LONG_COMMON_NAME")); String orderObs = trim(theRecord.get("ORDER_OBS")); - ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME, null); + String codeSystemVersionId = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + String valueSetId; + if (codeSystemVersionId != null) { + valueSetId = VS_ID_BASE + "-" + codeSystemVersionId; + } else { + valueSetId = VS_ID_BASE; + } + + ValueSet valueSet = getValueSet(valueSetId, VS_URI, VS_NAME, null); addCodeAsIncludeToValueSet(valueSet, ITermLoaderSvc.LOINC_URI, loincNumber, displayName); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java index 225601034b7..af8c414c79a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java @@ -96,7 +96,7 @@ public enum LoincUploadPropertiesEnum { /* * OPTIONAL */ - // This is the version identifier for the answer list file + // This is the version identifier for the LOINC code system LOINC_CODESYSTEM_VERSION("loinc.codesystem.version"), // This is the version identifier for the answer list file diff --git a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties index cb7d1eb0345..ceb67c31bd8 100644 --- a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties +++ b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties @@ -63,7 +63,7 @@ loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersVa # This is the version identifier for the LOINC code system ## Key may be omitted if only a single version of LOINC is being kept. -loinc.codesystem.version=2.68 +#loinc.codesystem.version=2.68 # This is the version identifier for the answer list file ## Key may be omitted 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 5e400e1ffe0..d143f838bda 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()); @@ -274,7 +274,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { assertEquals("Hemoglobin [Mass/volume] in Blood", vs.getCompose().getInclude().get(0).getConcept().get(1).getDisplay()); // Universal lab order VS - vs = valueSets.get(LoincUniversalOrderSetHandler.VS_ID); + vs = valueSets.get(LoincUniversalOrderSetHandler.VS_ID_BASE); assertEquals(1, vs.getCompose().getInclude().size()); assertEquals(ITermLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); assertEquals(9, vs.getCompose().getInclude().get(0).getConcept().size()); @@ -314,7 +314,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { assertEquals("MDC_CONC_GLU_VENOUS_PLASMA", conceptMap.getGroup().get(0).getElement().get(4).getTarget().get(0).getDisplay()); // Imaging Document Codes - vs = valueSets.get(LoincImagingDocumentCodeHandler.VS_ID); + vs = valueSets.get(LoincImagingDocumentCodeHandler.VS_ID_BASE); assertEquals(LoincImagingDocumentCodeHandler.VS_URI, vs.getUrl()); assertEquals(LoincImagingDocumentCodeHandler.VS_NAME, vs.getName()); assertEquals(1, vs.getCompose().getInclude().size()); 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 e1c193e0cc3..a4875fcb035 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 @@ -156,7 +156,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { Builder.BuilderWithTableName trmValueSet = version.onTable("TRM_VALUESET"); trmValueSet.addColumn("20200923.2", "VER").nullable().type(ColumnTypeEnum.STRING, TermValueSet.MAX_VER_LENGTH); trmValueSet.dropIndex("20200923.3", "IDX_VALUESET_URL"); - trmValueSet.addIndex("20200923.4", "IDX_VALUESET_URL").unique(true).withColumns("URL", "VAR"); + trmValueSet.addIndex("20200923.4", "IDX_VALUESET_URL").unique(true).withColumns("URL", "VER"); } protected void init510_20200725() { From 732624bfaf1c5c80c902a806ede575c19771ca3b Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Fri, 25 Sep 2020 22:55:01 -0400 Subject: [PATCH 53/71] Fix broken JUnit test. --- .../provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java | 5 ++--- .../support/CommonCodeSystemsTerminologyService.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java index 421344f8a85..0d8d9180bd4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java @@ -133,7 +133,6 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst } - @SuppressWarnings("unchecked") @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1), @OperationParam(name = "message", type = StringType.class), @@ -164,9 +163,9 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); UriType valueSetIdentifier; if (theValueSetVersion != null) { - valueSetIdentifier = new UriType(theValueSetUrl.getValue() + "|" + theValueSetVersion); + valueSetIdentifier = new UriType(url.getValue() + "|" + theValueSetVersion); } else { - valueSetIdentifier = theValueSetUrl; + valueSetIdentifier = url; } UriType codeSystemIdentifier; if (theSystemVersion != null) { 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 40322adf98c..cd63e8335e3 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 @@ -363,7 +363,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { case DSTU2_HL7ORG: case DSTU2_1: default: - throw new IllegalArgumentException("Can not handle version: " + theValueSet.getStructureFhirVersionEnum()); + version=null; } return version; } From e86d9deaacbc37ca465738e1cd810add78e69f0b Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 27 Sep 2020 17:45:15 -0400 Subject: [PATCH 54/71] Additional tests. --- .../dstu3/FhirResourceDaoValueSetDstu3.java | 6 +- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 9 +- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 10 +- .../BaseJpaResourceProviderCodeSystemR5.java | 2 +- .../ca/uhn/fhir/jpa/term/TermReadSvcR5.java | 6 + .../FhirResourceDaoValueSetDstu2Test.java | 13 + .../FhirResourceDaoDstu3CodeSystemTest.java | 15 +- .../r4/FhirResourceDaoR4CodeSystemTest.java | 2 - ...urceDaoR4SearchWithLuceneDisabledTest.java | 28 ++ .../r5/FhirResourceDaoR5CodeSystemTest.java | 175 +++++++++++ .../r5/ResourceProviderR5CodeSystemTest.java | 99 ++++++- ...urceProviderR5CodeSystemVersionedTest.java | 273 ++++++++++++++++++ 12 files changed, 621 insertions(+), 17 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5CodeSystemTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemVersionedTest.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 637d19630b6..8c5d06c3535 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -126,8 +126,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao if (isNotBlank(theFilter)) { ConceptSetComponent include = source.getCompose().addInclude(); - ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + ConceptSetFilterComponent filter = include.addFilter(); filter.setProperty("display"); filter.setOp(FilterOperator.EQUAL); filter.setValue(theFilter); @@ -135,10 +136,10 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { - ValueSet.ConceptSetComponent include = source.getCompose().addInclude(); - ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + ConceptSetComponent include = source.getCompose().addInclude(); + ConceptSetFilterComponent filter = include.addFilter(); filter.setProperty("display"); - filter.setOp(ValueSet.FilterOperator.EQUAL); + filter.setOp(FilterOperator.EQUAL); filter.setValue(theFilter); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 6036e99ef2b..5a8dd230f06 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -40,6 +40,7 @@ import org.hl7.fhir.r5.model.Enumerations; import org.hl7.fhir.r5.model.IntegerType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import java.util.Date; @@ -102,13 +103,14 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao if (isNotBlank(theFilter)) { ConceptSetComponent include = source.getCompose().addInclude(); - ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + ConceptSetFilterComponent filter = include.addFilter(); filter.setProperty("display"); filter.setOp(Enumerations.FilterOperator.EQUAL); filter.setValue(theFilter); } - return doExpand(source); + ValueSet retVal = doExpand(source); + return retVal; // if (defaultValueSet != null) { // source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet)); @@ -135,8 +137,8 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao source.getCompose().addInclude().addValueSet(theUri); if (isNotBlank(theFilter)) { - ValueSet.ConceptSetComponent include = source.getCompose().addInclude(); - ValueSet.ConceptSetFilterComponent filter = include.addFilter(); + ConceptSetComponent include = source.getCompose().addInclude(); + ConceptSetFilterComponent filter = include.addFilter(); filter.setProperty("display"); filter.setOp(Enumerations.FilterOperator.EQUAL); filter.setValue(theFilter); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java index 37628cb2812..0860e55dd3e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCodeSystemR5.java @@ -59,7 +59,7 @@ public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5 theProperties, RequestDetails theRequestDetails ) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java index 8303aea044e..31237867821 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcR5.java @@ -142,4 +142,10 @@ public class TermReadSvcR5 extends BaseTermReadSvcImpl implements IValidationSup org.hl7.fhir.r4.model.ValueSet valueSetR4 = toCanonicalValueSet(valueSet); return super.isValueSetPreExpandedForCodeValidation(valueSetR4); } + + @Override + public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { + return super.lookupCode(theSystem, theCode); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java index 5c3b320ecbc..a4569557dd1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.dao.dstu2; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.resource.ValueSet; @@ -21,7 +22,9 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { @@ -229,5 +232,15 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { assertThat(resp, not(containsString(""))); } + @Test + public void testValidateCodeForCodeSystemOperationNotSupported() { + try { + ((IFhirResourceDaoCodeSystem)myValueSetDao).validateCode(null, null, null, null, null, null, null, null); + fail(); + } catch (UnsupportedOperationException theE) { + assertNotNull(theE); + } + + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java index 9be3a98c9cc..edbb9f682a9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3CodeSystemTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; -import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.Enumerations; @@ -13,6 +12,8 @@ import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; public class FhirResourceDaoDstu3CodeSystemTest extends BaseJpaDstu3Test { @AfterAll @@ -77,5 +78,17 @@ public class FhirResourceDaoDstu3CodeSystemTest extends BaseJpaDstu3Test { } + @Test + public void testValidateCodeForCodeSystemOperationNotSupported() { + try { + myCodeSystemDao.validateCode(null, null, null, null, null, null, null, null); + fail(); + } catch (UnsupportedOperationException theE) { + assertNotNull(theE); + } + + } + + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java index fa4c2aae93b..724d721ac1f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CodeSystemTest.java @@ -9,14 +9,12 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; public class FhirResourceDaoR4CodeSystemTest extends BaseJpaR4Test { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java index f53f269a2d8..dd3fc3d3a49 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java @@ -337,6 +337,34 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest { } + @Test + public void testExpandValueSetContainingSystemIncludeAndExcludeWithNoCodes() throws IOException { + CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/r4/iar/CodeSystem-iar-citizenship-status.xml"); + myCodeSystemDao.create(cs); + + ValueSet vs = loadResourceFromClasspath(ValueSet.class, "/r4/iar/ValueSet-iar-citizenship-status.xml"); + ValueSet.ConceptSetComponent excludeComponent = new ValueSet.ConceptSetComponent().setSystem("http://ccim.on.ca/fhir/iar/CodeSystem/iar-citizenship-status"); + excludeComponent.addConcept().setCode("REF"); + vs.getCompose().addExclude(excludeComponent); + myValueSetDao.create(vs); + + CodeSystem cs2 = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + myCodeSystemDao.create(cs2); + + ValueSet vs2 = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs-with-exclude.xml"); + myValueSetDao.create(vs2); + + ValueSet expansion = myValueSetDao.expandByIdentifier("http://ccim.on.ca/fhir/iar/ValueSet/iar-citizenship-status", null); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); + + assertEquals(5, expansion.getExpansion().getContains().size()); + + ValueSet expansion2 = myValueSetDao.expandByIdentifier("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", null); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion2)); + + assertEquals(22, expansion2.getExpansion().getContains().size()); + + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5CodeSystemTest.java new file mode 100644 index 00000000000..6e7e00a44c7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5CodeSystemTest.java @@ -0,0 +1,175 @@ +package ca.uhn.fhir.jpa.dao.r5; + +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.CodeSystem; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class FhirResourceDaoR5CodeSystemTest extends BaseJpaR5Test { + + @Test + public void testDeleteLargeCompleteCodeSystem() { + + IIdType id = createLargeCodeSystem(null); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(222, myTermConceptDao.count()); + }); + + myCodeSystemDao.delete(id); + + // Nothing is deleted initially but the URI is changed so it can't be found + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(222, myTermConceptDao.count()); + }); + + // Now the background scheduler will do its thing + myTermDeferredStorageSvc.saveDeferred(); + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(0, myTermCodeSystemVersionDao.count()); + assertEquals(0, myTermConceptDao.count()); + }); + + } + + @Test + public void testDeleteCodeSystemVersion() { + + // Create code system with two versions. + IIdType id_first = createLargeCodeSystem("1"); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(222, myTermConceptDao.count()); + assertEquals(1, resourceList.size()); + assertNull(resourceList.get(0).getDeleted()); + }); + + IIdType id_second = createLargeCodeSystem("2"); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(2, myTermCodeSystemVersionDao.count()); + assertEquals(444, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(2, active); + }); + + // Attempt to delete first version + myCodeSystemDao.delete(id_first, mySrd); + + // Only the resource will be deleted initially + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(2, myTermCodeSystemVersionDao.count()); + assertEquals(444, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(1, active); + }); + + // Now the background scheduler will do its thing + myTermDeferredStorageSvc.saveDeferred(); + + // Entities for first resource should be gone now. + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNotNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(222, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(1, active); + }); + + // Attempt to delete second version + myCodeSystemDao.delete(id_second, mySrd); + + // Only the resource will be deleted initially, but the URL for the TermCodeSystem will be cleared and not searchable. + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(222, myTermConceptDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(0, active); + }); + + // Now the background scheduler will do its thing + myTermDeferredStorageSvc.saveDeferred(); + + // The remaining versions and Code System entities should be gone now. + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertNull(myTermCodeSystemDao.findByCodeSystemUri("http://foo")); + assertEquals(0, myTermCodeSystemVersionDao.count()); + List resourceList = myResourceTableDao.findAll(); + assertEquals(2, resourceList.size()); + long active = resourceList + .stream() + .filter(t -> t.getDeleted() == null).count(); + assertEquals(0, active); + }); + + } + + private IIdType createLargeCodeSystem(String theVersion) { + CodeSystem cs = new CodeSystem(); + cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo"); + if (theVersion != null) { + cs.setVersion(theVersion); + } + for (int i = 0; i < 222; i++) { + cs.addConcept().setCode("CODE" + i); + } + IIdType id = myCodeSystemDao.create(cs).getId().toUnqualifiedVersionless(); + myTermDeferredStorageSvc.saveDeferred(); + myTermDeferredStorageSvc.saveDeferred(); + return id; + } + + @AfterAll + public static void afterClassClearContext() { + TermReindexingSvcImpl.setForceSaveDeferredAlwaysForUnitTest(false); + } + + +} 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 index 2606db1b248..477a6afaddd 100644 --- 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 @@ -2,6 +2,12 @@ package ca.uhn.fhir.jpa.provider.r5; import static org.junit.jupiter.api.Assertions.assertEquals; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; +import org.hl7.fhir.r5.model.Enumerations; +import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.BooleanType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; @@ -9,12 +15,40 @@ 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.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test { + private static final String SYSTEM_PARENTCHILD = "http://parentchild"; private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5CodeSystemTest.class); + private Long parentChildCsId; + + @BeforeEach + @Transactional + public void before02() throws IOException { + CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + myCodeSystemDao.create(cs, mySrd); + + CodeSystem parentChildCs = new CodeSystem(); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setName("Parent Child CodeSystem"); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + CodeSystem.ConceptDefinitionComponent parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); + parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B"); + + DaoMethodOutcome parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCsId = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); + + } @Test public void testValidateCodeWithUrlAndVersion_v1() { @@ -36,7 +70,47 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); assertEquals("Code v1 display", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); } - + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedCode() { + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(1).getName()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(2).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); + } + + @Test + public void testSubsumesOnCodes_Subsumes() { + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ChildAA")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + } + private void createCodeSystem(String url, String version, String code, String display) { CodeSystem codeSystem = new CodeSystem(); @@ -55,4 +129,27 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test myCodeSystemDao.create(codeSystem, mySrd); } + + @Test + public void testLookupOperationByCoding() throws IOException { + + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(1).getName()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(2).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); + } + + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemVersionedTest.java new file mode 100644 index 00000000000..7f4250a53a2 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemVersionedTest.java @@ -0,0 +1,273 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.UriType; +import org.hl7.fhir.r5.model.Enumerations; +import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.CodeSystem; +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 java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ResourceProviderR5CodeSystemVersionedTest extends BaseResourceProviderR5Test { + + private static final String SYSTEM_PARENTCHILD = "http://parentchild"; + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5CodeSystemVersionedTest.class); + + private long parentChildCs1Id; + private long parentChildCs2Id; + + @BeforeEach + @Transactional + public void before02() throws IOException { + CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + cs.setVersion("1"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : cs.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v1"); + } + myCodeSystemDao.create(cs, mySrd); + + cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); + cs.setVersion("2"); + for(CodeSystem.ConceptDefinitionComponent conceptDefinitionComponent : cs.getConcept()) { + conceptDefinitionComponent.setDisplay(conceptDefinitionComponent.getDisplay() + " v2"); + } + myCodeSystemDao.create(cs, mySrd); + + CodeSystem parentChildCs = new CodeSystem(); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setVersion("1"); + parentChildCs.setName("Parent Child CodeSystem 1"); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + CodeSystem.ConceptDefinitionComponent parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA"); + parentA.addConcept().setCode("ParentC").setDisplay("Parent C"); + parentChildCs.addConcept().setCode("ParentB").setDisplay("Parent B"); + + DaoMethodOutcome parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCs1Id = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); + + parentChildCs = new CodeSystem(); + parentChildCs.setVersion("2"); + parentChildCs.setName("Parent Child CodeSystem 2"); + parentChildCs.setUrl(SYSTEM_PARENTCHILD); + parentChildCs.setStatus(Enumerations.PublicationStatus.ACTIVE); + parentChildCs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + parentChildCs.setHierarchyMeaning(CodeSystem.CodeSystemHierarchyMeaning.ISA); + + parentA = parentChildCs.addConcept().setCode("ParentA").setDisplay("Parent A v2"); + parentA.addConcept().setCode("ChildAA").setDisplay("Child AA v2"); + parentA.addConcept().setCode("ParentB").setDisplay("Parent B v2"); + parentChildCs.addConcept().setCode("ParentC").setDisplay("Parent C v2"); + + parentChildCsOutcome = myCodeSystemDao.create(parentChildCs); + parentChildCs2Id = ((ResourceTable)parentChildCsOutcome.getEntity()).getId(); + + } + + @Test + public void testLookupOperationByCoding() { + // First test with no version specified (should return from last version created) + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version set to 1 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v1"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version set to 2 + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "coding", new Coding().setSystem("http://acme.org").setCode("8450-9").setVersion("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + } + + @Test + public void testLookupOperationByCodeAndSystemUserDefinedCode() { + // First test with no version specified (should return from last version created) + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 1 specified. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("1", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v1"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Test with version 2 specified + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("8450-9")) + .andParameter("system", new UriType("http://acme.org")) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals(("ACME Codes"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals(("Systolic blood pressure--expiration v2"), ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + @Test + public void testSubsumesOnCodes_Subsumes() { + // First test with no version specified (should return result for last version created). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentB")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 1. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentC")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("1")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + // Test with version set to 2. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codeA", new CodeType("ParentB")) + .andParameter("codeB", new CodeType("ParentA")) + .andParameter("system", new UriType(SYSTEM_PARENTCHILD)) + .andParameter("version", new StringType("2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + + } + +} From 9d8664ccd84d7c5cd40f63faec3995632a13c151 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Sun, 27 Sep 2020 21:09:06 -0400 Subject: [PATCH 55/71] Additional tests and updates. --- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 24 ++++---- .../ResourceProviderDstu3ValueSetTest.java | 22 ++++++++ ...rceProviderDstu3ValueSetVersionedTest.java | 55 +++++++++++++++++++ .../r4/ResourceProviderR4ValueSetTest.java | 47 ++++++++++++++++ ...sourceProviderR4ValueSetVersionedTest.java | 55 +++++++++++++++++++ .../r5/ResourceProviderR5CodeSystemTest.java | 20 +++++++ .../r5/ResourceProviderR5ValueSetTest.java | 21 +++++++ ...sourceProviderR5ValueSetVersionedTest.java | 55 +++++++++++++++++++ .../jpa/term/TerminologySvcImplDstu2Test.java | 20 +++++++ .../jpa/term/TerminologySvcImplR4Test.java | 8 +++ 10 files changed, 315 insertions(+), 12 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 4364e647b3e..2fe48ea79b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -655,22 +655,22 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { */ private Boolean expandValueSetHandleIncludeOrExclude(@Nullable ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull) { - String includeOrExcludeSystemUrl = theIncludeOrExclude.getSystem(); - boolean hasSystem = isNotBlank(includeOrExcludeSystemUrl); + String system = theIncludeOrExclude.getSystem(); + boolean hasSystem = isNotBlank(system); boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0; if (hasSystem) { - if (theWantConceptOrNull != null && theWantConceptOrNull.getSystem() != null && !includeOrExcludeSystemUrl.equals(theWantConceptOrNull.getSystem())) { + if (theWantConceptOrNull != null && theWantConceptOrNull.getSystem() != null && !system.equals(theWantConceptOrNull.getSystem())) { return false; } - ourLog.debug("Starting {} expansion around CodeSystem: {}", (theAdd ? "inclusion" : "exclusion"), includeOrExcludeSystemUrl); + ourLog.debug("Starting {} expansion around CodeSystem: {}", (theAdd ? "inclusion" : "exclusion"), system); - TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(includeOrExcludeSystemUrl); + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); if (cs != null) { - return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, includeOrExcludeSystemUrl, cs); + return expandValueSetHandleIncludeOrExcludeUsingDatabase(theValueSetCodeAccumulator, theAddedCodes, theIncludeOrExclude, theAdd, theCodeCounter, theQueryIndex, theWantConceptOrNull, system, cs); } else { @@ -683,7 +683,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } // No CodeSystem matching the URL found in the database. - CodeSystem codeSystemFromContext = fetchCanonicalCodeSystemFromCompleteContext(includeOrExcludeSystemUrl); + CodeSystem codeSystemFromContext = fetchCanonicalCodeSystemFromCompleteContext(system); if (codeSystemFromContext == null) { // This is a last ditch effort.. We don't have a CodeSystem resource for the desired CS, and we don't have @@ -708,7 +708,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { for (VersionIndependentConcept next : includedConcepts) { String nextSystem = next.getSystem(); if (nextSystem == null) { - nextSystem = includeOrExcludeSystemUrl; + nextSystem = system; } LookupCodeResult lookup = myValidationSupport.lookupCode(new ValidationSupportContext(provideValidationSupport()), nextSystem, next.getCode()); @@ -724,7 +724,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", includeOrExcludeSystemUrl); + String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "expansionRefersToUnknownCs", system); if (provideExpansionOptions(theExpansionOptions).isFailOnMissingCodeSystem()) { throw new PreconditionFailedException(msg); } else { @@ -739,12 +739,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { for (ValueSet.ConceptReferenceComponent next : theIncludeOrExclude.getConcept()) { String nextCode = next.getCode(); if (theWantConceptOrNull == null || theWantConceptOrNull.getCode().equals(nextCode)) { - if (isNoneBlank(includeOrExcludeSystemUrl, nextCode) && !theAddedCodes.contains(includeOrExcludeSystemUrl + "|" + nextCode)) { + if (isNoneBlank(system, nextCode) && !theAddedCodes.contains(system + "|" + nextCode)) { CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode); if (code != null) { String display = code.getDisplay(); - addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, includeOrExcludeSystemUrl, nextCode, display); + addOrRemoveCode(theValueSetCodeAccumulator, theAddedCodes, theAdd, system, nextCode, display); } } @@ -752,7 +752,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } else { List concept = codeSystemFromContext.getConcept(); - addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, includeOrExcludeSystemUrl, concept, theAdd, theWantConceptOrNull); + addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, system, concept, theAdd, theWantConceptOrNull); } return false; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index e293bb59bb7..95e1bd8d31a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -400,6 +400,28 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } + @Test + public void testExpandByUrlNoPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(false); + + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, Matchers.stringContainsInOrder( + "", + "")); + + } + @Test public void testExpandByUrlWithBogusUrl() throws Exception { loadAndPersistCodeSystemAndValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java index b92cf6566d1..602e788cdcb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java @@ -504,6 +504,61 @@ public class ResourceProviderDstu3ValueSetVersionedTest extends BaseResourceProv } + @Test + public void testExpandByUrlAndVersionNoPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSets(false); + loadAndPersistCodeSystemAndValueSet(); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + @Test public void testExpandByUrlWithBogusVersion() throws Exception { loadAndPersistCodeSystemAndValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index e49548c259d..1a7b5adddd1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -31,6 +31,7 @@ import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IntegerType; @@ -63,6 +64,7 @@ import static org.hamcrest.Matchers.containsStringIgnoringCase; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -342,6 +344,27 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } + @Test + public void testExpandByUrlNoPreExpand() throws Exception { + myDaoConfig.setPreExpandValueSets(false); + loadAndPersistCodeSystemAndValueSet(); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + @Test public void testExpandByUrlWithBogusUrl() throws Exception { loadAndPersistCodeSystemAndValueSet(); @@ -1065,6 +1088,30 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } + @Test + public void testValidateCodeOperationByCoding() throws Exception { + loadAndPersistCodeSystemAndValueSet(); + + Coding codingToValidate = new Coding("http://acme.org", "8495-4", "Systolic blood pressure 24 hour minimum"); + + // With correct system version specified. Should pass. + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("validate-code") + .withParameter(Parameters.class, "coding", codingToValidate) + .andParameter("url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + + } + + + private boolean clearDeferredStorageQueue() { if(!myTerminologyDeferredStorageSvc.isStorageQueueEmpty()) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java index 613f33351ac..51120f167df 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVersionedTest.java @@ -494,6 +494,61 @@ public class ResourceProviderR4ValueSetVersionedTest extends BaseResourceProvide } + @Test + public void testExpandByUrlAndVersionNoPreExpand() throws Exception { + myDaoConfig.setPreExpandValueSets(false); + loadAndPersistCodeSystemAndValueSet(); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + @Test public void testExpandByUrlWithBogusVersion() throws Exception { loadAndPersistCodeSystemAndValueSet(); 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 index 477a6afaddd..f584c47a50d 100644 --- 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 @@ -111,6 +111,26 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); } + @Test + public void testSubsumesOnCodings_Subsumes() { + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_SUBSUMES) + .withParameter(Parameters.class, "codingA", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ChildAA")) + .andParameter("codingB", new Coding().setSystem(SYSTEM_PARENTCHILD).setCode("ParentA")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(1, respParam.getParameter().size()); + assertEquals("outcome", respParam.getParameter().get(0).getName()); + assertEquals(ConceptSubsumptionOutcome.SUBSUMES.toCode(), ((CodeType) respParam.getParameter().get(0).getValue()).getValue()); + } + + + private void createCodeSystem(String url, String version, String code, String display) { CodeSystem codeSystem = new CodeSystem(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index c26c8bde431..cc59fbe5d62 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -457,6 +457,27 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { } + @Test + public void testExpandByUrlNoPreExpand() throws Exception { + myDaoConfig.setPreExpandValueSets(false); + loadAndPersistCodeSystemAndValueSet(HTTPVerb.POST); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + @Test public void testExpandByUrlWithBogusUrl() throws Exception { loadAndPersistCodeSystemAndValueSet(HTTPVerb.POST); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java index 59eead8a901..020107a1a60 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java @@ -488,6 +488,61 @@ public class ResourceProviderR5ValueSetVersionedTest extends BaseResourceProvide } + @Test + public void testExpandByUrlAndVersionNoPreExpand() throws Exception { + myDaoConfig.setPreExpandValueSets(false); + loadAndPersistCodeSystemAndValueSet(); + + // Check expansion of multi-versioned ValueSet with version 1 + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("1")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of multi-versioned ValueSet with version set to null + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + // Should return v2 as this was the last version loaded. + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + // Check expansion of version 2 + respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .andParameter("valueSetVersion", new StringType("2")) + .execute(); + expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder( + "", + "")); + + } + @Test public void testExpandByUrlWithBogusVersion() throws Exception { loadAndPersistCodeSystemAndValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java new file mode 100644 index 00000000000..35a53442c9d --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java @@ -0,0 +1,20 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.r4.model.Coding; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TerminologySvcImplDstu2Test { + + @Test + public void testToCanonicalCoding() { + TermReadSvcDstu2 myReadSvc = new TermReadSvcDstu2(); + IBaseCoding myCoding = new CodingDt("dstuSystem", "dstuCode"); + Coding convertedCoding = myReadSvc.toCanonicalCoding(myCoding); + assertEquals("dstuCode", convertedCoding.getCode()); + assertEquals("dstuSystem", convertedCoding.getSystem()); + } +} 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 b713b8a6908..5c53c12b29d 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 @@ -2068,4 +2068,12 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { assertEquals("1", termCodeSystem.getCurrentVersion().getCodeSystemVersionId()); }); } + + @Test + public void testFindCodeInvalidCodeSystem() { + runInTransaction(() -> { + Optional termConcept = myTermSvc.findCode("http://InvalidSystem", "mycode"); + assertFalse(termConcept.isPresent()); + }); + } } From 42b4e9ae02f33baa02a49b523a10935275155b7f Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Mon, 28 Sep 2020 08:45:59 -0400 Subject: [PATCH 56/71] Add change log. --- .../changelog/5_2_0/2081-multiple-version-code-systems.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml new file mode 100644 index 00000000000..d2b2f92e3b7 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2081 +title: "Terminology loader for LOINC and operations for CodeSystem, ValueSet and ConcepMap will now support + multiple CodeSystem versions." From 26688aac65204fcdbe41b3ebfa1f3c771cf20e6c Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 12:11:56 -0400 Subject: [PATCH 57/71] Update hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties Co-authored-by: Diederik Muylwyk --- .../main/resources/ca/uhn/fhir/i18n/hapi-messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 82fae249713..7ead97940eb 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 @@ -145,7 +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.cannotCreateDuplicateConceptMapUrlAndVersion=Can not create multiple ConceptMap resources with ConceptMap.url "{0}" and 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} From 10620a8bb46ffc081aea3214772bc51faa17defd Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:16:13 -0400 Subject: [PATCH 58/71] Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java Co-authored-by: Diederik Muylwyk --- .../provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index b4ede03ec9a..237a9f2847a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -90,7 +90,7 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD @OperationParam(name = "system", min = 0, max = 1) UriType theSystem, @OperationParam(name = "codingA", min = 0, max = 1) Coding theCodingA, @OperationParam(name = "codingB", min = 0, max = 1) Coding theCodingB, - @OperationParam(name="version", min=0, max=1) StringType theVersion, + @OperationParam(name = "version", min = 0, max = 1) StringType theVersion, RequestDetails theRequestDetails ) { From ebb3fa0ca1e0be3d9d7555b88c0c1bfa5c0bce3b Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:16:56 -0400 Subject: [PATCH 59/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f7aa915920d..23b98065cb5 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 @@ -108,7 +108,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { } @Test - public void testConcaptMapFindTermConceptMapByUrl() { + public void testConceptMapFindTermConceptMapByUrl() { Pageable page = PageRequest.of(0, 1); List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); From 6aa1a5ec3fcbcc9851b27c66a70868b13015440c Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:17:19 -0400 Subject: [PATCH 60/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 23b98065cb5..0c5e09b6f6e 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 @@ -118,7 +118,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { } @Test - public void testConcaptMapTwoConceptMapWithSameUrlDifferentVersion() { + public void testConceptMapTwoConceptMapWithSameUrlDifferentVersion() { String theUrl = "http://loinc.org/property/analyte-suffix"; ConceptMap theConceptMap1 = new ConceptMap(); From 3940e7526fb6fc2e2087ae91a334fb9c6430e029 Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:17:36 -0400 Subject: [PATCH 61/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 997e5a35b45..464ded634e1 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 @@ -1155,7 +1155,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { } @Test - public void testConcaptMapFindTermConceptMapByUrl() { + public void testConceptMapFindTermConceptMapByUrl() { Pageable page = PageRequest.of(0, 1); List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); From b8531e599f99f48f6a400029cb92f7808e8dcc82 Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:18:00 -0400 Subject: [PATCH 62/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 464ded634e1..3f342102fd7 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 @@ -1165,7 +1165,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { } @Test - public void testConcaptMapTwoConceptMapWithSameUrlDifferentVersion() { + public void testConceptMapTwoConceptMapWithSameUrlDifferentVersion() { String theUrl = "http://loinc.org/property/analyte-suffix"; ConceptMap theConceptMap1 = new ConceptMap(); From 2be47b5567b373061a601942b516de5220a2aedf Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:18:21 -0400 Subject: [PATCH 63/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3f342102fd7..e98ba97bd71 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 @@ -1198,7 +1198,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { } @Test - public void testConcaptMapTwoConceptMapWithSameUrlOneWithoutVersion() { + public void testConceptMapTwoConceptMapWithSameUrlOneWithoutVersion() { String theUrl = "http://loinc.org/property/analyte-suffix"; ConceptMap theConceptMap1 = new ConceptMap(); From 43b6bd38ff456fe0119cb4346d5f19426ca5f0cb Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:26:30 -0400 Subject: [PATCH 64/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 835e806e93e..33e7d2fc2f3 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 @@ -175,7 +175,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithVersionedConcaptMapUrl_v2() { + public void testTranslateWithVersionedConceptMapUrl_v2() { String url = "http://url"; createConceptMap(url, "v1", "12222", "Target Code 12222"); From 43c16e17372e9abd34238cb59313c6a9a97aa56a Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:27:03 -0400 Subject: [PATCH 65/71] Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java Co-authored-by: Diederik Muylwyk --- .../jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 33e7d2fc2f3..7c7a63052e7 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 @@ -225,7 +225,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithVersionedConcaptMapUrl_v1() { + public void testTranslateWithVersionedConceptMapUrl_v1() { String url = "http://url"; createConceptMap(url, "v1", "12222", "Target Code 12222"); From d63c15ef096b7401ae9422f278f33ca5d0a2c438 Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:27:57 -0400 Subject: [PATCH 66/71] Apply suggestions from code review Co-authored-by: Diederik Muylwyk --- .../provider/dstu3/ResourceProviderDstu3ConceptMapTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 7c7a63052e7..9d196fde0c0 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 @@ -275,7 +275,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithVersionedConcaptMapUrl_NoVersion() { + public void testTranslateWithVersionedConceptMapUrl_NoVersion() { String url = "http://url"; createConceptMap(url, "v1", "12222", "Target Code 12222"); @@ -323,7 +323,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v1() { + public void testTranslateWithVersionedConceptMapUrl_NoVersion_null_v1() { String url = "http://url"; createConceptMap(url, null, "12222", "Target Code 12222"); // first version is null @@ -371,7 +371,7 @@ public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDst } @Test - public void testTranslateWithVersionedConcaptMapUrl_NoVersion_null_v2() { + public void testTranslateWithVersionedConceptMapUrl_NoVersion_null_v2() { String url = "http://url"; createConceptMap(url, "v1", "12222", "Target Code 12222"); From c593958d2c45b16ed7328eed815ab71c99f6269b Mon Sep 17 00:00:00 2001 From: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com> Date: Tue, 29 Sep 2020 15:16:46 -0400 Subject: [PATCH 67/71] Apply suggestions from code review Co-authored-by: Diederik Muylwyk --- .../provider/r4/ResourceProviderR4CodeSystemValidationTest.java | 2 +- .../fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java index c4d705303fc..0b55d5f4450 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java @@ -31,7 +31,7 @@ public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProv 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"; + private static final String CS_ACME_URL = "http://acme.org"; @BeforeEach @Transactional 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 2888f57b253..35e1def3af6 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 @@ -886,7 +886,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test } @Test - public void testTranslateWithConcaptMapUrlAndVersion() { + public void testTranslateWithConceptMapUrlAndVersion() { //- conceptMap1 v1 ConceptMap conceptMap1 = new ConceptMap(); From 8e7b4e8437bb7f09b3cd14f57f3d9cb5231e61b8 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 29 Sep 2020 21:05:28 -0400 Subject: [PATCH 68/71] Changes per code review. --- ...ava => FhirVersionIndependentConcept.java} | 13 +- ...add-codeystem-validate-code-operation.yaml | 4 + .../2081-loinc-loader-multiple-version.yaml | 4 + .../fhir/jpa/term/BaseTermReadSvcImpl.java | 22 +- .../term/TermCodeSystemStorageSvcImpl.java | 2 +- .../FhirResourceDaoDstu3ConceptMapTest.java | 2 +- ...eProviderDstu3CodeSystemVersionedTest.java | 47 +- .../r4/ResourceProviderR4CodeSystemTest.java | 512 +++++++++++++++- ...rceProviderR4CodeSystemValidationTest.java | 560 ------------------ ...urceProviderR4CodeSystemVersionedTest.java | 2 +- .../r4/ResourceProviderR4ConceptMapTest.java | 2 +- .../r5/ResourceProviderR5CodeSystemTest.java | 68 ++- .../r5/ResourceProviderR5ConceptMapTest.java | 5 +- ... => TerminologyLoaderSvcLoincJpaTest.java} | 2 +- .../jpa/term/TerminologySvcImplR4Test.java | 2 +- .../loinc/v268_loincupload.properties | 8 +- ...oryTerminologyServerValidationSupport.java | 30 +- ...mmonCodeSystemsTerminologyServiceTest.java | 2 + 18 files changed, 670 insertions(+), 617 deletions(-) rename hapi-fhir-base/src/main/java/ca/uhn/fhir/util/{VersionIndependentConcept.java => FhirVersionIndependentConcept.java} (80%) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-add-codeystem-validate-code-operation.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-loinc-loader-multiple-version.yaml delete mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java rename hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/{TerminologyLoaderSvcLoincIntegratedTest.java => TerminologyLoaderSvcLoincJpaTest.java} (97%) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirVersionIndependentConcept.java similarity index 80% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirVersionIndependentConcept.java index ec5439fbffc..cd524b8ef7a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionIndependentConcept.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirVersionIndependentConcept.java @@ -20,12 +20,11 @@ package ca.uhn.fhir.util; * #L% */ -import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -public class VersionIndependentConcept implements Comparable { +public class FhirVersionIndependentConcept implements Comparable { private final String mySystem; private final String mySystemVersion; @@ -36,15 +35,15 @@ public class VersionIndependentConcept implements Comparable predicates; Coding coding; - //-- get the latest ConceptMapVersion if theTranslationRequest has url, but ConceptMapVersion + //-- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version String latestConceptMapVersion = null; if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); @@ -2174,7 +2174,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { ArrayList predicates; Coding coding; - //-- get the latest ConceptMapVersion if theTranslationRequest has url, but ConceptMapVersion + //-- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version String latestConceptMapVersion = null; if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index b55e14cf0f0..e8ba5fbb1b0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -235,7 +235,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { @Transactional(propagation = Propagation.NEVER) public void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { // Delete TermCodeSystemVersion - ourLog.info(" * Deleting code system version {}", theCodeSystemVersion.getCodeSystemVersionId()); + ourLog.info(" * Deleting TermCodeSystemVersion {}", theCodeSystemVersion.getCodeSystemVersionId()); deleteCodeSystemVersion(theCodeSystemVersion.getPid()); } 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 0c5e09b6f6e..7202a38c741 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 @@ -151,7 +151,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { } @Test - public void testConcaptMapTwoConceptMapWithSameUrlOneWithoutVersion() { + public void testConceptMapTwoConceptMapWithSameUrlOneWithoutVersion() { String theUrl = "http://loinc.org/property/analyte-suffix"; ConceptMap theConceptMap1 = new ConceptMap(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java index c75bfec66ba..6f71a5d227d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemVersionedTest.java @@ -4,8 +4,6 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; -import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test; -import ca.uhn.fhir.jpa.provider.dstu3.ResourceProviderDstu3ValueSetTest; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.apache.commons.io.IOUtils; @@ -21,7 +19,6 @@ import org.hl7.fhir.dstu3.model.Enumerations; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.UriType; -//import org.hl7.fhir.dstu3.model.codesystems.ConceptSubsumptionOutcome; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.transaction.annotation.Transactional; @@ -377,6 +374,48 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr } + @Test + public void testLookupOperationByCodeAndSystemBuiltInCode() { + // First test with no version specified (should return the one and only version defined). + Parameters respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("N")) + .andParameter("system", new UriType("http://hl7.org/fhir/v2/0243")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2 Identity May Be Divulged", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.8.2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("No", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + + // Repeat with version specified. + respParam = ourClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("N")) + .andParameter("system", new UriType("http://hl7.org/fhir/v2/0243")) + .andParameter("version", new StringType("2.8.2")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2 Identity May Be Divulged", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.8.2", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("No", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + } + @Test public void testSubsumesOnCodes_Subsumes() { // First test with no version specified (should return result for last version created). @@ -722,7 +761,7 @@ public class ResourceProviderDstu3CodeSystemVersionedTest extends BaseResourcePr } @Test - public void testUpdateCodeSystemName() throws IOException { + public void testUpdateCodeSystemById() throws IOException { CodeSystem initialCodeSystem = ourClient.read().resource(CodeSystem.class).withId(parentChildCs1Id).execute(); assertEquals("Parent Child CodeSystem 1", initialCodeSystem.getName()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index 6b2fed44ea6..c8dfce5efd9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -11,6 +11,7 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.junit.jupiter.api.BeforeEach; @@ -27,11 +28,14 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemTest.class); private Long parentChildCsId; + private IIdType myCsId; + private static final String CS_ACME_URL = "http://acme.org"; + @BeforeEach @Transactional public void before02() throws IOException { CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); - myCodeSystemDao.create(cs, mySrd); + myCsId = myCodeSystemDao.create(cs, mySrd).getId().toUnqualifiedVersionless(); CodeSystem parentChildCs = new CodeSystem(); parentChildCs.setUrl(SYSTEM_PARENTCHILD); @@ -261,7 +265,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test assertEquals("display", respParam.getParameter().get(2).getName()); assertEquals("Married", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(3).getName()); - assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).booleanValue()); + assertFalse(((BooleanType) respParam.getParameter().get(3).getValue()).booleanValue()); } @Test @@ -427,7 +431,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test } @Test - public void testUpdateCodeSystemName() throws IOException { + public void testUpdateCodeSystemById() throws IOException { CodeSystem initialCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCsId).execute(); assertEquals("Parent Child CodeSystem", initialCodeSystem.getName()); @@ -446,5 +450,507 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test assertEquals("Updated Parent Child CodeSystem", updatedCodeSystem.getName()); } + @Test + public void testValidateCodeFoundByCode() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCode() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertFalse(((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() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeNotMatchDisplay() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertFalse(((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() { + + 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() { + + 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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testValidateCodeWithoutCodeOrCodingOrCodeableConcept() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodingWithSystem() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_URL)); + inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodingUrlNotMatch() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCoding() { + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertFalse(((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() { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithSystem() { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACME_URL); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeFoundByCodeableConceptWithDisplay() { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5").setSystem(CS_ACME_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + + } + + @Test + public void testValidateCodeNotFoundByCodeableConcept() { + + CodeableConcept cc = new CodeableConcept(); + cc.addCoding().setCode("8452-5-a"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + assertFalse(((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_ACME_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_ACME_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7").setSystem(CS_ACME_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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_ACME_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7-a").setSystem(CS_ACME_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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_ACME_URL).setDisplay("Systolic blood pressure.inspiration - expiration"); + cc.addCoding().setCode("8451-7").setSystem(CS_ACME_URL).setDisplay("Systolic blood pressure--inspiration"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("url").setValue(new UriType(CS_ACME_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); + + CodeSystem.ConceptDefinitionComponent concept1 = codeSystem.addConcept(); + concept1.setCode("1000").setDisplay("Code Dispaly 1000"); + + CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept(); + concept.setCode(code).setDisplay(display); + + CodeSystem.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/ResourceProviderR4CodeSystemValidationTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java deleted file mode 100644 index 0b55d5f4450..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemValidationTest.java +++ /dev/null @@ -1,560 +0,0 @@ -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_ACME_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/ResourceProviderR4CodeSystemVersionedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java index f5e28967d36..3c13aef9a8e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemVersionedTest.java @@ -783,7 +783,7 @@ public class ResourceProviderR4CodeSystemVersionedTest extends BaseResourceProvi } @Test - public void testUpdateCodeSystemName() throws IOException { + public void testUpdateCodeSystemById() throws IOException { CodeSystem initialCodeSystem = myClient.read().resource(CodeSystem.class).withId(parentChildCs1Id).execute(); assertEquals("Parent Child CodeSystem 1", initialCodeSystem.getName()); 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 35e1def3af6..9236a043090 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 @@ -1721,7 +1721,7 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test } @Test - public void testTranslateWithReverseConcaptMapUrlAndVersion() { + public void testTranslateWithReverseConceptMapUrlAndVersion() { //- conceptMap1 v1 ConceptMap conceptMap1 = new ConceptMap(); 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 index f584c47a50d..9d3d8914a5a 100644 --- 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 @@ -1,10 +1,12 @@ package ca.uhn.fhir.jpa.provider.r5; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.hl7.fhir.r5.model.Enumerations; import org.hl7.fhir.r5.model.Coding; @@ -28,11 +30,13 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5CodeSystemTest.class); private Long parentChildCsId; + private IIdType myCsId; + @BeforeEach @Transactional public void before02() throws IOException { CodeSystem cs = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); - myCodeSystemDao.create(cs, mySrd); + myCsId = myCodeSystemDao.create(cs, mySrd).getId().toUnqualifiedVersionless(); CodeSystem parentChildCs = new CodeSystem(); parentChildCs.setUrl(SYSTEM_PARENTCHILD); @@ -171,5 +175,67 @@ public class ResourceProviderR5CodeSystemTest extends BaseResourceProviderR5Test assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue()); } + @Test + public void testValidateCodeFoundByCodeWithId() { + + 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); + + assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); + assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + } + + @Test + public void testLookupOperationByCodeAndSystemBuiltInCode() { + // First test with no version specified (should return the one and only version defined). + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203")) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + + // Repeat with version specified. + respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203")) + .andParameter("version", new StringType("2.9")) + .execute(); + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + + } 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 index 04b55803ce4..a2cb18581f0 100644 --- 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 @@ -5,7 +5,6 @@ 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; @@ -26,7 +25,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR5ConceptMapTest.class); @Test - public void testTranslateWithConcaptMapUrlAndVersion() { + public void testTranslateWithConceptMapUrlAndVersion() { //- conceptMap1 v1 ConceptMap conceptMap1 = new ConceptMap(); @@ -109,7 +108,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test } @Test - public void testTranslateWithReverseConcaptMapUrlAndVersion() { + public void testTranslateWithReverseConceptMapUrlAndVersion() { //- conceptMap1 v1 ConceptMap conceptMap1 = new ConceptMap(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincJpaTest.java similarity index 97% rename from hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincJpaTest.java index b6703763dbe..5b44a99f9e3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincIntegratedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincJpaTest.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; -public class TerminologyLoaderSvcLoincIntegratedTest extends BaseJpaR4Test { +public class TerminologyLoaderSvcLoincJpaTest extends BaseJpaR4Test { private TermLoaderSvcImpl mySvc; private ZipCollectionBuilder myFiles; 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 5c53c12b29d..48f4f4465ff 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 @@ -290,7 +290,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { 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()); + assertEquals("Can not create multiple ConceptMap resources with ConceptMap.url \"http://example.com/my_concept_map\" and ConceptMap.version \"v1\", already have one with resource ID: ConceptMap/" + myConceptMapId.getIdPart(), e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties index 7f4854293de..cb7d1eb0345 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties @@ -2,10 +2,6 @@ ### MANDATORY ### ################# -# This is the version identifier for the LOINC code system -## Key may be omitted if only a single version of LOINC is being kept. -loinc.codesystem.version=2.68 - # Answer lists (ValueSets of potential answers/values for LOINC "questions") ## File must be present loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv @@ -65,6 +61,10 @@ loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersVa ### OPTIONAL ### ################ +# This is the version identifier for the LOINC code system +## Key may be omitted if only a single version of LOINC is being kept. +loinc.codesystem.version=2.68 + # This is the version identifier for the answer list file ## Key may be omitted loinc.answerlist.version=Beta.1 diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 4704912f7fb..d6bad82267d 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -91,23 +91,23 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu return new ValueSetExpansionOutcome(expansion, null); } - private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetToCanonical(ValidationSupportContext theValidationSupportContext, IBaseResource theValueSetToExpand, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) { org.hl7.fhir.r5.model.ValueSet expansionR5; switch (theValueSetToExpand.getStructureFhirVersionEnum()) { case DSTU2: { - expansionR5 = expandValueSetDstu2(theValidationSupportContext, (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); + expansionR5 = expandValueSetDstu2(theValidationSupportContext, (ca.uhn.fhir.model.dstu2.resource.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); break; } case DSTU2_HL7ORG: { - expansionR5 = expandValueSetDstu2Hl7Org(theValidationSupportContext, (ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); + expansionR5 = expandValueSetDstu2Hl7Org(theValidationSupportContext, (ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); break; } case DSTU3: { - expansionR5 = expandValueSetDstu3(theValidationSupportContext, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); + expansionR5 = expandValueSetDstu3(theValidationSupportContext, (org.hl7.fhir.dstu3.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); break; } case R4: { - expansionR5 = expandValueSetR4(theValidationSupportContext, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystemIdentifier, theWantCode); + expansionR5 = expandValueSetR4(theValidationSupportContext, (org.hl7.fhir.r4.model.ValueSet) theValueSetToExpand, theWantSystemUrlAndVersion, theWantCode); break; } case R5: { @@ -153,15 +153,9 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } switch (myCtx.getVersion().getVersion()) { case DSTU2_HL7ORG: - if (codeSystemVersion != null) { - vs = new org.hl7.fhir.dstu2.model.ValueSet() - .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(codeSystemUrl).setVersion(codeSystemVersion))); - } else { - vs = new org.hl7.fhir.dstu2.model.ValueSet() - .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() - .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); - } + vs = new org.hl7.fhir.dstu2.model.ValueSet() + .setCompose(new org.hl7.fhir.dstu2.model.ValueSet.ValueSetComposeComponent() + .addInclude(new org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent().setSystem(theCodeSystem))); break; case DSTU3: if (codeSystemVersion != null) { @@ -228,25 +222,25 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu case DSTU2_HL7ORG: { ValueSet expansionVs = (ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesDstu2(contains, codesInValueSetExpansion); + flattenAndConvertCodesDstu2(contains, codes); break; } case DSTU3: { org.hl7.fhir.dstu3.model.ValueSet expansionVs = (org.hl7.fhir.dstu3.model.ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesDstu3(contains, codesInValueSetExpansion); + flattenAndConvertCodesDstu3(contains, codes); break; } case R4: { org.hl7.fhir.r4.model.ValueSet expansionVs = (org.hl7.fhir.r4.model.ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesR4(contains, codesInValueSetExpansion); + flattenAndConvertCodesR4(contains, codes); break; } case R5: { org.hl7.fhir.r5.model.ValueSet expansionVs = (org.hl7.fhir.r5.model.ValueSet) theExpansion; List contains = expansionVs.getExpansion().getContains(); - flattenAndConvertCodesR5(contains, codesInValueSetExpansion); + flattenAndConvertCodesR5(contains, codes); break; } case DSTU2: 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 b3cb3984ee9..898b973c042 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 @@ -129,4 +129,6 @@ public class CommonCodeSystemsTerminologyServiceTest { assertEquals("Can not handle version: DSTU3", e.getMessage()); } } + + } From dc3443665cebadb957d29caa0a7c8ea9a26bc9f3 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Tue, 29 Sep 2020 21:12:27 -0400 Subject: [PATCH 69/71] Changes per code review. --- .../2081-multiple-version-code-systems.yaml | 3 +- .../fhir/jpa/api/model/TranslationQuery.java | 2 - .../fhir/jpa/dao/data/ITermConceptMapDao.java | 5 +- .../fhir/jpa/dao/data/ITermValueSetDao.java | 1 + .../dao/predicate/PredicateBuilderToken.java | 22 +++--- .../ca/uhn/fhir/jpa/entity/TermConcept.java | 1 - .../fhir/jpa/term/BaseTermReadSvcImpl.java | 76 +++++++++---------- .../uhn/fhir/jpa/term/TermReadSvcDstu2.java | 25 +++--- .../uhn/fhir/jpa/term/TermReadSvcDstu3.java | 9 --- .../uhn/fhir/jpa/term/api/ITermReadSvc.java | 15 ++-- .../java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java | 6 +- .../FhirResourceDaoDstu3ConceptMapTest.java | 6 +- .../r4/FhirResourceDaoR4ConceptMapTest.java | 6 +- .../jpa/term/TerminologySvcImplDstu3Test.java | 6 +- .../jpa/term/TerminologySvcImplR4Test.java | 4 +- ...oryTerminologyServerValidationSupport.java | 44 +++++------ 16 files changed, 108 insertions(+), 123 deletions(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml index d2b2f92e3b7..0c5f644c8a1 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_2_0/2081-multiple-version-code-systems.yaml @@ -1,5 +1,4 @@ --- type: add issue: 2081 -title: "Terminology loader for LOINC and operations for CodeSystem, ValueSet and ConcepMap will now support - multiple CodeSystem versions." +title: "Operations for CodeSystem, ValueSet and ConcepMap will now support multiple CodeSystem versions." 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 06bfd9cd07a..04e51dd9039 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 @@ -61,7 +61,6 @@ public class TranslationQuery { myResourceId = theResourceId; } - //-- url public boolean hasUrl() { return myUrl != null && myUrl.hasValue(); } @@ -74,7 +73,6 @@ public class TranslationQuery { myUrl = theUrl; } - //-- ConceptMapVersion public boolean hasConceptMapVersion() { return myConceptMapVersion != null && myConceptMapVersion.hasValue(); } 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 a7716ebafe0..f4109f94e96 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 @@ -46,9 +46,10 @@ public interface ITermConceptMapDao extends JpaRepository @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url and cm.myVersion is null") Optional findTermConceptMapByUrlAndNullVersion(@Param("url") String theUrl); - + + // Note that last updated version is considered current version. @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); + List getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(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/data/ITermValueSetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java index bc698dc8b3b..3046ac6a244 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermValueSetDao.java @@ -36,6 +36,7 @@ public interface ITermValueSetDao extends JpaRepository { @Query("SELECT vs FROM TermValueSet vs WHERE vs.myResourcePid = :resource_pid") Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); + // Keeping for backwards compatibility but recommend using findTermValueSetByUrlAndNullVersion instead. @Deprecated @Query("SELECT vs FROM TermValueSet vs WHERE vs.myUrl = :url") Optional findByUrl(@Param("url") String theUrl); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java index a58f739e1fa..9606694f68d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java @@ -39,7 +39,7 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import com.google.common.collect.Sets; import org.hibernate.query.criteria.internal.CriteriaBuilderImpl; import org.hibernate.query.criteria.internal.predicate.BooleanStaticAssertionPredicate; @@ -160,7 +160,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu From theFrom, SearchFilterParser.CompareOperation operation, RequestPartitionId theRequestPartitionId) { - final List codes = new ArrayList<>(); + final List codes = new ArrayList<>(); String paramName = theSearchParam.getName(); TokenParamModifier modifier = null; @@ -214,12 +214,12 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu validateHaveSystemAndCodeForToken(paramName, code, system); codes.addAll(myTerminologySvc.findCodesBelow(system, code)); } else { - codes.add(new VersionIndependentConcept(system, code)); + codes.add(new FhirVersionIndependentConcept(system, code)); } } - List sortedCodesList = codes + List sortedCodesList = codes .stream() .filter(t -> t.getCode() != null || t.getSystem() != null) .sorted() @@ -234,19 +234,19 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu List retVal = new ArrayList<>(); // System only - List systemOnlyCodes = sortedCodesList.stream().filter(t -> isBlank(t.getCode())).collect(Collectors.toList()); + List systemOnlyCodes = sortedCodesList.stream().filter(t -> isBlank(t.getCode())).collect(Collectors.toList()); if (!systemOnlyCodes.isEmpty()) { retVal.add(addPredicate(theResourceName, paramName, theBuilder, theFrom, systemOnlyCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_ONLY, theRequestPartitionId)); } // Code only - List codeOnlyCodes = sortedCodesList.stream().filter(t -> t.getSystem() == null).collect(Collectors.toList()); + List codeOnlyCodes = sortedCodesList.stream().filter(t -> t.getSystem() == null).collect(Collectors.toList()); if (!codeOnlyCodes.isEmpty()) { retVal.add(addPredicate(theResourceName, paramName, theBuilder, theFrom, codeOnlyCodes, modifier, SearchBuilderTokenModeEnum.VALUE_ONLY, theRequestPartitionId)); } // System and code - List systemAndCodeCodes = sortedCodesList.stream().filter(t -> isNotBlank(t.getCode()) && t.getSystem() != null).collect(Collectors.toList()); + List systemAndCodeCodes = sortedCodesList.stream().filter(t -> isNotBlank(t.getCode()) && t.getSystem() != null).collect(Collectors.toList()); if (!systemAndCodeCodes.isEmpty()) { retVal.add(addPredicate(theResourceName, paramName, theBuilder, theFrom, systemAndCodeCodes, modifier, SearchBuilderTokenModeEnum.SYSTEM_AND_VALUE, theRequestPartitionId)); } @@ -272,8 +272,8 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu String valueSet = valueSetUris.iterator().next(); ValueSetExpansionOptions options = new ValueSetExpansionOptions() .setFailOnMissingCodeSystem(false); - List candidateCodes = myTerminologySvc.expandValueSet(options, valueSet); - for (VersionIndependentConcept nextCandidate : candidateCodes) { + List candidateCodes = myTerminologySvc.expandValueSet(options, valueSet); + for (FhirVersionIndependentConcept nextCandidate : candidateCodes) { if (nextCandidate.getCode().equals(code)) { retVal = nextCandidate.getSystem(); break; @@ -298,7 +298,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu } } - private Predicate addPredicate(String theResourceName, String theParamName, CriteriaBuilder theBuilder, From theFrom, List theTokens, TokenParamModifier theModifier, SearchBuilderTokenModeEnum theTokenMode, RequestPartitionId theRequestPartitionId) { + private Predicate addPredicate(String theResourceName, String theParamName, CriteriaBuilder theBuilder, From theFrom, List theTokens, TokenParamModifier theModifier, SearchBuilderTokenModeEnum theTokenMode, RequestPartitionId theRequestPartitionId) { if (myDontUseHashesForSearch) { final Path systemExpression = theFrom.get("mySystem"); final Path valueExpression = theFrom.get("myValue"); @@ -317,7 +317,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu orPredicates.add(orPredicate); break; case SYSTEM_AND_VALUE: - for (VersionIndependentConcept next : theTokens) { + for (FhirVersionIndependentConcept next : theTokens) { orPredicates.add(theBuilder.and( toEqualOrIsNullPredicate(systemExpression, next.getSystem()), toEqualOrIsNullPredicate(valueExpression, next.getCode()) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index 753b56b9b4f..176f77a2740 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor; -import ca.uhn.fhir.util.VersionIndependentConcept; import ca.uhn.fhir.util.ValidateUtil; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 85e9afc540c..30553cb86a2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -84,7 +84,7 @@ import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.annotations.VisibleForTesting; @@ -301,7 +301,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } } - private void addConceptsToList(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, String theSystem, List theConcept, boolean theAdd, VersionIndependentConcept theWantConceptOrNull) { + private void addConceptsToList(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, String theSystem, List theConcept, boolean theAdd, FhirVersionIndependentConcept theWantConceptOrNull) { for (CodeSystem.ConceptDefinitionComponent next : theConcept) { if (isNoneBlank(theSystem, next.getCode())) { if (theWantConceptOrNull == null || theWantConceptOrNull.getCode().equals(next.getCode())) { @@ -388,7 +388,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { deleteValueSetForResource(theResourceTable); } - private ValueSet expandValueSetInMemory(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, VersionIndependentConcept theWantConceptOrNull) { + private ValueSet expandValueSetInMemory(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, FhirVersionIndependentConcept theWantConceptOrNull) { int maxCapacity = myDaoConfig.getMaximumExpansionSize(); ValueSetExpansionComponentWithConceptAccumulator expansionComponent = new ValueSetExpansionComponentWithConceptAccumulator(myContext, maxCapacity); @@ -409,7 +409,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public List expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet) { + public List expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet) { // TODO: DM 2019-09-10 - This is problematic because an incorrect URL that matches ValueSet.id will not be found in the terminology tables but will yield a ValueSet here. Depending on the ValueSet, the expansion may time-out. ValueSet valueSet = fetchCanonicalValueSetFromCompleteContext(theValueSet); @@ -554,7 +554,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @SuppressWarnings("ConstantConditions") - private void expandValueSet(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, AtomicInteger theCodeCounter, VersionIndependentConcept theWantConceptOrNull) { + private void expandValueSet(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, AtomicInteger theCodeCounter, FhirVersionIndependentConcept theWantConceptOrNull) { Set addedCodes = new HashSet<>(); StopWatch sw = new StopWatch(); @@ -640,12 +640,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return sb.toString(); } - protected List expandValueSetAndReturnVersionIndependentConcepts(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpandR4, VersionIndependentConcept theWantConceptOrNull) { + protected List expandValueSetAndReturnVersionIndependentConcepts(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpandR4, FhirVersionIndependentConcept theWantConceptOrNull) { org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSetInMemory(theExpansionOptions, theValueSetToExpandR4, theWantConceptOrNull).getExpansion(); - ArrayList retVal = new ArrayList<>(); + ArrayList retVal = new ArrayList<>(); for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent nextContains : expandedR4.getContains()) { - retVal.add(new VersionIndependentConcept(nextContains.getSystem(), nextContains.getCode(), nextContains.getDisplay(), nextContains.getVersion())); + retVal.add(new FhirVersionIndependentConcept(nextContains.getSystem(), nextContains.getCode(), nextContains.getDisplay(), nextContains.getVersion())); } return retVal; } @@ -653,7 +653,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { /** * @return Returns true if there are potentially more results to process. */ - private Boolean expandValueSetHandleIncludeOrExclude(@Nullable ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull) { + private Boolean expandValueSetHandleIncludeOrExclude(@Nullable ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, FhirVersionIndependentConcept theWantConceptOrNull) { String system = theIncludeOrExclude.getSystem(); boolean hasSystem = isNotBlank(system); @@ -691,7 +691,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { // just in case there is a registered service that knows how to handle this. This can happen, for example, // if someone creates a valueset that includes UCUM codes, since we don't have a CodeSystem resource for those // but CommonCodeSystemsTerminologyService can validate individual codes. - List includedConcepts = null; + List includedConcepts = null; if (theWantConceptOrNull != null) { includedConcepts = new ArrayList<>(); includedConcepts.add(theWantConceptOrNull); @@ -699,13 +699,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { includedConcepts = theIncludeOrExclude .getConcept() .stream() - .map(t -> new VersionIndependentConcept(theIncludeOrExclude.getSystem(), t.getCode())) + .map(t -> new FhirVersionIndependentConcept(theIncludeOrExclude.getSystem(), t.getCode())) .collect(Collectors.toList()); } if (includedConcepts != null) { int foundCount = 0; - for (VersionIndependentConcept next : includedConcepts) { + for (FhirVersionIndependentConcept next : includedConcepts) { String nextSystem = next.getSystem(); if (nextSystem == null) { nextSystem = system; @@ -763,10 +763,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { for (CanonicalType nextValueSet : theIncludeOrExclude.getValueSet()) { ourLog.debug("Starting {} expansion around ValueSet: {}", (theAdd ? "inclusion" : "exclusion"), nextValueSet.getValueAsString()); - List expanded = expandValueSet(theExpansionOptions, nextValueSet.getValueAsString()); + List expanded = expandValueSet(theExpansionOptions, nextValueSet.getValueAsString()); Map uriToCodeSystem = new HashMap<>(); - for (VersionIndependentConcept nextConcept : expanded) { + for (FhirVersionIndependentConcept nextConcept : expanded) { if (theAdd) { if (!uriToCodeSystem.containsKey(nextConcept.getSystem())) { @@ -811,7 +811,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Nonnull - private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, VersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs) { + private Boolean expandValueSetHandleIncludeOrExcludeUsingDatabase(IValueSetConceptAccumulator theValueSetCodeAccumulator, Set theAddedCodes, ValueSet.ConceptSetComponent theIncludeOrExclude, boolean theAdd, AtomicInteger theCodeCounter, int theQueryIndex, FhirVersionIndependentConcept theWantConceptOrNull, String theSystem, TermCodeSystem theCs) { String codeSystemVersion = theIncludeOrExclude.getVersion(); TermCodeSystemVersion csv; if (isEmpty(codeSystemVersion)) { @@ -1507,7 +1507,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional @Override - public List findCodesAbove(String theSystem, String theCode) { + public List findCodesAbove(String theSystem, String theCode) { TermCodeSystem cs = getCodeSystem(theSystem); if (cs == null) { return findCodesAboveUsingBuiltInSystems(theSystem, theCode); @@ -1539,7 +1539,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional @Override - public List findCodesBelow(String theSystem, String theCode) { + public List findCodesBelow(String theSystem, String theCode) { TermCodeSystem cs = getCodeSystem(theSystem); if (cs == null) { return findCodesBelowUsingBuiltInSystems(theSystem, theCode); @@ -1939,8 +1939,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { @Transactional public IFhirResourceDaoCodeSystem.SubsumesResult subsumes(IPrimitiveType theCodeA, IPrimitiveType theCodeB, IPrimitiveType theSystem, IBaseCoding theCodingA, IBaseCoding theCodingB) { - VersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); - VersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); + FhirVersionIndependentConcept conceptA = toConcept(theCodeA, theSystem, theCodingA); + FhirVersionIndependentConcept conceptB = toConcept(theCodeB, theSystem, theCodingB); if (!StringUtils.equals(conceptA.getSystem(), conceptB.getSystem())) { throw new InvalidRequestException("Unable to test subsumption across different code systems"); @@ -2044,10 +2044,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return null; } - private ArrayList toVersionIndependentConcepts(String theSystem, Set codes) { - ArrayList retVal = new ArrayList<>(codes.size()); + private ArrayList toVersionIndependentConcepts(String theSystem, Set codes) { + ArrayList retVal = new ArrayList<>(codes.size()); for (TermConcept next : codes) { - retVal.add(new VersionIndependentConcept(theSystem, next.getCode())); + retVal.add(new FhirVersionIndependentConcept(theSystem, next.getCode())); } return retVal; } @@ -2291,7 +2291,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { Pageable page = PageRequest.of(0, 1); - List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, + List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, theTranslationRequest.getUrl().asStringValue()); if (!theConceptMapList.isEmpty()) { return theConceptMapList.get(0).getVersion(); @@ -2324,10 +2324,10 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); - Optional codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new VersionIndependentConcept(theCodeSystem, c.getCode()))); + Optional codeOpt = txTemplate.execute(t -> findCode(theCodeSystem, theCode).map(c -> new FhirVersionIndependentConcept(theCodeSystem, c.getCode()))); if (codeOpt != null && codeOpt.isPresent()) { - VersionIndependentConcept code = codeOpt.get(); + FhirVersionIndependentConcept code = codeOpt.get(); if (!theOptions.isValidateDisplay() || (isNotBlank(code.getDisplay()) && isNotBlank(theDisplay) && code.getDisplay().equals(theDisplay))) { return new CodeValidationResult() .setCode(code.getCode()) @@ -2429,7 +2429,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return myContext; } - private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List theListToPopulate) { + private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List theListToPopulate) { List conceptList = theSystem.getConcept(); for (CodeSystem.ConceptDefinitionComponent next : conceptList) { addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate); @@ -2437,8 +2437,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) { - ArrayList retVal = new ArrayList<>(); + public List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) { + ArrayList retVal = new ArrayList<>(); CodeSystem system = fetchCanonicalCodeSystemFromCompleteContext(theSystem); if (system != null) { findCodesAbove(system, theSystem, theCode, retVal); @@ -2446,12 +2446,12 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return retVal; } - private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List theListToPopulate) { + private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List theListToPopulate) { List conceptList = theSystem.getConcept(); findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList); } - private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { + private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { for (CodeSystem.ConceptDefinitionComponent next : conceptList) { if (theCode.equals(next.getCode())) { addAllChildren(theSystemString, next, theListToPopulate); @@ -2462,8 +2462,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @Override - public List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) { - ArrayList retVal = new ArrayList<>(); + public List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) { + ArrayList retVal = new ArrayList<>(); CodeSystem system = fetchCanonicalCodeSystemFromCompleteContext(theSystem); if (system != null) { findCodesBelow(system, theSystem, theCode, retVal); @@ -2471,23 +2471,23 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return retVal; } - private void addAllChildren(String theSystemString, CodeSystem.ConceptDefinitionComponent theCode, List theListToPopulate) { + private void addAllChildren(String theSystemString, CodeSystem.ConceptDefinitionComponent theCode, List theListToPopulate) { if (isNotBlank(theCode.getCode())) { - theListToPopulate.add(new VersionIndependentConcept(theSystemString, theCode.getCode())); + theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theCode.getCode())); } for (CodeSystem.ConceptDefinitionComponent nextChild : theCode.getConcept()) { addAllChildren(theSystemString, nextChild, theListToPopulate); } } - private boolean addTreeIfItContainsCode(String theSystemString, CodeSystem.ConceptDefinitionComponent theNext, String theCode, List theListToPopulate) { + private boolean addTreeIfItContainsCode(String theSystemString, CodeSystem.ConceptDefinitionComponent theNext, String theCode, List theListToPopulate) { boolean foundCodeInChild = false; for (CodeSystem.ConceptDefinitionComponent nextChild : theNext.getConcept()) { foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate); } if (theCode.equals(theNext.getCode()) || foundCodeInChild) { - theListToPopulate.add(new VersionIndependentConcept(theSystemString, theNext.getCode())); + theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theNext.getCode())); return true; } @@ -2591,7 +2591,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { } @NotNull - private VersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theCodeSystemIdentifierType, IBaseCoding theCodingType) { + private FhirVersionIndependentConcept toConcept(IPrimitiveType theCodeType, IPrimitiveType theCodeSystemIdentifierType, IBaseCoding theCodingType) { String code = theCodeType != null ? theCodeType.getValueAsString() : null; String system = theCodeSystemIdentifierType != null ? getUrlFromIdentifier(theCodeSystemIdentifierType.getValueAsString()): null; String systemVersion = theCodeSystemIdentifierType != null ? getVersionFromIdentifier(theCodeSystemIdentifierType.getValueAsString()): null; @@ -2601,7 +2601,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { system = canonicalizedCoding.getSystem(); systemVersion = canonicalizedCoding.getVersion(); } - return new VersionIndependentConcept(system, code, null, systemVersion); + return new FhirVersionIndependentConcept(system, code, null, systemVersion); } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java index e0f0ef269b5..8cc707f8b22 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java @@ -26,7 +26,7 @@ import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -34,7 +34,6 @@ import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.utilities.validation.ValidationOptions; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nullable; @@ -48,23 +47,23 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { @Autowired private IValidationSupport myValidationSupport; - private void addAllChildren(String theSystemString, org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent theCode, List theListToPopulate) { + private void addAllChildren(String theSystemString, org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent theCode, List theListToPopulate) { if (isNotBlank(theCode.getCode())) { - theListToPopulate.add(new VersionIndependentConcept(theSystemString, theCode.getCode())); + theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theCode.getCode())); } for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent nextChild : theCode.getConcept()) { addAllChildren(theSystemString, nextChild, theListToPopulate); } } - private boolean addTreeIfItContainsCode(String theSystemString, org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent theNext, String theCode, List theListToPopulate) { + private boolean addTreeIfItContainsCode(String theSystemString, org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent theNext, String theCode, List theListToPopulate) { boolean foundCodeInChild = false; for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent nextChild : theNext.getConcept()) { foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate); } if (theCode.equals(theNext.getCode()) || foundCodeInChild) { - theListToPopulate.add(new VersionIndependentConcept(theSystemString, theNext.getCode())); + theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theNext.getCode())); return true; } @@ -96,7 +95,7 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { throw new UnsupportedOperationException(); } - private void findCodesAbove(org.hl7.fhir.dstu2.model.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { + private void findCodesAbove(org.hl7.fhir.dstu2.model.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { List conceptList = theSystem.getCodeSystem().getConcept(); for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent next : conceptList) { addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate); @@ -104,8 +103,8 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { } @Override - public List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) { - ArrayList retVal = new ArrayList<>(); + public List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) { + ArrayList retVal = new ArrayList<>(); org.hl7.fhir.dstu2.model.ValueSet system = (org.hl7.fhir.dstu2.model.ValueSet) myValidationSupport.fetchCodeSystem(theSystem); if (system != null) { findCodesAbove(system, theSystem, theCode, retVal); @@ -113,12 +112,12 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { return retVal; } - private void findCodesBelow(org.hl7.fhir.dstu2.model.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { + private void findCodesBelow(org.hl7.fhir.dstu2.model.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { List conceptList = theSystem.getCodeSystem().getConcept(); findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList); } - private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { + private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent next : conceptList) { if (theCode.equals(next.getCode())) { addAllChildren(theSystemString, next, theListToPopulate); @@ -129,8 +128,8 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { } @Override - public List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) { - ArrayList retVal = new ArrayList<>(); + public List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) { + ArrayList retVal = new ArrayList<>(); org.hl7.fhir.dstu2.model.ValueSet system = (org.hl7.fhir.dstu2.model.ValueSet) myValidationSupport.fetchCodeSystem(theSystem); if (system != null) { findCodesBelow(system, theSystem, theCode, retVal); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java index 4ab16d142d9..33d7355b441 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu3.java @@ -5,15 +5,11 @@ 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.context.support.ValueSetExpansionOptions; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ValidateUtil; -import ca.uhn.fhir.util.VersionIndependentConcept; import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.convertors.conv30_40.CodeSystem30_40; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeableConcept; @@ -23,16 +19,11 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.utilities.validation.ValidationOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Nullable; -import java.util.Optional; -import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.convertors.conv30_40.ValueSet30_40.convertValueSet; /* 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 b945f337a63..465e7f562b5 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 @@ -10,9 +10,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.IValueSetConceptAccumulator; -import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; -import ca.uhn.fhir.model.dstu2.composite.CodingDt; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -21,7 +19,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.utilities.validation.ValidationOptions; import javax.annotation.Nullable; import java.util.List; @@ -71,21 +68,21 @@ public interface ITermReadSvc extends IValidationSupport { void expandValueSet(@Nullable ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator); - List expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet); + List expandValueSet(ValueSetExpansionOptions theExpansionOptions, String theValueSet); Optional findCode(String theCodeSystem, String theCode); Set findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode); - List findCodesAbove(String theSystem, String theCode); + List findCodesAbove(String theSystem, String theCode); - List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode); + List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode); Set findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode); - List findCodesBelow(String theSystem, String theCode); + List findCodesBelow(String theSystem, String theCode); - List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode); + List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode); CodeSystem fetchCanonicalCodeSystemFromCompleteContext(String theSystem); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index b08b4c9ebc6..03d79468ca9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -40,7 +40,7 @@ import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import org.apache.commons.io.IOUtils; import org.hibernate.HibernateException; import org.hibernate.Session; @@ -507,9 +507,9 @@ public abstract class BaseJpaTest extends BaseTest { return retVal; } - protected static Set toCodes(List theConcepts) { + protected static Set toCodes(List theConcepts) { HashSet retVal = new HashSet<>(); - for (VersionIndependentConcept next : theConcepts) { + for (FhirVersionIndependentConcept next : theConcepts) { retVal.add(next.getCode()); } return retVal; 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 7202a38c741..32cee6db0c2 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 @@ -111,7 +111,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { public void testConceptMapFindTermConceptMapByUrl() { Pageable page = PageRequest.of(0, 1); - List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); assertEquals(1, theExpConceptMapList.size()); assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl()); @@ -143,7 +143,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { // should return the latest one which is v2 Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); @@ -171,7 +171,7 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { // should return the latest one which in this case is not versioned Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); 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 e98ba97bd71..da8fab09060 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 @@ -1158,7 +1158,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { public void testConceptMapFindTermConceptMapByUrl() { Pageable page = PageRequest.of(0, 1); - List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + List theExpConceptMapList = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); assertEquals(1, theExpConceptMapList.size()); assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl()); @@ -1190,7 +1190,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { // should return the latest one which is v2 Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); @@ -1218,7 +1218,7 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { // should return the latest one which is v2 Pageable page = PageRequest.of(0, 1); - List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, theUrl); + List theExpSecondOne = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, theUrl); assertEquals(1, theExpSecondOne.size()); assertEquals(theUrl, theExpSecondOne.get(0).getUrl()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index ba515681dc3..332d7e42f2f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -15,7 +15,7 @@ import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import com.google.common.collect.Lists; import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu3.model.CodeSystem; @@ -1719,7 +1719,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { @Test public void testFindCodesAboveBuiltInCodeSystem() { - List concepts; + List concepts; Set codes; concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "active"); @@ -1765,7 +1765,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { @Test public void testFindCodesBelowBuiltInCodeSystem() { - List concepts; + List concepts; Set codes; concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "inactive"); 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 48f4f4465ff..5fe2a780584 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 @@ -322,7 +322,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { @Override protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { Pageable page = PageRequest.of(0, 1); - List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); assertEquals(1, optionalConceptMap.size()); TermConceptMap conceptMap = optionalConceptMap.get(0); @@ -501,7 +501,7 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test { @Override protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) { Pageable page = PageRequest.of(0, 1); - List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByVersion(page, CM_URL); + List optionalConceptMap = myTermConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate(page, CM_URL); assertEquals(1, optionalConceptMap.size()); TermConceptMap conceptMap = optionalConceptMap.get(0); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index d6bad82267d..037e88ffa4c 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.util.VersionIndependentConcept; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import org.apache.commons.lang3.Validate; import org.hl7.fhir.convertors.conv10_50.ValueSet10_50; import org.hl7.fhir.convertors.conv30_50.CodeSystem30_50; @@ -217,7 +217,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu codeSystemToValidateResource = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystemIdentifierToValidate); } - List codesInValueSetExpansion = new ArrayList<>(); + List codes = new ArrayList<>(); switch (theExpansion.getStructureFhirVersionEnum()) { case DSTU2_HL7ORG: { ValueSet expansionVs = (ValueSet) theExpansion; @@ -300,7 +300,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu codeSystemUrlToValidate = theCodeSystemIdentifierToValidate; } } - for (VersionIndependentConcept nextExpansionCode : codesInValueSetExpansion) { + for (FhirVersionIndependentConcept nextExpansionCode : codes) { boolean codeMatches; if (caseSensitive) { @@ -480,7 +480,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu @Nullable private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, Function theCodeSystemLoader, Function theValueSetLoader, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { - Set concepts = new HashSet<>(); + Set concepts = new HashSet<>(); try { expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystemIdentifier, theWantCode); @@ -490,7 +490,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } org.hl7.fhir.r5.model.ValueSet retVal = new org.hl7.fhir.r5.model.ValueSet(); - for (VersionIndependentConcept next : concepts) { + for (FhirVersionIndependentConcept next : concepts) { org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent contains = retVal.getExpansion().addContains(); contains.setSystem(next.getSystem()); contains.setCode(next.getCode()); @@ -501,7 +501,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu return retVal; } - private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set theConcepts, Function theCodeSystemLoader, Function theValueSetLoader, List theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { + private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set theConcepts, Function theCodeSystemLoader, Function theValueSetLoader, List theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { String wantSystemUrl = null; String wantSystemVersion = null; if (theWantSystemIdentifier != null) { @@ -516,7 +516,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent nextInclude : theComposeList) { - List nextCodeList = new ArrayList<>(); + List nextCodeList = new ArrayList<>(); String includeOrExcludeConceptSystemUrl = nextInclude.getSystem(); String includeOrExcludeConceptSystemVersion = nextInclude.getVersion(); if (isNotBlank(includeOrExcludeConceptSystemUrl)) { @@ -612,7 +612,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu throw new ExpansionCouldNotBeCompletedInternallyException(); } for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) { - nextCodeList.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); + nextCodeList.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); } } } @@ -627,11 +627,11 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } - private void addCodes(String theCodeSystemUrl, String theCodeSystemVersion, List theSource, List theTarget, Set theCodeFilter) { + private void addCodes(String theCodeSystemUrl, String theCodeSystemVersion, List theSource, List theTarget, Set theCodeFilter) { for (CodeSystem.ConceptDefinitionComponent next : theSource) { if (isNotBlank(next.getCode())) { if (theCodeFilter == null || theCodeFilter.contains(next.getCode())) { - theTarget.add(new VersionIndependentConcept(theCodeSystemUrl, next.getCode(), next.getDisplay(), theCodeSystemVersion)); + theTarget.add(new FhirVersionIndependentConcept(theCodeSystemUrl, next.getCode(), next.getDisplay(), theCodeSystemVersion)); } } addCodes(theCodeSystemUrl, theCodeSystemVersion, next.getConcept(), theTarget, theCodeFilter); @@ -642,31 +642,31 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } - private static void flattenAndConvertCodesDstu2(List theInput, List theVersionIndependentConcepts) { + private static void flattenAndConvertCodesDstu2(List theInput, List theFhirVersionIndependentConcepts) { for (org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); - flattenAndConvertCodesDstu2(next.getContains(), theVersionIndependentConcepts); + theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay())); + flattenAndConvertCodesDstu2(next.getContains(), theFhirVersionIndependentConcepts); } } - private static void flattenAndConvertCodesDstu3(List theInput, List theVersionIndependentConcepts) { + private static void flattenAndConvertCodesDstu3(List theInput, List theFhirVersionIndependentConcepts) { for (org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); - flattenAndConvertCodesDstu3(next.getContains(), theVersionIndependentConcepts); + theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); + flattenAndConvertCodesDstu3(next.getContains(), theFhirVersionIndependentConcepts); } } - private static void flattenAndConvertCodesR4(List theInput, List theVersionIndependentConcepts) { + private static void flattenAndConvertCodesR4(List theInput, List theFhirVersionIndependentConcepts) { for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); - flattenAndConvertCodesR4(next.getContains(), theVersionIndependentConcepts); + theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); + flattenAndConvertCodesR4(next.getContains(), theFhirVersionIndependentConcepts); } } - private static void flattenAndConvertCodesR5(List theInput, List theVersionIndependentConcepts) { + private static void flattenAndConvertCodesR5(List theInput, List theFhirVersionIndependentConcepts) { for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : theInput) { - theVersionIndependentConcepts.add(new VersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); - flattenAndConvertCodesR5(next.getContains(), theVersionIndependentConcepts); + theFhirVersionIndependentConcepts.add(new FhirVersionIndependentConcept(next.getSystem(), next.getCode(), next.getDisplay(), next.getVersion())); + flattenAndConvertCodesR5(next.getContains(), theFhirVersionIndependentConcepts); } } From 90f198181744253e1231e27c0c54f1a17132c882 Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 30 Sep 2020 18:10:57 -0400 Subject: [PATCH 70/71] Updated tests for missed dstu2 code coverage. --- .../uhn/fhir/jpa/term/TermReadSvcDstu2.java | 26 +++--- .../jpa/term/TerminologySvcImplDstu2Test.java | 89 ++++++++++++++++++- ...erminologyServerValidationSupportTest.java | 58 ++++++++++++ 3 files changed, 159 insertions(+), 14 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java index 8cc707f8b22..71265e45269 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcDstu2.java @@ -47,18 +47,18 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { @Autowired private IValidationSupport myValidationSupport; - private void addAllChildren(String theSystemString, org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent theCode, List theListToPopulate) { + private void addAllChildren(String theSystemString, ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept theCode, List theListToPopulate) { if (isNotBlank(theCode.getCode())) { theListToPopulate.add(new FhirVersionIndependentConcept(theSystemString, theCode.getCode())); } - for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent nextChild : theCode.getConcept()) { + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextChild : theCode.getConcept()) { addAllChildren(theSystemString, nextChild, theListToPopulate); } } - private boolean addTreeIfItContainsCode(String theSystemString, org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent theNext, String theCode, List theListToPopulate) { + private boolean addTreeIfItContainsCode(String theSystemString, ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept theNext, String theCode, List theListToPopulate) { boolean foundCodeInChild = false; - for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent nextChild : theNext.getConcept()) { + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept nextChild : theNext.getConcept()) { foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate); } @@ -95,9 +95,9 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { throw new UnsupportedOperationException(); } - private void findCodesAbove(org.hl7.fhir.dstu2.model.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { - List conceptList = theSystem.getCodeSystem().getConcept(); - for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent next : conceptList) { + private void findCodesAbove(ca.uhn.fhir.model.dstu2.resource.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { + List conceptList = theSystem.getCodeSystem().getConcept(); + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept next : conceptList) { addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate); } } @@ -105,20 +105,20 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { @Override public List findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) { ArrayList retVal = new ArrayList<>(); - org.hl7.fhir.dstu2.model.ValueSet system = (org.hl7.fhir.dstu2.model.ValueSet) myValidationSupport.fetchCodeSystem(theSystem); + ca.uhn.fhir.model.dstu2.resource.ValueSet system = (ca.uhn.fhir.model.dstu2.resource.ValueSet) myValidationSupport.fetchCodeSystem(theSystem); if (system != null) { findCodesAbove(system, theSystem, theCode, retVal); } return retVal; } - private void findCodesBelow(org.hl7.fhir.dstu2.model.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { - List conceptList = theSystem.getCodeSystem().getConcept(); + private void findCodesBelow(ca.uhn.fhir.model.dstu2.resource.ValueSet theSystem, String theSystemString, String theCode, List theListToPopulate) { + List conceptList = theSystem.getCodeSystem().getConcept(); findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList); } - private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { - for (org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent next : conceptList) { + private void findCodesBelow(String theSystemString, String theCode, List theListToPopulate, List conceptList) { + for (ca.uhn.fhir.model.dstu2.resource.ValueSet.CodeSystemConcept next : conceptList) { if (theCode.equals(next.getCode())) { addAllChildren(theSystemString, next, theListToPopulate); } else { @@ -130,7 +130,7 @@ public class TermReadSvcDstu2 extends BaseTermReadSvcImpl { @Override public List findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) { ArrayList retVal = new ArrayList<>(); - org.hl7.fhir.dstu2.model.ValueSet system = (org.hl7.fhir.dstu2.model.ValueSet) myValidationSupport.fetchCodeSystem(theSystem); + ca.uhn.fhir.model.dstu2.resource.ValueSet system = (ca.uhn.fhir.model.dstu2.resource.ValueSet) myValidationSupport.fetchCodeSystem(theSystem); if (system != null) { findCodesBelow(system, theSystem, theCode, retVal); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java index 35a53442c9d..63fadfb48dd 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu2Test.java @@ -1,13 +1,34 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.jpa.dao.dstu2.BaseJpaDstu2Test; +import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import ca.uhn.fhir.model.dstu2.resource.ValueSet; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.util.FhirVersionIndependentConcept; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.r4.model.Coding; import org.junit.jupiter.api.Test; +import org.mockito.Answers; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; import static org.junit.jupiter.api.Assertions.assertEquals; -public class TerminologySvcImplDstu2Test { +public class TerminologySvcImplDstu2Test extends BaseJpaDstu2Test { + + @Autowired + protected ITermReadSvc myTermReadSvc; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + protected ServletRequestDetails mySrd; @Test public void testToCanonicalCoding() { @@ -17,4 +38,70 @@ public class TerminologySvcImplDstu2Test { assertEquals("dstuCode", convertedCoding.getCode()); assertEquals("dstuSystem", convertedCoding.getSystem()); } + + @Test + public void testFindCodesBelowBuiltInCodeSystem() { + List concepts; + Set codes; + + ValueSet upload = new ValueSet(); + upload.setId(new IdDt("testVs")); + upload.setUrl("http://myVs"); + ValueSet.CodeSystem codeSystem = new ValueSet.CodeSystem().setSystem("http://myCs"); + codeSystem.addConcept(new ValueSet.CodeSystemConcept().setCode("codeA").addConcept(new ValueSet.CodeSystemConcept().setCode("codeAB"))); + upload.setCodeSystem(codeSystem); + myValueSetDao.update(upload, mySrd); + + concepts = myTermReadSvc.findCodesBelow("http://myVs", "codeA"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("codeA", "codeAB")); + + concepts = myTermReadSvc.findCodesBelow("http://myVs", "codeAB"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("codeAB")); + + // Unknown code + concepts = myTermReadSvc.findCodesBelow("http://myVs", "FOO"); + codes = toCodes(concepts); + assertThat(codes, empty()); + + // Unknown system + concepts = myTermReadSvc.findCodesBelow("http://myVs2222", "codeA"); + codes = toCodes(concepts); + assertThat(codes, empty()); + } + + @Test + public void testFindCodesAboveBuiltInCodeSystem() { + List concepts; + Set codes; + + ValueSet upload = new ValueSet(); + upload.setId(new IdDt("testVs")); + upload.setUrl("http://myVs"); + ValueSet.CodeSystem codeSystem = new ValueSet.CodeSystem().setSystem("http://myCs"); + codeSystem.addConcept(new ValueSet.CodeSystemConcept().setCode("codeA").addConcept(new ValueSet.CodeSystemConcept().setCode("codeAB"))); + upload.setCodeSystem(codeSystem); + myValueSetDao.update(upload, mySrd); + + concepts = myTermReadSvc.findCodesAbove("http://myVs", "codeA"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("codeA")); + + concepts = myTermReadSvc.findCodesAbove("http://myVs", "codeAB"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("codeA", "codeAB")); + + // Unknown code + concepts = myTermReadSvc.findCodesAbove("http://myVs", "FOO"); + codes = toCodes(concepts); + assertThat(codes, empty()); + + // Unknown system + concepts = myTermReadSvc.findCodesBelow("http://myVs2222", "codeA"); + codes = toCodes(concepts); + assertThat(codes, empty()); + } + + } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupportTest.java index 96136c55255..a081ec087df 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupportTest.java @@ -5,11 +5,15 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.HashMap; +import java.util.Map; + import static org.junit.jupiter.api.Assertions.*; class InMemoryTerminologyServerValidationSupportTest { @@ -54,5 +58,59 @@ class InMemoryTerminologyServerValidationSupportTest { } + @Test + public void testValidateCodeDstu2() { + FhirContext ctxDstu2 = FhirContext.forDstu2Hl7Org(); + PrePopulatedValidationSupportDstu2 dstu2PrePopulated = new PrePopulatedValidationSupportDstu2(ctxDstu2); + mySvc = new InMemoryTerminologyServerValidationSupport(ctxDstu2); + myDefaultSupport = new DefaultProfileValidationSupport(ctxDstu2); + myChain = new ValidationSupportChain(mySvc, dstu2PrePopulated, myDefaultSupport); + + org.hl7.fhir.dstu2.model.ValueSet vs = new org.hl7.fhir.dstu2.model.ValueSet(); + vs.setUrl("http://vs"); + vs + .getCompose() + .addInclude() + .setSystem("http://cs") + .addConcept(new org.hl7.fhir.dstu2.model.ValueSet.ConceptReferenceComponent(new org.hl7.fhir.dstu2.model.CodeType("code1"))) + .addConcept(new org.hl7.fhir.dstu2.model.ValueSet.ConceptReferenceComponent(new org.hl7.fhir.dstu2.model.CodeType("code2"))); + vs.getCodeSystem() + .addConcept(new org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent(new org.hl7.fhir.dstu2.model.CodeType("code1"))) + .addConcept(new org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent(new org.hl7.fhir.dstu2.model.CodeType("code2"))); + + dstu2PrePopulated.addValueSet(vs, "http://vs", "http://cs"); + + ValidationSupportContext valCtx = new ValidationSupportContext(myChain); + ConceptValidationOptions options = new ConceptValidationOptions(); + + IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(valCtx, options, "http://cs", "code1", null, "http://vs"); + assertTrue(outcome.isOk()); + + } + + private static class PrePopulatedValidationSupportDstu2 extends PrePopulatedValidationSupport { + private final Map myDstu2ValueSets; + + PrePopulatedValidationSupportDstu2(FhirContext theFhirContext) { + super(theFhirContext); + myDstu2ValueSets = new HashMap<>(); + } + + public void addValueSet(org.hl7.fhir.dstu2.model.ValueSet theValueSet, String valueSetUrl, String codeSystemUrl) { + myDstu2ValueSets.put(valueSetUrl, theValueSet); + myDstu2ValueSets.put(codeSystemUrl, theValueSet); + } + + @Override + public IBaseResource fetchValueSet(String theUri) { + return myDstu2ValueSets.get(theUri); + } + + @Override + public IBaseResource fetchCodeSystem(String theSystem) { + return myDstu2ValueSets.get(theSystem); + } + + } } From 7899837fe0313a8601d1ee387a2025d52a859a6c Mon Sep 17 00:00:00 2001 From: ianmarshall Date: Wed, 30 Sep 2020 22:04:08 -0400 Subject: [PATCH 71/71] Replaced references to SystemIdentifier with SystemUrlAndVersion. --- ...oryTerminologyServerValidationSupport.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 037e88ffa4c..f9d134ffe39 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -124,12 +124,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu @Override public CodeValidationResult - validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemIdentifier, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { - org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemIdentifier, theCode); + validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersion, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { + org.hl7.fhir.r5.model.ValueSet expansion = expandValueSetToCanonical(theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode); if (expansion == null) { return null; } - return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemIdentifier, theCode, theDisplay, expansion); + return validateCodeInExpandedValueSet(theValidationSupportContext, theOptions, theCodeSystemUrlAndVersion, theCode, theDisplay, expansion); } @@ -208,13 +208,13 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } - private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemIdentifierToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion) { + private CodeValidationResult validateCodeInExpandedValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystemUrlAndVersionToValidate, String theCodeToValidate, String theDisplayToValidate, IBaseResource theExpansion) { assert theExpansion != null; boolean caseSensitive = true; IBaseResource codeSystemToValidateResource = null; - if (!theOptions.isInferSystem() && isNotBlank(theCodeSystemIdentifierToValidate)) { - codeSystemToValidateResource = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystemIdentifierToValidate); + if (!theOptions.isInferSystem() && isNotBlank(theCodeSystemUrlAndVersionToValidate)) { + codeSystemToValidateResource = theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(theCodeSystemUrlAndVersionToValidate); } List codes = new ArrayList<>(); @@ -291,13 +291,13 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu String codeSystemUrlToValidate=null; String codeSystemVersionToValidate=null; - if (theCodeSystemIdentifierToValidate != null) { - int versionIndex = theCodeSystemIdentifierToValidate.indexOf("|"); + if (theCodeSystemUrlAndVersionToValidate != null) { + int versionIndex = theCodeSystemUrlAndVersionToValidate.indexOf("|"); if (versionIndex > -1) { - codeSystemUrlToValidate = theCodeSystemIdentifierToValidate.substring(0, versionIndex); - codeSystemVersionToValidate = theCodeSystemIdentifierToValidate.substring(versionIndex+1); + codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate.substring(0, versionIndex); + codeSystemVersionToValidate = theCodeSystemUrlAndVersionToValidate.substring(versionIndex+1); } else { - codeSystemUrlToValidate = theCodeSystemIdentifierToValidate; + codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate; } } for (FhirVersionIndependentConcept nextExpansionCode : codes) { @@ -332,10 +332,10 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu String message; if ("fragment".equals(codeSystemResourceContentMode)) { severity = ValidationMessage.IssueSeverity.WARNING; - message = "Unknown code in fragment CodeSystem '" + (isNotBlank(theCodeSystemIdentifierToValidate) ? theCodeSystemIdentifierToValidate + "#" : "") + theCodeToValidate + "'"; + message = "Unknown code in fragment CodeSystem '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'"; } else { severity = ValidationMessage.IssueSeverity.ERROR; - message = "Unknown code '" + (isNotBlank(theCodeSystemIdentifierToValidate) ? theCodeSystemIdentifierToValidate + "#" : "") + theCodeToValidate + "'"; + message = "Unknown code '" + (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + theCodeToValidate + "'"; } return new CodeValidationResult() @@ -349,7 +349,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2Hl7Org(ValidationSupportContext theValidationSupportContext, ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) { Function codeSystemLoader = t -> { org.hl7.fhir.dstu2.model.ValueSet codeSystem = (org.hl7.fhir.dstu2.model.ValueSet) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); CodeSystem retVal = new CodeSystem(); @@ -362,12 +362,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu }; org.hl7.fhir.r5.model.ValueSet input = ValueSet10_50.convertValueSet(theInput); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode); return (output); } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu2(ValidationSupportContext theValidationSupportContext, ca.uhn.fhir.model.dstu2.resource.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) { IParser parserRi = FhirContext.forCached(FhirVersionEnum.DSTU2_HL7ORG).newJsonParser(); IParser parserHapi = FhirContext.forCached(FhirVersionEnum.DSTU2).newJsonParser(); @@ -390,7 +390,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu org.hl7.fhir.dstu2.model.ValueSet valueSetRi = parserRi.parseResource(org.hl7.fhir.dstu2.model.ValueSet.class, parserHapi.encodeResourceToString(theInput)); org.hl7.fhir.r5.model.ValueSet input = ValueSet10_50.convertValueSet(valueSetRi); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode); return (output); } @@ -439,7 +439,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetDstu3(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.dstu3.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) { Function codeSystemLoader = t -> { org.hl7.fhir.dstu3.model.CodeSystem codeSystem = (org.hl7.fhir.dstu3.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); return CodeSystem30_50.convertCodeSystem(codeSystem); @@ -450,12 +450,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu }; org.hl7.fhir.r5.model.ValueSet input = ValueSet30_50.convertValueSet(theInput); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode); return (output); } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetR4(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r4.model.ValueSet theInput, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) { Function codeSystemLoader = t -> { org.hl7.fhir.r4.model.CodeSystem codeSystem = (org.hl7.fhir.r4.model.CodeSystem) theValidationSupportContext.getRootValidationSupport().fetchCodeSystem(t); return CodeSystem40_50.convertCodeSystem(codeSystem); @@ -466,7 +466,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu }; org.hl7.fhir.r5.model.ValueSet input = ValueSet40_50.convertValueSet(theInput); - org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemIdentifier, theWantCode); + org.hl7.fhir.r5.model.ValueSet output = expandValueSetR5(theValidationSupportContext, input, codeSystemLoader, valueSetLoader, theWantSystemUrlAndVersion, theWantCode); return (output); } @@ -479,12 +479,12 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } @Nullable - private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, Function theCodeSystemLoader, Function theValueSetLoader, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) { + private org.hl7.fhir.r5.model.ValueSet expandValueSetR5(ValidationSupportContext theValidationSupportContext, org.hl7.fhir.r5.model.ValueSet theInput, Function theCodeSystemLoader, Function theValueSetLoader, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) { Set concepts = new HashSet<>(); try { - expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystemIdentifier, theWantCode); - expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false, theWantSystemIdentifier, theWantCode); + expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getInclude(), true, theWantSystemUrlAndVersion, theWantCode); + expandValueSetR5IncludeOrExclude(theValidationSupportContext, concepts, theCodeSystemLoader, theValueSetLoader, theInput.getCompose().getExclude(), false, theWantSystemUrlAndVersion, theWantCode); } catch (ExpansionCouldNotBeCompletedInternallyException e) { return null; } @@ -501,16 +501,16 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu return retVal; } - private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set theConcepts, Function theCodeSystemLoader, Function theValueSetLoader, List theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemIdentifier, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { + private void expandValueSetR5IncludeOrExclude(ValidationSupportContext theValidationSupportContext, Set theConcepts, Function theCodeSystemLoader, Function theValueSetLoader, List theComposeList, boolean theComposeListIsInclude, @Nullable String theWantSystemUrlAndVersion, @Nullable String theWantCode) throws ExpansionCouldNotBeCompletedInternallyException { String wantSystemUrl = null; String wantSystemVersion = null; - if (theWantSystemIdentifier != null) { - int versionIndex = theWantSystemIdentifier.indexOf("|"); + if (theWantSystemUrlAndVersion != null) { + int versionIndex = theWantSystemUrlAndVersion.indexOf("|"); if (versionIndex > -1) { - wantSystemUrl = theWantSystemIdentifier.substring(0,versionIndex); - wantSystemVersion = theWantSystemIdentifier.substring(versionIndex+1); + wantSystemUrl = theWantSystemUrlAndVersion.substring(0,versionIndex); + wantSystemVersion = theWantSystemUrlAndVersion.substring(versionIndex+1); } else { - wantSystemUrl = theWantSystemIdentifier; + wantSystemUrl = theWantSystemUrlAndVersion; } } @@ -572,7 +572,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu * enumerate a set of good codes for them is a nice compromise there. */ for (org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent next : theComposeList) { - if (Objects.equals(next.getSystem(), theWantSystemIdentifier)) { + if (Objects.equals(next.getSystem(), theWantSystemUrlAndVersion)) { Optional matchingEnumeratedConcept = next.getConcept().stream().filter(t -> Objects.equals(t.getCode(), theWantCode)).findFirst(); if (matchingEnumeratedConcept.isPresent()) { CodeSystem.ConceptDefinitionComponent conceptDefinition = new CodeSystem.ConceptDefinitionComponent() @@ -607,7 +607,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu for (CanonicalType nextValueSetInclude : nextInclude.getValueSet()) { org.hl7.fhir.r5.model.ValueSet vs = theValueSetLoader.apply(nextValueSetInclude.getValueAsString()); if (vs != null) { - org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theCodeSystemLoader, theValueSetLoader, theWantSystemIdentifier, theWantCode); + org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theCodeSystemLoader, theValueSetLoader, theWantSystemUrlAndVersion, theWantCode); if (subExpansion == null) { throw new ExpansionCouldNotBeCompletedInternallyException(); }