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 069117b8ad0..53cccd72a9b 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 @@ -158,6 +158,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -2417,10 +2418,16 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl)); } + // for loinc CodeSystem last version is not necessarily the current anymore, so if no version is present + // we need to query for the current, which is that which version is null if (isNoneBlank(theCodeSystemVersion)) { predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion)); } else { - query.orderBy(criteriaBuilder.desc(root.get("myUpdated"))); + if (theCodeSystemUrl.toLowerCase(Locale.ROOT).contains("loinc")) { + predicates.add(criteriaBuilder.isNull(systemVersionJoin.get("myCodeSystemVersionId"))); + } else { + query.orderBy(criteriaBuilder.desc(root.get("myUpdated"))); + } } Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); 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 e750f1fee3d..6f762f15103 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 @@ -722,7 +722,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { retVal.setPublisher("Regenstrief Institute, Inc."); retVal.setDescription("A value set that includes all LOINC codes"); retVal.setCopyright("This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/"); - retVal.getCompose().addInclude().setSystem(ITermLoaderSvc.LOINC_URI); + retVal.getCompose().addInclude().setSystem(ITermLoaderSvc.LOINC_URI).setVersion(codeSystemVersionId); return retVal; } 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 cba3609fa8b..7cd49e97c45 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 @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.term.loinc; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ContactPoint; import org.hl7.fhir.r4.model.Enumerations; @@ -74,6 +75,9 @@ public abstract class BaseLoincHandler implements IZipContentsHandlerCsv { if (include == null) { include = theVs.getCompose().addInclude(); include.setSystem(theCodeSystemUrl); + if (StringUtils.isNotBlank(theVs.getVersion())) { + include.setVersion(theVs.getVersion()); + } } boolean found = false; 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 cdb97ca48d8..037a2fb0e6b 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 @@ -104,6 +104,7 @@ public class LoincAnswerListHandler extends BaseLoincHandler { .getCompose() .getIncludeFirstRep() .setSystem(ITermLoaderSvc.LOINC_URI) + .setVersion(codeSystemVersionId) .addConcept() .setCode(answerString) .setDisplay(displayText); 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 d0b03f0acc9..d81466f5380 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 @@ -120,6 +120,7 @@ public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IZipCo .getCompose() .getIncludeFirstRep() .setSystem(ITermLoaderSvc.LOINC_URI) + .setVersion(codeSystemVersionId) .addConcept() .setCode(loincNumber) .setDisplay(longCommonName); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java index 21ace9a8c7a..f33e1e17874 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java @@ -22,6 +22,7 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.BeforeEach; @@ -187,13 +188,13 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { private void validateValidateCodeForVersion(String theVersion) { IValidationSupport.CodeValidationResult resultNoVersioned = myCodeSystemDao.validateCode(null, - new UriType(BASE_LOINC_URL), null, new CodeType(VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE), + new UriType(BASE_LOINC_URL), new StringType(theVersion), new CodeType(VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE), null, null, null, null); assertNotNull(resultNoVersioned); assertEquals(prefixWithVersion(theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), resultNoVersioned.getDisplay()); IValidationSupport.CodeValidationResult resultVersioned = myCodeSystemDao.validateCode(null, - new UriType(BASE_LOINC_URL), null, new CodeType(VS_VERSIONED_ON_UPLOAD_FIRST_CODE), + new UriType(BASE_LOINC_URL), new StringType(theVersion), new CodeType(VS_VERSIONED_ON_UPLOAD_FIRST_CODE), null, null, null, null); assertNotNull(resultVersioned); assertEquals(prefixWithVersion(theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), resultVersioned.getDisplay()); @@ -245,10 +246,11 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { ValueSet vs = myValueSetDao.expandByIdentifier(VS_NO_VERSIONED_ON_UPLOAD, null); assertEquals(1, vs.getExpansion().getContains().size()); - // version was added before code display to validate + // version was added prefixing code display to validate assertEquals(prefixWithVersion(currentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), vs.getExpansion().getContains().iterator().next().getDisplay()); + // for CS ver = null, VS ver != null ValueSet vs1 = myValueSetDao.expandByIdentifier(VS_VERSIONED_ON_UPLOAD + "|" + VS_ANSWER_LIST_VERSION, null); assertEquals(3, vs1.getExpansion().getContains().size()); @@ -256,6 +258,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { assertEquals(prefixWithVersion(currentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), vs1.getExpansion().getContains().iterator().next().getDisplay()); + validateExpandedTermConcepts(currentVersion, theAllVersions); // now for each uploaded version @@ -269,29 +272,33 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { "select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" + VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId is null").getSingleResult(); assertNotNull(termConceptNoVerCsvNoVer); - assertEquals(VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY, termConceptNoVerCsvNoVer.getDisplay()); + // data should have version because it was loaded with a version + assertEquals(prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptNoVerCsvNoVer.getDisplay()); @SuppressWarnings("unchecked") TermConcept termConceptVerCsvNoVer = (TermConcept) myEntityManager.createQuery( "select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" + VS_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId is null").getSingleResult(); assertNotNull(termConceptVerCsvNoVer); - assertEquals(VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY, termConceptVerCsvNoVer.getDisplay()); + // data should have version because it was loaded with a version + assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptVerCsvNoVer.getDisplay()); if (theCurrentVersion != null) { @SuppressWarnings("unchecked") TermConcept termConceptNoVerCsvVer = (TermConcept) myEntityManager.createQuery( "select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" + - VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId is null").getSingleResult(); + VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId = '" + theCurrentVersion + "'").getSingleResult(); assertNotNull(termConceptNoVerCsvVer); - assertEquals(VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY, termConceptNoVerCsvVer.getDisplay()); + // data should have version because it was loaded with a version + assertEquals(prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptNoVerCsvVer.getDisplay()); @SuppressWarnings("unchecked") TermConcept termConceptVerCsvVer = (TermConcept) myEntityManager.createQuery( "select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" + - VS_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId is null").getSingleResult(); + VS_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId = '" + theCurrentVersion + "'").getSingleResult(); assertNotNull(termConceptVerCsvVer); - assertEquals(VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY, termConceptVerCsvVer.getDisplay()); + // data should have version because it was loaded with a version + assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptVerCsvVer.getDisplay()); } @@ -443,7 +450,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { * _ current TVSs with upload version have upload-version with no version append * _ current TVSs with no upload version have null version */ - private void runCommonValidations(String theVersion) { + private void runCommonValidations(List theAllVersions) { // for CodeSystem: // _ current CS is present and has no version @@ -452,8 +459,9 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { ourLog.info("CodeSystem:\n" + csString); HashSet shouldNotBePresentVersions = new HashSet<>(possibleVersions); - shouldNotBePresentVersions.remove(theVersion); - shouldNotBePresentVersions.stream().forEach(vv -> assertFalse(csString.contains(vv))); + shouldNotBePresentVersions.removeAll(theAllVersions); + shouldNotBePresentVersions.stream().forEach(vv -> assertFalse(csString.contains(vv), + "Found version string: '" + vv + "' in CodeSystem: " + csString)); // same reading it from term service CodeSystem cs = myITermReadSvc.fetchCanonicalCodeSystemFromCompleteContext(BASE_LOINC_URL); @@ -489,7 +497,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { public void uploadCurrentNoVersion() throws Exception { IIdType csId = uploadLoincCodeSystem(null, true); - runCommonValidations(null); + runCommonValidations(Collections.emptyList()); // validate operation for current (no version parameter) validateOperations(null, Collections.emptySet()); @@ -497,11 +505,11 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { @Test() - public void uploadCurrentWithVersion() throws Exception { + public void uploadWithVersion() throws Exception { String ver = "2.67"; IIdType csId = uploadLoincCodeSystem(ver, true); - runCommonValidations(ver); + runCommonValidations(Collections.singletonList(ver)); // validate operation for specific version validateOperations(ver, Collections.singleton(ver)); @@ -509,15 +517,14 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { @Test - public void uploadCurrentNoVersionThenNoCurrent() throws Exception { + public void uploadNoVersionThenNoCurrent() throws Exception { uploadLoincCodeSystem(null, true); String ver = "2.67"; uploadLoincCodeSystem(ver, false); - myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - - runCommonValidations(ver); +// myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + runCommonValidations(Collections.singletonList(ver)); // validate operation for specific version validateOperations(null, Collections.singleton(ver)); @@ -525,15 +532,14 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { @Test - public void uploadFirstCurrentWithVersionThenNoCurrent() throws Exception { + public void uploadWithVersionThenNoCurrent() throws Exception { String firstVer = "2.67"; uploadLoincCodeSystem(firstVer, true); String secondVer = "2.68"; uploadLoincCodeSystem(secondVer, false); - runCommonValidations(firstVer); - runCommonValidations(secondVer); + runCommonValidations(Lists.newArrayList(firstVer, secondVer)); // validate operation for specific version validateOperations(null, Lists.newArrayList(firstVer, secondVer)); @@ -541,7 +547,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { @Test - public void uploadFirstCurrentNoVersionThenNoCurrentThenCurrent() throws Exception { + public void uploadNoVersionThenNoCurrentThenCurrent() throws Exception { uploadLoincCodeSystem(null, true); String firstVer = "2.67"; @@ -550,8 +556,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { String secondVer = "2.68"; uploadLoincCodeSystem(secondVer, true); - runCommonValidations(firstVer); - runCommonValidations(secondVer); + runCommonValidations(Lists.newArrayList(firstVer, secondVer)); // validate operation for specific version validateOperations(secondVer, Lists.newArrayList(firstVer, secondVer)); @@ -559,7 +564,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { @Test - public void uploadFirstCurrentWithVersionThenNoCurrentThenCurrent() throws Exception { + public void uploadWithVersionThenNoCurrentThenCurrent() throws Exception { String firstVer = "2.67"; uploadLoincCodeSystem(firstVer, true); @@ -569,9 +574,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { String thirdVer = "2.68"; uploadLoincCodeSystem(thirdVer, true); - runCommonValidations(firstVer); - runCommonValidations(secondVer); - runCommonValidations(thirdVer); + runCommonValidations(Lists.newArrayList(firstVer, secondVer, thirdVer)); // validate operation for specific version validateOperations(thirdVer, Lists.newArrayList(firstVer, secondVer, thirdVer));