diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1983-allow-conceptmap-with-no-group-source_or_target.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1983-allow-conceptmap-with-no-group-source_or_target.yaml new file mode 100644 index 00000000000..bf2bd0b832c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_1_0/1983-allow-conceptmap-with-no-group-source_or_target.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 1983 +title: "ConceptMap resources were blocked from uploading into the JPA server if the ConceptMap had a source + and/or target URL defined at the ConceptMap level but not at the group level. This prevented some US Core + resources from being successfully uploaded. This has been corrected." 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 908782c89c1..1aea36fc9d6 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 @@ -1504,6 +1504,13 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return; } + if (source == null && theConceptMap.hasSourceCanonicalType()) { + source = theConceptMap.getSourceCanonicalType().getValueAsString(); + } + if (target == null && theConceptMap.hasTargetCanonicalType()) { + target = theConceptMap.getTargetCanonicalType().getValueAsString(); + } + /* * For now we always delete old versions. At some point, it would be nice to allow configuration to keep old versions. */ @@ -1531,17 +1538,28 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { if (theConceptMap.hasGroup()) { TermConceptMapGroup termConceptMapGroup; for (ConceptMap.ConceptMapGroupComponent group : theConceptMap.getGroup()) { - if (isBlank(group.getSource())) { + + String groupSource = group.getSource(); + if (isBlank(groupSource)) { + groupSource = source; + } + if (isBlank(groupSource)) { throw new UnprocessableEntityException("ConceptMap[url='" + theConceptMap.getUrl() + "'] contains at least one group without a value in ConceptMap.group.source"); } - if (isBlank(group.getTarget())) { + + String groupTarget = group.getTarget(); + if (isBlank(groupTarget)) { + groupTarget = target; + } + if (isBlank(groupTarget)) { throw new UnprocessableEntityException("ConceptMap[url='" + theConceptMap.getUrl() + "'] contains at least one group without a value in ConceptMap.group.target"); } + termConceptMapGroup = new TermConceptMapGroup(); termConceptMapGroup.setConceptMap(termConceptMap); - termConceptMapGroup.setSource(group.getSource()); + termConceptMapGroup.setSource(groupSource); termConceptMapGroup.setSourceVersion(group.getSourceVersion()); - termConceptMapGroup.setTarget(group.getTarget()); + termConceptMapGroup.setTarget(groupTarget); termConceptMapGroup.setTargetVersion(group.getTargetVersion()); myConceptMapGroupDao.save(termConceptMapGroup); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index 3a6598e05ef..4bbe4c92f7e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -127,7 +127,8 @@ public class TestR4Config extends BaseJavaConfigR4 { SLF4JLogLevel level = SLF4JLogLevel.INFO; DataSource dataSource = ProxyDataSourceBuilder .create(retVal) - .logSlowQueryBySlf4j(10, TimeUnit.SECONDS) + .logQueryBySlf4j(level) + .logSlowQueryBySlf4j(10, TimeUnit.SECONDS, level) .beforeQuery(new BlockLargeNumbersOfParamsListener()) .afterQuery(captureQueriesListener()) .afterQuery(new CurrentThreadCaptureQueriesListener()) 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 23a59c9e29b..f03d74debe4 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 @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.model.TranslationRequest; import ca.uhn.fhir.jpa.api.model.TranslationResult; import ca.uhn.fhir.util.TestUtil; 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; @@ -1042,6 +1043,49 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { }); } + /** + * Some US core ConceptMaps use this style, e.g: + * + * http://hl7.org/fhir/us/core/ConceptMap/ndc-cvx + */ + @Test + public void testUploadConceptMapWithOnlyCanonicalSourceAtConceptMapLevel() { + + ConceptMap cm = new ConceptMap(); + cm.setUrl("http://foo"); + cm.setSource(new CanonicalType("http://source")); + cm.setTarget(new CanonicalType("http://target")); + cm.addGroup().addElement().setCode("source1").addTarget().setCode("target1").setEquivalence(ConceptMapEquivalence.EQUAL); + myConceptMapDao.create(cm); + + runInTransaction(()->{ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem("http://source") + .setCode("source1"); + translationRequest.setTarget(new UriType("http://target")); + + ourLog.info("*** About to translate"); + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + ourLog.info("*** Done translating"); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals("equal", translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("target1", concept.getCode()); + assertEquals(null, concept.getDisplay()); + assertEquals("http://target", concept.getSystem()); + }); + + + } + + @Test public void testUploadAndApplyR4DemoConceptMap() throws IOException { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java index df54a97f723..2dfd045bfa3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmTestR4.java @@ -7,14 +7,12 @@ import ca.uhn.fhir.jpa.dao.data.INpmPackageDao; import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao; import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; -import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity; import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.test.utilities.JettyUtil; @@ -96,6 +94,7 @@ public class NpmTestR4 extends BaseJpaR4Test { myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); } + @Test public void testCacheDstu3Package() throws Exception { byte[] bytes = loadClasspathBytes("/packages/nictiz.fhir.nl.stu3.questionnaires-1.0.2.tgz");