diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2786-upload-terminology-command-add-current-version-parameter.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2786-upload-terminology-command-add-current-version-parameter.yaml new file mode 100644 index 00000000000..c65ca45bef9 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2786-upload-terminology-command-add-current-version-parameter.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2786 +title: "Add 'loinc.codesystem.make.current' property to upload-terminology command to allow loading LOINC version + without becoming current." 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 335cef64375..6a01ab94c70 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 @@ -27,14 +27,14 @@ 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.model.cross.IBasePersistedResource; -import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; +import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -52,7 +52,6 @@ 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 { @@ -147,7 +146,7 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao theValueSets, List theConceptMaps) { + public IIdType storeNewCodeSystemVersion(CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, + RequestDetails theRequest, List theValueSets, List theConceptMaps) { assert TransactionSynchronizationManager.isActualTransactionActive(); Validate.notBlank(theCodeSystemResource.getUrl(), "theCodeSystemResource must have a URL"); // Note that this creates the TermCodeSystem and TermCodeSystemVersion entities if needed - IIdType csId = myTerminologyVersionAdapterSvc.createOrUpdateCodeSystem(theCodeSystemResource); + IIdType csId = myTerminologyVersionAdapterSvc.createOrUpdateCodeSystem(theCodeSystemResource, theRequest); ResourcePersistentId codeSystemResourcePid = myIdHelperService.resolveResourcePersistentIds(RequestPartitionId.allPartitions(), csId.getResourceType(), csId.getIdPart()); ResourceTable resource = myResourceTableDao.getOne(codeSystemResourcePid.getIdAsLong()); @@ -396,7 +395,8 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { populateCodeSystemVersionProperties(theCodeSystemVersion, theCodeSystemResource, resource); - storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemResource.getVersion(), theCodeSystemVersion, resource); + storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), + theCodeSystemResource.getVersion(), theCodeSystemVersion, resource, theRequest); myDeferredStorageSvc.addConceptMapsToStorageQueue(theConceptMaps); myDeferredStorageSvc.addValueSetsToStorageQueue(theValueSets); @@ -406,7 +406,9 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { @Override @Transactional - public void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theCodeSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable) { + public void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, + String theSystemName, String theCodeSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, + ResourceTable theCodeSystemResourceTable, RequestDetails theRequestDetails) { assert TransactionSynchronizationManager.isActualTransactionActive(); ourLog.debug("Storing code system"); @@ -468,34 +470,42 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc { codeSystemToStore = myCodeSystemVersionDao.saveAndFlush(codeSystemToStore); } - ourLog.debug("Saving code system"); - - codeSystem.setCurrentVersion(codeSystemToStore); - if (codeSystem.getPid() == null) { - codeSystem = myCodeSystemDao.saveAndFlush(codeSystem); - } - - ourLog.debug("Setting CodeSystemVersion[{}] on {} concepts...", codeSystem.getPid(), totalCodeCount); - - for (TermConcept next : conceptsToSave) { - populateVersion(next, codeSystemToStore); - } - - ourLog.debug("Saving {} concepts...", totalCodeCount); - - IdentityHashMap conceptsStack2 = new IdentityHashMap<>(); - for (TermConcept next : conceptsToSave) { - persistChildren(next, codeSystemToStore, conceptsStack2, totalCodeCount); - } - - ourLog.debug("Done saving concepts, flushing to database"); - - if (myDeferredStorageSvc.isStorageQueueEmpty() == false) { - ourLog.info("Note that some concept saving has been deferred"); + // defaults to true + boolean isMakeVersionCurrent = theRequestDetails == null || + (boolean) theRequestDetails.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE); + if (isMakeVersionCurrent) { + makeCodeSystemCurrentVersion(codeSystem, codeSystemToStore, conceptsToSave, totalCodeCount); } } + + private void makeCodeSystemCurrentVersion(TermCodeSystem theCodeSystem, TermCodeSystemVersion theCodeSystemToStore, + Collection theConceptsToSave, int theTotalCodeCount) { + + theCodeSystem.setCurrentVersion(theCodeSystemToStore); + if (theCodeSystem.getPid() == null) { + theCodeSystem = myCodeSystemDao.saveAndFlush(theCodeSystem); + } + + ourLog.debug("Setting CodeSystemVersion[{}] on {} concepts...", theCodeSystem.getPid(), theTotalCodeCount); + for (TermConcept next : theConceptsToSave) { + populateVersion(next, theCodeSystemToStore); + } + + ourLog.debug("Saving {} concepts...", theTotalCodeCount); + IdentityHashMap conceptsStack2 = new IdentityHashMap<>(); + for (TermConcept next : theConceptsToSave) { + persistChildren(next, theCodeSystemToStore, conceptsStack2, theTotalCodeCount); + } + + ourLog.debug("Done saving concepts, flushing to database"); + if (! myDeferredStorageSvc.isStorageQueueEmpty()) { + ourLog.info("Note that some concept saving has been deferred"); + } + } + + private TermCodeSystemVersion getExistingTermCodeSystemVersion(Long theCodeSystemVersionPid, String theCodeSystemVersion) { TermCodeSystemVersion existing; if (theCodeSystemVersion == null) { 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 cc2ab6e078a..60f2952754c 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 @@ -1,9 +1,95 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.context.FhirContext; +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.entity.TermConceptProperty; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; +import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; +import ca.uhn.fhir.jpa.term.icd10cm.Icd10CmLoader; +import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListLinkHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincConsumerNameHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincDocumentOntologyHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincGroupFileHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincGroupTermsFileHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincHierarchyHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincIeeeMedicalDeviceCodeHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincImagingDocumentCodeHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincLinguisticVariantHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincLinguisticVariantsHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincParentGroupFileHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincPartHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincPartLinkHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincPartRelatedCodeMappingHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincRsnaPlaybookHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsSiHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsUsHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincUniversalOrderSetHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincXmlFileZipContentsHandler; +import ca.uhn.fhir.jpa.term.loinc.PartTypeAndPartName; +import ca.uhn.fhir.jpa.term.snomedct.SctHandlerConcept; +import ca.uhn.fhir.jpa.term.snomedct.SctHandlerDescription; +import ca.uhn.fhir.jpa.term.snomedct.SctHandlerRelationship; +import ca.uhn.fhir.jpa.util.Counter; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.fhir.util.ValidateUtil; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.csv.QuoteMode; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.api.IIdType; +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.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.xml.sax.SAXException; + +import javax.annotation.Nonnull; +import javax.validation.constraints.NotNull; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +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.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +import static ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc.MAKE_LOADING_VERSION_CURRENT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_FILE; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_FILE_DEFAULT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_FILE; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_FILE_DEFAULT; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_MAKE_CURRENT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONSUMER_NAME_FILE; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONSUMER_NAME_FILE_DEFAULT; @@ -49,93 +135,6 @@ import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_UPLOAD_ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.LineNumberReader; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Arrays; -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.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.annotation.Nonnull; -import javax.validation.constraints.NotNull; - -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.apache.commons.csv.QuoteMode; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.IIdType; -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.springframework.aop.support.AopUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.xml.sax.SAXException; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; - -import ca.uhn.fhir.context.FhirContext; -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.entity.TermConceptProperty; -import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; -import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; -import ca.uhn.fhir.jpa.term.icd10cm.Icd10CmLoader; -import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListLinkHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincConsumerNameHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincDocumentOntologyHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincGroupFileHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincGroupTermsFileHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincHierarchyHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincIeeeMedicalDeviceCodeHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincImagingDocumentCodeHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincLinguisticVariantHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincLinguisticVariantsHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincParentGroupFileHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincPartHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincPartLinkHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincPartRelatedCodeMappingHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincRsnaPlaybookHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsSiHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsUsHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincUniversalOrderSetHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincXmlFileZipContentsHandler; -import ca.uhn.fhir.jpa.term.loinc.PartTypeAndPartName; -import ca.uhn.fhir.jpa.term.snomedct.SctHandlerConcept; -import ca.uhn.fhir.jpa.term.snomedct.SctHandlerDescription; -import ca.uhn.fhir.jpa.term.snomedct.SctHandlerRelationship; -import ca.uhn.fhir.jpa.util.Counter; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.util.ClasspathUtil; -import ca.uhn.fhir.util.ValidateUtil; - /* * #%L * HAPI FHIR JPA Server @@ -200,7 +199,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { @Override public UploadStatistics loadImgthla(List theFiles, RequestDetails theRequestDetails) { - try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors descriptors = getLoadedFileDescriptors(theFiles)) { List mandatoryFilenameFragments = Arrays.asList( IMGTHLA_HLA_NOM_TXT, IMGTHLA_HLA_XML @@ -213,11 +212,25 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { } } + @VisibleForTesting + LoadedFileDescriptors getLoadedFileDescriptors(List theFiles) { + return new LoadedFileDescriptors(theFiles); + } + @Override public UploadStatistics loadLoinc(List theFiles, RequestDetails theRequestDetails) { - try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors descriptors = getLoadedFileDescriptors(theFiles)) { Properties uploadProperties = getProperties(descriptors, LOINC_UPLOAD_PROPERTIES_FILE.getCode()); + String codeSystemVersionId = uploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); + boolean isMakeCurrentVersion = Boolean.parseBoolean( + uploadProperties.getProperty(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), "true")); + + if (StringUtils.isBlank(codeSystemVersionId) && ! isMakeCurrentVersion) { + throw new InvalidRequestException("'" + LOINC_CODESYSTEM_VERSION.getCode() + + "' property is required when '" + LOINC_CODESYSTEM_MAKE_CURRENT.getCode() + "' property is 'false'"); + } + List mandatoryFilenameFragments = Arrays.asList( uploadProperties.getProperty(LOINC_ANSWERLIST_FILE.getCode(), LOINC_ANSWERLIST_FILE_DEFAULT.getCode()), uploadProperties.getProperty(LOINC_ANSWERLIST_LINK_FILE.getCode(), LOINC_ANSWERLIST_LINK_FILE_DEFAULT.getCode()), @@ -245,31 +258,35 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { uploadProperties.getProperty(LOINC_PARENT_GROUP_FILE.getCode(), LOINC_PARENT_GROUP_FILE_DEFAULT.getCode()), uploadProperties.getProperty(LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE.getCode(), LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE_DEFAULT.getCode()), uploadProperties.getProperty(LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE.getCode(), LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE_DEFAULT.getCode()), - + //-- optional consumer name uploadProperties.getProperty(LOINC_CONSUMER_NAME_FILE.getCode(), LOINC_CONSUMER_NAME_FILE_DEFAULT.getCode()), uploadProperties.getProperty(LOINC_LINGUISTIC_VARIANTS_FILE.getCode(), LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT.getCode()) - + ); descriptors.verifyOptionalFilesExist(optionalFilenameFragments); ourLog.info("Beginning LOINC processing"); + if (isMakeCurrentVersion) { + if (codeSystemVersionId != null) { + processLoincFiles(descriptors, theRequestDetails, uploadProperties, false); + uploadProperties.remove(LOINC_CODESYSTEM_VERSION.getCode()); + } + ourLog.info("Uploading CodeSystem and making it current version"); - String codeSystemVersionId = uploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); - if (codeSystemVersionId != null) { - // Load the code system with version and then remove the version property. - processLoincFiles(descriptors, theRequestDetails, uploadProperties, false); - uploadProperties.remove(LOINC_CODESYSTEM_VERSION.getCode()); + } else { + ourLog.info("Uploading CodeSystem without updating current version"); } - // Load the same code system with null version. This will become the default version. + + theRequestDetails.getUserData().put(MAKE_LOADING_VERSION_CURRENT, isMakeCurrentVersion); return processLoincFiles(descriptors, theRequestDetails, uploadProperties, true); } } @Override public UploadStatistics loadSnomedCt(List theFiles, RequestDetails theRequestDetails) { - try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors descriptors = getLoadedFileDescriptors(theFiles)) { List expectedFilenameFragments = Arrays.asList( SCT_FILE_DESCRIPTION, @@ -296,7 +313,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); int count = 0; - try (LoadedFileDescriptors compressedDescriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors compressedDescriptors = getLoadedFileDescriptors(theFiles)) { for (FileDescriptor nextDescriptor : compressedDescriptors.getUncompressedFileDescriptors()) { if (nextDescriptor.getFilename().toLowerCase(Locale.US).endsWith(".xml")) { try (InputStream inputStream = nextDescriptor.getInputStream(); @@ -319,7 +336,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { @Override public UploadStatistics loadCustom(String theSystem, List theFiles, RequestDetails theRequestDetails) { - try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors descriptors = getLoadedFileDescriptors(theFiles)) { Optional codeSystemContent = loadFile(descriptors, CUSTOM_CODESYSTEM_JSON, CUSTOM_CODESYSTEM_XML); CodeSystem codeSystem; if (codeSystemContent.isPresent()) { @@ -347,7 +364,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { @Override public UploadStatistics loadDeltaAdd(String theSystem, List theFiles, RequestDetails theRequestDetails) { ourLog.info("Processing terminology delta ADD for system[{}] with files: {}", theSystem, theFiles.stream().map(t -> t.getFilename()).collect(Collectors.toList())); - try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors descriptors = getLoadedFileDescriptors(theFiles)) { CustomTerminologySet terminologySet = CustomTerminologySet.load(descriptors, false); return myCodeSystemStorageSvc.applyDeltaCodeSystemsAdd(theSystem, terminologySet); } @@ -356,7 +373,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { @Override public UploadStatistics loadDeltaRemove(String theSystem, List theFiles, RequestDetails theRequestDetails) { ourLog.info("Processing terminology delta REMOVE for system[{}] with files: {}", theSystem, theFiles.stream().map(t -> t.getFilename()).collect(Collectors.toList())); - try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { + try (LoadedFileDescriptors descriptors = getLoadedFileDescriptors(theFiles)) { CustomTerminologySet terminologySet = CustomTerminologySet.load(descriptors, true); return myCodeSystemStorageSvc.applyDeltaCodeSystemsRemove(theSystem, terminologySet); } @@ -395,8 +412,9 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { } + @VisibleForTesting @NotNull - private Properties getProperties(LoadedFileDescriptors theDescriptors, String thePropertiesFile) { + Properties getProperties(LoadedFileDescriptors theDescriptors, String thePropertiesFile) { Properties retVal = new Properties(); try (InputStream propertyStream = TermLoaderSvcImpl.class.getResourceAsStream("/ca/uhn/fhir/jpa/term/loinc/loincupload.properties")) { @@ -537,7 +555,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { final Map code2concept = new HashMap<>(); final List valueSets = new ArrayList<>(); final List conceptMaps = new ArrayList<>(); - + final List linguisticVariants = new ArrayList<>(); LoincXmlFileZipContentsHandler loincXmlHandler = new LoincXmlFileZipContentsHandler(); @@ -661,7 +679,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { langFileName = linguisticVariant.getLinguisticVariantFileName(); iterateOverZipFileCsvOptional(theDescriptors, theUploadProperties.getProperty(LOINC_LINGUISTIC_VARIANTS_PATH.getCode() + langFileName, LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT.getCode() + langFileName), handler, ',', QuoteMode.NON_NUMERIC, false); } - + if (theCloseFiles) { IOUtils.closeQuietly(theDescriptors); } @@ -684,7 +702,7 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { return new UploadStatistics(conceptCount, target); } - + private ValueSet getValueSetLoincAll(Properties theUploadProperties) { ValueSet retVal = new ValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu2.java index 0cf6b732869..6fad466ce7e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu2.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term; */ import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; @@ -29,7 +30,7 @@ import org.hl7.fhir.r4.model.ValueSet; public class TermVersionAdapterSvcDstu2 implements ITermVersionAdapterSvc { @Override - public IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource) { + public IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { throw new UnsupportedOperationException(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java index 59610787049..2933d256a7b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcDstu3.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.UrlUtil; import org.hl7.fhir.dstu3.model.CodeSystem; @@ -70,7 +71,7 @@ public class TermVersionAdapterSvcDstu3 extends BaseTermVersionAdapterSvcImpl im } @Override - public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { + public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { CodeSystem resourceToStore; try { resourceToStore = convertCodeSystem(theCodeSystemResource); @@ -80,9 +81,9 @@ public class TermVersionAdapterSvcDstu3 extends BaseTermVersionAdapterSvcImpl im validateCodeSystemForStorage(theCodeSystemResource); if (isBlank(resourceToStore.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); - return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId(); + return myCodeSystemResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); } else { - return myCodeSystemResourceDao.update(resourceToStore).getId(); + return myCodeSystemResourceDao.update(resourceToStore, theRequestDetails).getId(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4.java index ebf4f6c009a..be3cf92be7e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR4.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.UrlUtil; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; @@ -59,13 +60,13 @@ public class TermVersionAdapterSvcR4 extends BaseTermVersionAdapterSvcImpl imple } @Override - public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { + public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { validateCodeSystemForStorage(theCodeSystemResource); if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); - return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId(); + return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl, theRequestDetails).getId(); } else { - return myCodeSystemResourceDao.update(theCodeSystemResource).getId(); + return myCodeSystemResourceDao.update(theCodeSystemResource, theRequestDetails).getId(); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java index aad4d0b3792..f0664b99564 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermVersionAdapterSvcR5.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.UrlUtil; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r5.model.CodeSystem; @@ -59,15 +60,15 @@ public class TermVersionAdapterSvcR5 extends BaseTermVersionAdapterSvcImpl imple } @Override - public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { + public IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { validateCodeSystemForStorage(theCodeSystemResource); CodeSystem codeSystemR4 = org.hl7.fhir.convertors.conv40_50.CodeSystem40_50.convertCodeSystem(theCodeSystemResource); if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); - return myCodeSystemResourceDao.update(codeSystemR4, matchUrl).getId(); + return myCodeSystemResourceDao.update(codeSystemR4, matchUrl, theRequestDetails).getId(); } else { - return myCodeSystemResourceDao.update(codeSystemR4).getId(); + return myCodeSystemResourceDao.update(codeSystemR4, theRequestDetails).getId(); } } 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 477f34d5bf1..2c62f3d05b9 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 @@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.term.api; * #L% */ -import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; @@ -28,10 +27,12 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.UploadStatistics; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ValueSet; +import javax.transaction.Transactional; import java.util.List; /** @@ -39,18 +40,48 @@ import java.util.List; */ public interface ITermCodeSystemStorageSvc { + static final String MAKE_LOADING_VERSION_CURRENT = "make.loading.version.current"; + void deleteCodeSystem(TermCodeSystem theCodeSystem); void deleteCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion); - void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable); + + void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, + String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable, + RequestDetails theRequestDetails); + + /** + * Default implementation supports previous signature of method which was added RequestDetails parameter + */ + @Transactional + default void storeNewCodeSystemVersion(ResourcePersistentId theCodeSystemResourcePid, String theSystemUri, String theSystemName, + String theSystemVersionId, TermCodeSystemVersion theCodeSystemVersion, ResourceTable theCodeSystemResourceTable) { + + storeNewCodeSystemVersion(theCodeSystemResourcePid, theSystemUri, theSystemName, theSystemVersionId, + theCodeSystemVersion, theCodeSystemResourceTable, null); + } + /** * @return Returns the ID of the created/updated code system */ - IIdType storeNewCodeSystemVersion(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List theValueSets, List theConceptMaps); + IIdType storeNewCodeSystemVersion(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, + TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List theValueSets, + List theConceptMaps); + + + + void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity, RequestDetails theRequestDetails); + + /** + * Default implementation supports previous signature of method which was added RequestDetails parameter + */ + default void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity) { + storeNewCodeSystemVersionIfNeeded(theCodeSystem, theResourceEntity, null); + } + - void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity); UploadStatistics applyDeltaCodeSystemsAdd(String theSystem, CustomTerminologySet theAdditions); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermVersionAdapterSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermVersionAdapterSvc.java index 40c1c6698f0..1f763f5a6b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermVersionAdapterSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermVersionAdapterSvc.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.term.api; * #L% */ +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; @@ -32,7 +33,11 @@ import org.hl7.fhir.r4.model.ValueSet; */ public interface ITermVersionAdapterSvc { - IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource); + default IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource) { + return createOrUpdateCodeSystem(theCodeSystemResource, null); + } + + IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails); void createOrUpdateConceptMap(ConceptMap theNextConceptMap); 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 d66d29bbdfb..8e2f26f5490 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 @@ -101,6 +101,9 @@ public enum LoincUploadPropertiesEnum { // This is the version identifier for the LOINC code system LOINC_CODESYSTEM_VERSION("loinc.codesystem.version"), + // Indicates if loading version has to become current + LOINC_CODESYSTEM_MAKE_CURRENT("loinc.codesystem.make.current"), + // 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 d2f5a5099c2..be2df017056 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 @@ -65,6 +65,10 @@ loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersVa ## Key may be omitted if only a single version of LOINC is being kept. #loinc.codesystem.version=2.68 +# Indicates if version being loaded has to become current +## Default is true +loinc.codesystem.make.current=true + # This is the version identifier for the answer list file ## Key may be omitted loinc.answerlist.version=Beta.1 @@ -100,4 +104,4 @@ loinc.consumer.name.file=AccessoryFiles/ConsumerName/ConsumerName.csv # Linguistic Variants ## Default value if key not provided: AccessoryFiles/LinguisticVariants/LinguisticVariants.csv ## File may be omitted -loinc.linguistic.variants.file=AccessoryFiles/LinguisticVariants/LinguisticVariants.csv \ No newline at end of file +loinc.linguistic.variants.file=AccessoryFiles/LinguisticVariants/LinguisticVariants.csv 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 c4b556bc6ed..f8d511fa2be 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 @@ -29,6 +29,7 @@ import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; @@ -42,8 +43,10 @@ import org.springframework.transaction.PlatformTransactionManager; import java.util.Set; import java.util.stream.Collectors; +import static ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc.MAKE_LOADING_VERSION_CURRENT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @RequiresDocker @@ -83,6 +86,13 @@ public class FhirResourceDaoR4TerminologyElasticsearchIT extends BaseJpaTest { @Autowired private IBulkDataExportSvc myBulkDataExportSvc; + + @BeforeEach + public void beforeEach() { + when(mySrd.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE)).thenReturn(Boolean.TRUE); + } + + @Test public void testExpandWithIncludeContainingDashesInInclude() { CodeSystem codeSystem = new 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 f1f3fa319ac..6e771593a33 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 @@ -1,9 +1,53 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermConceptProperty; +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; +import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; +import ca.uhn.fhir.jpa.term.loinc.LoincDocumentOntologyHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincIeeeMedicalDeviceCodeHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincImagingDocumentCodeHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincPartRelatedCodeMappingHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincRsnaPlaybookHandler; +import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsSiHandler; +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.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +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.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import static ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc.MAKE_LOADING_VERSION_CURRENT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_DUPLICATE_FILE_DEFAULT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_FILE_DEFAULT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_DUPLICATE_FILE_DEFAULT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_FILE_DEFAULT; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_MAKE_CURRENT; +import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONSUMER_NAME_FILE_DEFAULT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_DOCUMENT_ONTOLOGY_FILE_DEFAULT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_DUPLICATE_FILE_DEFAULT; @@ -30,49 +74,19 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; 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 static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -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.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.jpa.entity.TermConcept; -import ca.uhn.fhir.jpa.entity.TermConceptDesignation; -import ca.uhn.fhir.jpa.entity.TermConceptProperty; -import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; -import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; -import ca.uhn.fhir.jpa.term.loinc.LoincDocumentOntologyHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincIeeeMedicalDeviceCodeHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincImagingDocumentCodeHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincPartRelatedCodeMappingHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincRsnaPlaybookHandler; -import ca.uhn.fhir.jpa.term.loinc.LoincTop2000LabResultsSiHandler; -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; - public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyLoaderSvcLoincTest.class); private TermLoaderSvcImpl mySvc; @@ -99,6 +113,8 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { private ArgumentCaptor> myConceptMapCaptor_267_second; @Captor private ArgumentCaptor> myConceptMapCaptor_268; + @Captor + private ArgumentCaptor myRequestDetailsCaptor; private ZipCollectionBuilder myFiles; @Mock private ITermDeferredStorageSvc myTermDeferredStorageSvc; @@ -169,7 +185,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { verifyLoadLoinc(false, true); } - + private void verifyLoadLoinc() { verifyLoadLoinc(true, false); } @@ -458,7 +474,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { assertEquals(2, vs.getCompose().getInclude().get(0).getConcept().size()); assertEquals("17424-3", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); assertEquals("13006-2", vs.getCompose().getInclude().get(0).getConcept().get(1).getCode()); - + // Consumer Name if (theIncludeConsumerNameAndLinguisticVariants) { code = concepts.get("61438-8"); @@ -473,7 +489,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { verifyLinguisticVariant(code.getDesignations(), "de-AT", "","","","","","","","","","CoV OC43 RNA ql/SM P","Coronavirus OC43 RNA ql. /Sondermaterial PCR"); verifyLinguisticVariant(code.getDesignations(), "fr-CA", "Virus respiratoire syncytial bovin","Présence-Seuil","Temps ponctuel","XXX","Ordinal","Culture spécifique à un microorganisme","Microbiologie","","","",""); verifyLinguisticVariant(code.getDesignations(), "zh-CN", "血流速度.收缩期.最大值","速度","时间点","二尖瓣^胎儿","定量型","超声.多普勒","产科学检查与测量指标.超声","","","僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""); - } + } } @Test @@ -671,7 +687,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { theFiles.addFileZip("/loinc/", LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT.getCode() + "frCA8LinguisticVariant.csv"); } - + public static void addLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles) throws IOException { addBaseLoincMandatoryFilesToZip(theFiles, true); theFiles.addFileZip("/loinc/", LOINC_UPLOAD_PROPERTIES_FILE.getCode()); @@ -717,7 +733,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { theFiles.addFileZip("/loinc/", LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE_DEFAULT.getCode()); theFiles.addFileZip("/loinc/", LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE_DEFAULT.getCode()); } - + } @Test @@ -822,6 +838,110 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { assertEquals("LP52960-9", doublyNestedChildCode.getChildren().get(2).getChild().getCode()); } + + + + @Nested + public class LoadLoincCurrentVersion { + private TermLoaderSvcImpl testedSvc; + private final Properties testProps = new Properties(); + + @Mock private final LoadedFileDescriptors mockFileDescriptors = mock(LoadedFileDescriptors.class); + @SuppressWarnings("unchecked") + @Mock private final List mockFileDescriptorList = mock(List.class); + @Mock private final ITermCodeSystemStorageSvc mockCodeSystemStorageSvc = mock(ITermCodeSystemStorageSvc.class); + private final RequestDetails requestDetails = new ServletRequestDetails(); + + + @BeforeEach + void beforeEach() { + testedSvc = spy(mySvc); + doReturn(testProps).when(testedSvc).getProperties(any(), eq(LOINC_UPLOAD_PROPERTIES_FILE.getCode())); + requestDetails.setOperation(JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM); + } + + + @Test + public void testDontMakeCurrentVersion() throws IOException { + addLoincMandatoryFilesToZip(myFiles); + testProps.put(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), "false"); + testProps.put(LOINC_CODESYSTEM_VERSION.getCode(), "27.0"); + + testedSvc.loadLoinc(myFiles.getFiles(), requestDetails); + + verify(myTermCodeSystemStorageSvc, times(1)).storeNewCodeSystemVersion( + any(CodeSystem.class), any(TermCodeSystemVersion.class), myRequestDetailsCaptor.capture(), any(), any()); + + myRequestDetailsCaptor.getAllValues().forEach( rd -> + assertFalse(rd.getUserData() == null || + (boolean) requestDetails.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE)) + ); + } + + + @Test + public void testMakeCurrentVersionPropertySet() { + testProps.put(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), "true"); + testProps.put(LOINC_CODESYSTEM_VERSION.getCode(), "27.0"); + doReturn(mockFileDescriptors).when(testedSvc).getLoadedFileDescriptors(mockFileDescriptorList); + doReturn(mock(UploadStatistics.class)).when(testedSvc).processLoincFiles( + eq(mockFileDescriptors), eq(requestDetails), eq(testProps), any()); + + testedSvc.loadLoinc(mockFileDescriptorList, requestDetails); + + boolean isMakeCurrent = requestDetails.getUserData() == null || + (boolean) requestDetails.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE); + assertTrue(isMakeCurrent); + } + + + @Test + public void testMakeCurrentVersionByDefaultPropertySet() { + testProps.put(LOINC_CODESYSTEM_VERSION.getCode(), "27.0"); + doReturn(mockFileDescriptors).when(testedSvc).getLoadedFileDescriptors(mockFileDescriptorList); + doReturn(mock(UploadStatistics.class)).when(testedSvc).processLoincFiles( + eq(mockFileDescriptors), eq(requestDetails), eq(testProps), any()); + + testedSvc.loadLoinc(mockFileDescriptorList, requestDetails); + + boolean isMakeCurrent = requestDetails.getUserData() == null || + (boolean) requestDetails.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE); + assertTrue(isMakeCurrent); + } + + + @Test + public void testDontMakeCurrentVersionPropertySet() { + testProps.put(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), "false"); + testProps.put(LOINC_CODESYSTEM_VERSION.getCode(), "27.0"); + doReturn(mockFileDescriptors).when(testedSvc).getLoadedFileDescriptors(mockFileDescriptorList); + doReturn(mock(UploadStatistics.class)).when(testedSvc).processLoincFiles( + eq(mockFileDescriptors), eq(requestDetails), eq(testProps), any()); + + testedSvc.loadLoinc(mockFileDescriptorList, requestDetails); + + boolean isMakeCurrent = requestDetails.getUserData() == null || + (boolean) requestDetails.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE); + assertFalse(isMakeCurrent); + } + + + @Test + public void testNoVersionAndNoMakeCurrentThrows() { + testProps.put(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), "false"); + doReturn(mockFileDescriptors).when(testedSvc).getLoadedFileDescriptors(mockFileDescriptorList); + + InvalidRequestException thrown = Assertions.assertThrows(InvalidRequestException.class, + () -> testedSvc.loadLoinc(mockFileDescriptorList, mySrd) ); + + assertEquals("'" + LOINC_CODESYSTEM_VERSION.getCode() + "' property is required when '" + + LOINC_CODESYSTEM_MAKE_CURRENT.getCode() + "' property is 'false'", thrown.getMessage()); + } + + } + + + private static void verifyConsumerName(Collection designationList, String theConsumerName) { TermConceptDesignation consumerNameDesignation = null; 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 80411d14ad0..f8f01423448 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 @@ -30,6 +30,7 @@ import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; @@ -40,6 +41,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; +import static ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc.MAKE_LOADING_VERSION_CURRENT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.anyCollection; @@ -90,6 +92,13 @@ public class ValueSetExpansionR4ElasticsearchIT extends BaseJpaTest { @Mock private IValueSetConceptAccumulator myValueSetCodeAccumulator; + + @BeforeEach + public void beforeEach() { + when(mySrd.getUserData().getOrDefault(MAKE_LOADING_VERSION_CURRENT, Boolean.TRUE)).thenReturn(Boolean.TRUE); + } + + @AfterEach public void after() { myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE);