diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/8_8_0/7401-fix-smileutil-upload-terminology-not-load-all-concept-properties.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/8_8_0/7401-fix-smileutil-upload-terminology-not-load-all-concept-properties.yaml new file mode 100644 index 000000000000..6296ca1657cd --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/8_8_0/7401-fix-smileutil-upload-terminology-not-load-all-concept-properties.yaml @@ -0,0 +1,8 @@ +--- +type: fix +issue: 7401 +jira: SMILE-11256 +title: "Previously, `smileutil upload-terminology` failed to load and save multiple concept properties that +used the same property code for a single `CodeSystem.concept`. This is now fixed." + + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/PropertyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/PropertyHandler.java index f2897778d56d..78b1eb4440d7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/PropertyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/PropertyHandler.java @@ -22,8 +22,6 @@ import ca.uhn.fhir.jpa.entity.TermConceptProperty; import ca.uhn.fhir.jpa.entity.TermConceptPropertyTypeEnum; import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv; -import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl; -import ca.uhn.fhir.util.ValidateUtil; import org.apache.commons.csv.CSVRecord; import java.util.ArrayList; @@ -50,18 +48,14 @@ public void accept(CSVRecord theRecord) { String code = trim(theRecord.get(CODE)); String key = trim(theRecord.get(KEY)); - if (isNotBlank(code) && isNotBlank(KEY)) { + if (isNotBlank(code) && isNotBlank(key)) { String value = trim(theRecord.get(VALUE)); String type = trim(theRecord.get(TYPE)); List conceptProperties = myCode2Properties.get(code); if (conceptProperties == null) conceptProperties = new ArrayList<>(); - TermConceptProperty conceptProperty = - TermLoaderSvcImpl.getOrCreateConceptProperty(myCode2Properties, code, key); - ValidateUtil.isNotNullOrThrowUnprocessableEntity( - conceptProperty, "Concept property %s not found in file", conceptProperty); - + TermConceptProperty conceptProperty = new TermConceptProperty(); conceptProperty.setKey(key); conceptProperty.setValue(value); // TODO: check this for different types, other types should be added once TermConceptPropertyTypeEnum diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index 5b7cad0ae3dc..a502d88bb04b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -30,6 +30,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.zip.ZipEntry; @@ -318,6 +319,52 @@ public void testApplyDeltaAdd_UsingCsv_withPropertiesCsv() throws IOException { }); } + @Test + public void testApplyDeltaAdd_duplicateConceptPropertyKeys_allPropertiesSaved() throws IOException { + String inputParamJson = loadResource("/custom_term/codeSystem-duplicatePropertyKeys.json"); + Parameters inputParameters = myFhirContext.newJsonParser().parseResource(Parameters.class, inputParamJson); + + LoggingInterceptor interceptor = new LoggingInterceptor(true); + myClient.registerInterceptor(interceptor); + Parameters outcome = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) + .withParameters(inputParameters) + .prettyPrint() + .execute(); + myClient.unregisterInterceptor(interceptor); + + String encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); + ourLog.info(encoded); + assertThat(encoded).containsSubsequence( + "\"name\": \"conceptCount\"", + "\"valueInteger\": 1", + "\"name\": \"target\"", + "\"reference\": \"CodeSystem/" + ); + runInTransaction(() -> { + TermCodeSystem cs = myTermCodeSystemDao.findByCodeSystemUri("http://www.nlm.nih.gov/research/umls/rxnorm_resource"); + TermCodeSystemVersion version = cs.getCurrentVersion(); + Optional optional = myTermConceptDao.findByCodeSystemAndCode(version.getPid(), "748856"); + assertThat(optional).isPresent(); + + TermConcept concept = optional.get(); + Collection properties = concept.getProperties(); + + properties.forEach(prop -> ourLog.info("Property: {} = {}", prop.getKey(), prop.getValue())); + assertThat(properties).hasSize(3); + + // Check that a property with key "STY" appears twice + List valuesForSTY = properties.stream() + .filter(p -> "STY".equalsIgnoreCase(p.getKey())) + .map(TermConceptProperty::getValue) + .toList(); + assertThat(valuesForSTY).hasSize(2); + assertThat(valuesForSTY).containsExactlyInAnyOrder("STN:A1.3.1.1", "STY:Drug Delivery Device"); + }); + } + @Test public void testApplyDeltaAdd_UsingCodeSystem() { CodeSystem codeSystem = new CodeSystem(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/resources/custom_term/codeSystem-duplicatePropertyKeys.json b/hapi-fhir-jpaserver-test-utilities/src/main/resources/custom_term/codeSystem-duplicatePropertyKeys.json new file mode 100644 index 000000000000..e509e303350b --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/resources/custom_term/codeSystem-duplicatePropertyKeys.json @@ -0,0 +1,35 @@ +{ + "resourceType": "Parameters", + "parameter": [ + { + "name": "system", + "valueUri": "http://www.nlm.nih.gov/research/umls/rxnorm_resource" + }, + { + "name": "codeSystem", + "resource": { + "resourceType": "CodeSystem", + "concept": [ + { + "code": "748856", + "display": "{24 (drospirenone 3 MG / ethinyl estradiol 0.02 MG Oral Tablet) / 4 (inert ingredients 1 MG Oral Tablet) } Pack [Yaz 28 Day]", + "property": [ + { + "code": "STY", + "valueString": "STN:A1.3.1.1" + }, + { + "code": "STY", + "valueString": "STY:Drug Delivery Device" + }, + { + "code": "TTY", + "valueString": "BPCK" + } + ] + } + ] + } + } + ] +}