From d97fb8f5cf26f1ab751a7401e3cf2abb9d491726 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Thu, 10 May 2018 10:52:56 -0400 Subject: [PATCH] 709 the conceptmap operation called translate needs to be implemented (#923) The ConceptMap operation $translate has been implemented. --- .../main/java/ca/uhn/fhir/util/TestUtil.java | 1 - .../ca/uhn/fhir/i18n/hapi-messages.properties | 6 +- hapi-fhir-jpaserver-base/pom.xml | 10 + .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 12 +- .../java/ca/uhn/fhir/jpa/dao/DaoConfig.java | 26 + .../jpa/dao/IFhirResourceDaoConceptMap.java | 30 + .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 42 +- .../fhir/jpa/dao/data/ITermConceptMapDao.java | 41 + .../jpa/dao/data/ITermConceptMapGroupDao.java | 33 + .../data/ITermConceptMapGroupElementDao.java | 33 + .../ITermConceptMapGroupElementTargetDao.java | 33 + .../dstu3/FhirResourceDaoConceptMapDstu3.java | 176 ++ .../dao/r4/FhirResourceDaoConceptMapR4.java | 167 ++ .../ca/uhn/fhir/jpa/entity/TermConcept.java | 3 +- .../uhn/fhir/jpa/entity/TermConceptMap.java | 105 ++ .../fhir/jpa/entity/TermConceptMapGroup.java | 131 ++ .../entity/TermConceptMapGroupElement.java | 144 ++ .../TermConceptMapGroupElementTarget.java | 146 ++ ...aseJpaResourceProviderConceptMapDstu3.java | 153 ++ .../BaseJpaResourceProviderConceptMapR4.java | 142 ++ .../jpa/term/BaseHapiTerminologySvcImpl.java | 362 +++- .../jpa/term/HapiTerminologySvcDstu3.java | 4 +- .../fhir/jpa/term/IHapiTerminologySvc.java | 10 +- .../uhn/fhir/jpa/term/TranslationMatch.java | 74 + .../uhn/fhir/jpa/term/TranslationQuery.java | 128 ++ .../uhn/fhir/jpa/term/TranslationRequest.java | 155 ++ .../uhn/fhir/jpa/term/TranslationResult.java | 88 + .../ca/uhn/fhir/jpa/util/JpaConstants.java | 4 + .../jpa/util/ScrollableResultsIterator.java | 63 + .../java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java | 9 + .../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 43 +- .../FhirResourceDaoDstu3ConceptMapTest.java | 84 + .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 150 +- .../r4/FhirResourceDaoR4ConceptMapTest.java | 980 ++++++++++ .../dstu3/BaseResourceProviderDstu3Test.java | 54 + .../ResourceProviderDstu3ConceptMapTest.java | 89 + .../r4/BaseResourceProviderR4Test.java | 54 + .../r4/ResourceProviderR4ConceptMapTest.java | 1657 +++++++++++++++++ .../jpa/term/TerminologySvcImplDstu3Test.java | 1 - .../jpa/term/TerminologySvcImplR4Test.java | 1401 ++++++++++++++ .../PublicSecurityInterceptor.java | 2 +- .../resources/vm/jpa_resource_provider.vm | 8 +- .../src/main/resources/vm/jpa_spring_beans.vm | 8 +- .../resources/vm/jpa_spring_beans_java.vm | 10 +- src/changes/changes.xml | 6 +- 45 files changed, 6804 insertions(+), 74 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoConceptMap.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementTargetDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoConceptMapDstu3.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationMatch.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationQuery.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationRequest.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationResult.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TestUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TestUtil.java index 351f201e7ac..a75e36357e1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TestUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TestUtil.java @@ -30,7 +30,6 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; -import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicInteger; 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 db8bc3910d4..d1540251b79 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 @@ -91,7 +91,11 @@ ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number pr ca.uhn.fhir.jpa.provider.BaseJpaProvider.cantCombintAtAndSince=Unable to combine _at and _since parameters for history operation +ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1} ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateCodeSystemUri=Can not create multiple code systems with URI "{0}", already have one with resource ID: {1} ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! -ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! +ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.matchesFound=Matches found! +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! diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 3a3b8566e53..716551302f6 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -469,6 +469,16 @@ test + + com.github.ben-manes.caffeine + caffeine + + + com.google.guava + guava-testlib + test + + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index a42f359a19c..03a7663b141 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -79,13 +79,8 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nullable; import javax.persistence.*; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; @@ -335,6 +330,13 @@ public abstract class BaseHapiFhirDao implements IDao { doExpungeEverythingQuery("DELETE from " + TermConceptParentChildLink.class.getSimpleName() + " d"); return null; }); + txTemplate.execute(t -> { + doExpungeEverythingQuery("DELETE from " + TermConceptMapGroupElementTarget.class.getSimpleName() + " d"); + doExpungeEverythingQuery("DELETE from " + TermConceptMapGroupElement.class.getSimpleName() + " d"); + doExpungeEverythingQuery("DELETE from " + TermConceptMapGroup.class.getSimpleName() + " d"); + doExpungeEverythingQuery("DELETE from " + TermConceptMap.class.getSimpleName() + " d"); + return null; + }); txTemplate.execute(t -> { doExpungeEverythingQuery("DELETE from " + TermConceptProperty.class.getSimpleName() + " d"); doExpungeEverythingQuery("DELETE from " + TermConceptDesignation.class.getSimpleName() + " d"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index efa2021cb6f..88d005a4692 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -57,6 +57,16 @@ public class DaoConfig { */ private static final Integer DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = null; private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED; + /** + * Default value for {@link #setTranslationCachesExpireAfterWriteInMinutes(Long)}: 60 minutes + * + * @see #setTranslationCachesExpireAfterWriteInMinutes(Long) + */ + public static final Long DEFAULT_TRANSLATION_CACHES_EXPIRE_AFTER_WRITE_IN_MINUTES = 60L; + /** + * update setter javadoc if default changes + */ + private Long myTranslationCachesExpireAfterWriteInMinutes = DEFAULT_TRANSLATION_CACHES_EXPIRE_AFTER_WRITE_IN_MINUTES; /** * update setter javadoc if default changes */ @@ -557,6 +567,22 @@ public class DaoConfig { myReuseCachedSearchResultsForMillis = theReuseCachedSearchResultsForMillis; } + /** + * Specifies the duration in minutes for which values will be retained after being + * written to the terminology translation cache. Defaults to 60. + */ + public Long getTranslationCachesExpireAfterWriteInMinutes() { + return myTranslationCachesExpireAfterWriteInMinutes; + } + + /** + * Specifies the duration in minutes for which values will be retained after being + * written to the terminology translation cache. Defaults to 60. + */ + public void setTranslationCachesExpireAfterWriteInMinutes(Long translationCachesExpireAfterWriteInMinutes) { + myTranslationCachesExpireAfterWriteInMinutes = translationCachesExpireAfterWriteInMinutes; + } + /** * This setting may be used to advise the server that any references found in * resources that have any of the base URLs given here will be replaced with diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoConceptMap.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoConceptMap.java new file mode 100644 index 00000000000..64aed2891ff --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoConceptMap.java @@ -0,0 +1,30 @@ +package ca.uhn.fhir.jpa.dao; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.instance.model.api.IBaseResource; + +public interface IFhirResourceDaoConceptMap extends IFhirResourceDao { + TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index e4e3cf21a70..e208c64e1aa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.term.VersionIndependentConcept; import ca.uhn.fhir.jpa.util.BaseIterator; +import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.model.api.*; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; @@ -59,9 +60,6 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.tuple.Pair; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; -import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.internal.SessionImpl; import org.hibernate.query.Query; import org.hl7.fhir.dstu3.model.BaseResource; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -2193,7 +2191,7 @@ public class SearchBuilder implements ISearchBuilder { Query hibernateQuery = (Query) query; hibernateQuery.setFetchSize(myFetchSize); ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - myResultsIterator = new ScrollableResultsIterator(scroll); + myResultsIterator = new ScrollableResultsIterator<>(scroll); // If the query resulted in extra results being requested if (myAlsoIncludePids != null) { @@ -2275,42 +2273,6 @@ public class SearchBuilder implements ISearchBuilder { } } - public class ScrollableResultsIterator extends BaseIterator implements Iterator { - - private Long myNext; - private ScrollableResults myScroll; - - public ScrollableResultsIterator(ScrollableResults theScroll) { - myScroll = theScroll; - } - - private void ensureHaveNext() { - if (myNext == null) { - if (myScroll.next()) { - myNext = (Long) myScroll.get(0); - } else { - myNext = NO_MORE; - } - } - } - - @Override - public boolean hasNext() { - ensureHaveNext(); - return myNext != NO_MORE; - } - - @Override - public Long next() { - ensureHaveNext(); - Validate.isTrue(myNext != NO_MORE); - Long next = myNext; - myNext = null; - return next; - } - - } - private class UniqueIndexIterator implements Iterator { private final Set myUniqueQueryStrings; private Iterator myWrap = null; 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 new file mode 100644 index 00000000000..e5a037dcd84 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapDao.java @@ -0,0 +1,41 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.TermConceptMap; +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; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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% + */ + +public interface ITermConceptMapDao extends JpaRepository { + @Query("DELETE FROM TermConceptMap cm WHERE cm.myId = :pid") + @Modifying + void deleteTermConceptMapById(@Param("pid") Long theId); + + @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid") + Optional findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid); + + @Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url") + Optional findTermConceptMapByUrl(@Param("url") String theUrl); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupDao.java new file mode 100644 index 00000000000..ef5e83af470 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupDao.java @@ -0,0 +1,33 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; +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; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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% + */ + +public interface ITermConceptMapGroupDao extends JpaRepository { + @Query("DELETE FROM TermConceptMapGroup g WHERE g.myConceptMap.myId = :pid") + @Modifying + void deleteTermConceptMapGroupById(@Param("pid") Long theId); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementDao.java new file mode 100644 index 00000000000..0a659e3a510 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementDao.java @@ -0,0 +1,33 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; +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; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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% + */ + +public interface ITermConceptMapGroupElementDao extends JpaRepository { + @Query("DELETE FROM TermConceptMapGroupElement e WHERE e.myConceptMapGroup.myConceptMap.myId = :pid") + @Modifying + void deleteTermConceptMapGroupElementById(@Param("pid") Long theId); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementTargetDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementTargetDao.java new file mode 100644 index 00000000000..0900ce61a71 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptMapGroupElementTargetDao.java @@ -0,0 +1,33 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; +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; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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% + */ + +public interface ITermConceptMapGroupElementTargetDao extends JpaRepository { + @Query("DELETE FROM TermConceptMapGroupElementTarget t WHERE t.myConceptMapGroupElement.myConceptMapGroup.myConceptMap.myId = :pid") + @Modifying + void deleteTermConceptMapGroupElementTargetById(@Param("pid") Long theId); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoConceptMapDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoConceptMapDstu3.java new file mode 100644 index 00000000000..73d0f59a5e4 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoConceptMapDstu3.java @@ -0,0 +1,176 @@ +package ca.uhn.fhir.jpa.dao.dstu3; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.term.TranslationMatch; +import ca.uhn.fhir.jpa.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.jpa.entity.ResourceTable; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.hl7.fhir.dstu3.model.ConceptMap; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.*; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class FhirResourceDaoConceptMapDstu3 extends FhirResourceDaoDstu3 implements IFhirResourceDaoConceptMap { + @Autowired + private IHapiTerminologySvc myHapiTerminologySvc; + + @Override + public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) { + if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) { + return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest)); + } + + return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest)); + } + + private TranslationResult buildTranslationResult(List theTargets) { + TranslationResult retVal = new TranslationResult(); + + String msg; + if (theTargets.isEmpty()) { + + retVal.setResult(new BooleanType(false)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapDstu3.class, + "noMatchesFound"); + + retVal.setMessage(new StringType(msg)); + + } else { + + retVal.setResult(new BooleanType(true)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapDstu3.class, + "matchesFound"); + + retVal.setMessage(new StringType(msg)); + + TranslationMatch translationMatch; + Set targetsToReturn = new HashSet<>(); + for (TermConceptMapGroupElementTarget target : theTargets) { + if (targetsToReturn.add(target)) { + translationMatch = new TranslationMatch(); + + translationMatch.setEquivalence(new CodeType(target.getEquivalence().toCode())); + + translationMatch.setConcept( + new Coding() + .setCode(target.getCode()) + .setSystem(target.getSystem()) + .setVersion(target.getSystemVersion()) + .setDisplay(target.getDisplay()) + ); + + translationMatch.setSource(new UriType(target.getConceptMapUrl())); + + retVal.addMatch(translationMatch); + } + } + } + + return retVal; + } + + private TranslationResult buildReverseTranslationResult(List theElements) { + TranslationResult retVal = new TranslationResult(); + + String msg; + if (theElements.isEmpty()) { + + retVal.setResult(new BooleanType(false)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapDstu3.class, + "noMatchesFound"); + + retVal.setMessage(new StringType(msg)); + + } else { + + retVal.setResult(new BooleanType(true)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapDstu3.class, + "matchesFound"); + + retVal.setMessage(new StringType(msg)); + + TranslationMatch translationMatch; + Set elementsToReturn = new HashSet<>(); + for (TermConceptMapGroupElement element : theElements) { + if (elementsToReturn.add(element)) { + translationMatch = new TranslationMatch(); + + translationMatch.setConcept( + new Coding() + .setCode(element.getCode()) + .setSystem(element.getSystem()) + .setVersion(element.getSystemVersion()) + .setDisplay(element.getDisplay()) + ); + + translationMatch.setSource(new UriType(element.getConceptMapUrl())); + + retVal.addMatch(translationMatch); + } + } + } + + return retVal; + } + + @Override + protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); + + ConceptMap conceptMap = (ConceptMap) theResource; + + if (conceptMap != null && isNotBlank(conceptMap.getUrl())) { + // Convert from DSTU3 to R4 + try { + myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, VersionConvertor_30_40.convertConceptMap(conceptMap)); + } catch (FHIRException fe) { + throw new InternalErrorException(fe); + } + } + + return retVal; + } +} 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 new file mode 100644 index 00000000000..abe808206cf --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoConceptMapR4.java @@ -0,0 +1,167 @@ +package ca.uhn.fhir.jpa.dao.r4; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.entity.ResourceTable; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; +import ca.uhn.fhir.jpa.term.TranslationMatch; +import ca.uhn.fhir.jpa.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.*; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class FhirResourceDaoConceptMapR4 extends FhirResourceDaoR4 implements IFhirResourceDaoConceptMap { + @Autowired + private IHapiTerminologySvc myHapiTerminologySvc; + + @Override + public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) { + if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) { + return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest)); + } + + return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest)); + } + + private TranslationResult buildTranslationResult(List theTargets) { + TranslationResult retVal = new TranslationResult(); + + String msg; + if (theTargets.isEmpty()) { + + retVal.setResult(new BooleanType(false)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapR4.class, + "noMatchesFound"); + + retVal.setMessage(new StringType(msg)); + + } else { + + retVal.setResult(new BooleanType(true)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapR4.class, + "matchesFound"); + + retVal.setMessage(new StringType(msg)); + + TranslationMatch translationMatch; + Set targetsToReturn = new HashSet<>(); + for (TermConceptMapGroupElementTarget target : theTargets) { + if (targetsToReturn.add(target)) { + translationMatch = new TranslationMatch(); + + translationMatch.setEquivalence(new CodeType(target.getEquivalence().toCode())); + + translationMatch.setConcept( + new Coding() + .setCode(target.getCode()) + .setSystem(target.getSystem()) + .setVersion(target.getSystemVersion()) + .setDisplay(target.getDisplay()) + ); + + translationMatch.setSource(new UriType(target.getConceptMapUrl())); + + retVal.addMatch(translationMatch); + } + } + } + + return retVal; + } + + private TranslationResult buildReverseTranslationResult(List theElements) { + TranslationResult retVal = new TranslationResult(); + + String msg; + if (theElements.isEmpty()) { + + retVal.setResult(new BooleanType(false)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapR4.class, + "noMatchesFound"); + + retVal.setMessage(new StringType(msg)); + + } else { + + retVal.setResult(new BooleanType(true)); + + msg = getContext().getLocalizer().getMessage( + FhirResourceDaoConceptMapR4.class, + "matchesFound"); + + retVal.setMessage(new StringType(msg)); + + TranslationMatch translationMatch; + Set elementsToReturn = new HashSet<>(); + for (TermConceptMapGroupElement element : theElements) { + if (elementsToReturn.add(element)) { + translationMatch = new TranslationMatch(); + + translationMatch.setConcept( + new Coding() + .setCode(element.getCode()) + .setSystem(element.getSystem()) + .setVersion(element.getSystemVersion()) + .setDisplay(element.getDisplay()) + ); + + translationMatch.setSource(new UriType(element.getConceptMapUrl())); + + retVal.addMatch(translationMatch); + } + } + } + + return retVal; + } + + @Override + protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); + + ConceptMap conceptMap = (ConceptMap) theResource; + + if (conceptMap != null && isNotBlank(conceptMap.getUrl())) { + myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, conceptMap); + } + + return retVal; + } +} 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 a329c09df07..b6d5deba6c0 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 @@ -11,7 +11,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.annotations.*; import org.hl7.fhir.r4.model.Coding; -import org.springframework.validation.ValidationUtils; import javax.annotation.Nonnull; import javax.persistence.*; @@ -49,7 +48,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; @Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList = "INDEX_STATUS") }) public class TermConcept implements Serializable { - private static final int MAX_DESC_LENGTH = 400; + protected static final int MAX_DESC_LENGTH = 400; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermConcept.class); private static final long serialVersionUID = 1L; 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 new file mode 100644 index 00000000000..4125bbbf8bf --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java @@ -0,0 +1,105 @@ +package ca.uhn.fhir.jpa.entity; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "TRM_CONCEPT_MAP", uniqueConstraints = { + @UniqueConstraint(name = "IDX_CONCEPT_MAP_URL", columnNames = {"URL"}) +}) +public class TermConceptMap implements Serializable { + @Id() + @SequenceGenerator(name = "SEQ_CONCEPT_MAP_PID", sequenceName = "SEQ_CONCEPT_MAP_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_PID") + @Column(name = "PID") + private Long myId; + + @OneToOne() + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMCONCEPTMAP_RES")) + private ResourceTable myResource; + + @Column(name = "RES_ID", insertable = false, updatable = false) + private Long myResourcePid; + + @Column(name = "SOURCE_URL", nullable = false, length = 200) + private String mySource; + + @Column(name = "TARGET_URL", nullable = false, length = 200) + private String myTarget; + + @Column(name = "URL", length = 200) + private String myUrl; + + @OneToMany(mappedBy = "myConceptMap") + private List myConceptMapGroups; + + public List getConceptMapGroups() { + if (myConceptMapGroups == null) { + myConceptMapGroups = new ArrayList<>(); + } + + return myConceptMapGroups; + } + + public Long getId() { + return myId; + } + + public ResourceTable getResource() { + return myResource; + } + + public void setResource(ResourceTable resource) { + myResource = resource; + } + + public Long getResourcePid() { + return myResourcePid; + } + + public void setResourcePid(Long resourcePid) { + myResourcePid = resourcePid; + } + + public String getSource() { + return mySource; + } + + public void setSource(String source) { + mySource = source; + } + + public String getTarget() { + return myTarget; + } + + public void setTarget(String target) { + myTarget = target; + } + + public String getUrl() { + return myUrl; + } + + public void setUrl(String theUrl) { + myUrl = theUrl; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("myId", myId) + .append("myResource", myResource.toString()) + .append("myResourcePid", myResourcePid) + .append("mySource", mySource) + .append("myTarget", myTarget) + .append("myUrl", myUrl) + .append("myConceptMapGroups - size", myConceptMapGroups.size()) + .toString(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java new file mode 100644 index 00000000000..474fd06d39e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java @@ -0,0 +1,131 @@ +package ca.uhn.fhir.jpa.entity; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "TRM_CONCEPT_MAP_GROUP") +public class TermConceptMapGroup implements Serializable { + @Id() + @SequenceGenerator(name = "SEQ_CONCEPT_MAP_GROUP_PID", sequenceName = "SEQ_CONCEPT_MAP_GROUP_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_GROUP_PID") + @Column(name = "PID") + private Long myId; + + @ManyToOne() + @JoinColumn(name = "CONCEPT_MAP_PID", nullable = false, referencedColumnName = "PID", foreignKey=@ForeignKey(name="FK_TCMGROUP_CONCEPTMAP")) + private TermConceptMap myConceptMap; + + @Column(name = "SOURCE_URL", nullable = false, length = 200) + private String mySource; + + @Column(name = "SOURCE_VERSION", length = 50) + private String mySourceVersion; + + @Column(name = "TARGET_URL", nullable = false, length = 200) + private String myTarget; + + @Column(name = "TARGET_VERSION", length = 50) + private String myTargetVersion; + + @OneToMany(mappedBy = "myConceptMapGroup") + private List myConceptMapGroupElements; + + private String myConceptMapUrl; + private String mySourceValueSet; + private String myTargetValueSet; + + public TermConceptMap getConceptMap() { + return myConceptMap; + } + + public void setConceptMap(TermConceptMap theTermConceptMap) { + myConceptMap = theTermConceptMap; + } + + public List getConceptMapGroupElements() { + if (myConceptMapGroupElements == null) { + myConceptMapGroupElements = new ArrayList<>(); + } + + return myConceptMapGroupElements; + } + + public String getConceptMapUrl() { + if (myConceptMapUrl == null) { + myConceptMapUrl = getConceptMap().getUrl(); + } + return myConceptMapUrl; + } + + public Long getId() { + return myId; + } + + public String getSource() { + return mySource; + } + + public void setSource(String theSource) { + this.mySource = theSource; + } + + public String getSourceValueSet() { + if (mySourceValueSet == null) { + mySourceValueSet = getConceptMap().getSource(); + } + return mySourceValueSet; + } + + public String getSourceVersion() { + return mySourceVersion; + } + + public void setSourceVersion(String theSourceVersion) { + mySourceVersion = theSourceVersion; + } + + public String getTarget() { + return myTarget; + } + + public void setTarget(String theTarget) { + this.myTarget = theTarget; + } + + public String getTargetValueSet() { + if (myTargetValueSet == null) { + myTargetValueSet = getConceptMap().getTarget(); + } + return myTargetValueSet; + } + + public String getTargetVersion() { + return myTargetVersion; + } + + public void setTargetVersion(String theTargetVersion) { + myTargetVersion = theTargetVersion; + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("myId", myId) + .append("myConceptMap - id", myConceptMap.getId()) + .append("mySource", mySource) + .append("mySourceVersion", mySourceVersion) + .append("myTarget", myTarget) + .append("myTargetVersion", myTargetVersion) + .append("myConceptMapGroupElements - size", myConceptMapGroupElements.size()) + .append("myConceptMapUrl", this.getConceptMapUrl()) + .append("mySourceValueSet", this.getSourceValueSet()) + .append("myTargetValueSet", this.getTargetValueSet()) + .toString(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java new file mode 100644 index 00000000000..5e038e2c7dc --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java @@ -0,0 +1,144 @@ +package ca.uhn.fhir.jpa.entity; + +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 javax.persistence.*; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "TRM_CONCEPT_MAP_GRP_ELEMENT", indexes = { + @Index(name = "IDX_CNCPT_MAP_GRP_CD", columnList = "SOURCE_CODE") +}) +public class TermConceptMapGroupElement implements Serializable { + @Id() + @SequenceGenerator(name = "SEQ_CONCEPT_MAP_GRP_ELM_PID", sequenceName = "SEQ_CONCEPT_MAP_GRP_ELM_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_GRP_ELM_PID") + @Column(name = "PID") + private Long myId; + + @ManyToOne() + @JoinColumn(name = "CONCEPT_MAP_GROUP_PID", nullable = false, referencedColumnName = "PID", foreignKey=@ForeignKey(name="FK_TCMGELEMENT_GROUP")) + private TermConceptMapGroup myConceptMapGroup; + + @Column(name = "SOURCE_CODE", nullable = false, length = 50) + private String myCode; + + @Column(name = "SOURCE_DISPLAY", length = TermConcept.MAX_DESC_LENGTH) + private String myDisplay; + + @OneToMany(mappedBy = "myConceptMapGroupElement") + private List myConceptMapGroupElementTargets; + + private String myConceptMapUrl; + private String mySystem; + private String mySystemVersion; + private String myValueSet; + + public String getCode() { + return myCode; + } + + public void setCode(String theCode) { + myCode = theCode; + } + + public TermConceptMapGroup getConceptMapGroup() { + return myConceptMapGroup; + } + + public void setConceptMapGroup(TermConceptMapGroup theTermConceptMapGroup) { + myConceptMapGroup = theTermConceptMapGroup; + } + + public List getConceptMapGroupElementTargets() { + if (myConceptMapGroupElementTargets == null) { + myConceptMapGroupElementTargets = new ArrayList<>(); + } + + return myConceptMapGroupElementTargets; + } + + public String getConceptMapUrl() { + if (myConceptMapUrl == null) { + myConceptMapUrl = getConceptMapGroup().getConceptMap().getUrl(); + } + return myConceptMapUrl; + } + + public String getDisplay() { + return myDisplay; + } + + public void setDisplay(String theDisplay) { + myDisplay = theDisplay; + } + + public Long getId() { + return myId; + } + + public String getSystem() { + if (mySystem == null) { + mySystem = getConceptMapGroup().getSource(); + } + return mySystem; + } + + public String getSystemVersion() { + if (mySystemVersion == null) { + mySystemVersion = getConceptMapGroup().getSourceVersion(); + } + return mySystemVersion; + } + + public String getValueSet() { + if (myValueSet == null) { + myValueSet = getConceptMapGroup().getSourceValueSet(); + } + return myValueSet; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (!(o instanceof TermConceptMapGroupElement)) return false; + + TermConceptMapGroupElement that = (TermConceptMapGroupElement) o; + + return new EqualsBuilder() + .append(getCode(), that.getCode()) + .append(getSystem(), that.getSystem()) + .append(getSystemVersion(), that.getSystemVersion()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getCode()) + .append(getSystem()) + .append(getSystemVersion()) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("myId", myId) + .append("myConceptMapGroup - id", myConceptMapGroup.getId()) + .append("myCode", myCode) + .append("myDisplay", myDisplay) + .append("myConceptMapGroupElementTargets - size", myConceptMapGroupElementTargets.size()) + .append("myConceptMapUrl", this.getConceptMapUrl()) + .append("mySystem", this.getSystem()) + .append("mySystemVersion", this.getSystemVersion()) + .append("myValueSet", this.getValueSet()) + .toString(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java new file mode 100644 index 00000000000..f0a78e49bca --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java @@ -0,0 +1,146 @@ +package ca.uhn.fhir.jpa.entity; + +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.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; + +import javax.persistence.*; +import java.io.Serializable; + +@Entity +@Table(name = "TRM_CONCEPT_MAP_GRP_ELM_TGT", indexes = { + @Index(name = "IDX_CNCPT_MP_GRP_ELM_TGT_CD", columnList = "TARGET_CODE") +}) +public class TermConceptMapGroupElementTarget implements Serializable { + @Id() + @SequenceGenerator(name = "SEQ_CNCPT_MAP_GRP_ELM_TGT_PID", sequenceName = "SEQ_CNCPT_MAP_GRP_ELM_TGT_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CNCPT_MAP_GRP_ELM_TGT_PID") + @Column(name = "PID") + private Long myId; + + @ManyToOne() + @JoinColumn(name = "CONCEPT_MAP_GRP_ELM_PID", nullable = false, referencedColumnName = "PID", foreignKey=@ForeignKey(name="FK_TCMGETARGET_ELEMENT")) + private TermConceptMapGroupElement myConceptMapGroupElement; + + @Column(name = "TARGET_CODE", nullable = false, length = 50) + private String myCode; + + @Column(name = "TARGET_DISPLAY", length = TermConcept.MAX_DESC_LENGTH) + private String myDisplay; + + @Enumerated(EnumType.STRING) + @Column(name = "TARGET_EQUIVALENCE", length = 50) + private ConceptMapEquivalence myEquivalence; + + private String myConceptMapUrl; + private String mySystem; + private String mySystemVersion; + private String myValueSet; + + public String getCode() { + return myCode; + } + + public void setCode(String theCode) { + myCode = theCode; + } + + public TermConceptMapGroupElement getConceptMapGroupElement() { + return myConceptMapGroupElement; + } + + public void setConceptMapGroupElement(TermConceptMapGroupElement theTermConceptMapGroupElement) { + myConceptMapGroupElement = theTermConceptMapGroupElement; + } + + public String getConceptMapUrl() { + if (myConceptMapUrl == null) { + myConceptMapUrl = getConceptMapGroupElement().getConceptMapGroup().getConceptMap().getUrl(); + } + return myConceptMapUrl; + } + + public String getDisplay() { + return myDisplay; + } + + public void setDisplay(String theDisplay) { + myDisplay = theDisplay; + } + + public ConceptMapEquivalence getEquivalence() { + return myEquivalence; + } + + public void setEquivalence(ConceptMapEquivalence theEquivalence) { + myEquivalence = theEquivalence; + } + + public Long getId() { + return myId; + } + + public String getSystem() { + if (mySystem == null) { + mySystem = getConceptMapGroupElement().getConceptMapGroup().getTarget(); + } + return mySystem; + } + + public String getSystemVersion() { + if (mySystemVersion == null) { + mySystemVersion = getConceptMapGroupElement().getConceptMapGroup().getTargetVersion(); + } + return mySystemVersion; + } + + public String getValueSet() { + if (myValueSet == null) { + myValueSet = getConceptMapGroupElement().getConceptMapGroup().getTargetValueSet(); + } + return myValueSet; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (!(o instanceof TermConceptMapGroupElementTarget)) return false; + + TermConceptMapGroupElementTarget that = (TermConceptMapGroupElementTarget) o; + + return new EqualsBuilder() + .append(getCode(), that.getCode()) + .append(getEquivalence(), that.getEquivalence()) + .append(getSystem(), that.getSystem()) + .append(getSystemVersion(), that.getSystemVersion()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getCode()) + .append(getEquivalence()) + .append(getSystem()) + .append(getSystemVersion()) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("myId", myId) + .append("myConceptMapGroupElement - id", myConceptMapGroupElement.getId()) + .append("myCode", myCode) + .append("myDisplay", myDisplay) + .append("myEquivalence", myEquivalence.toCode()) + .append("myConceptMapUrl", this.getConceptMapUrl()) + .append("mySystem", this.getSystem()) + .append("mySystemVersion", this.getSystemVersion()) + .append("myValueSet", this.getValueSet()) + .toString(); + } +} 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 new file mode 100644 index 00000000000..82c766ce353 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderConceptMapDstu3.java @@ -0,0 +1,153 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.jpa.util.JpaConstants; +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 ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.exceptions.FHIRException; + +import javax.servlet.http.HttpServletRequest; + +public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderDstu3 { + @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { + @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), + @OperationParam(name = "message", type = StringType.class, min = 0, max = 1), + }) + public Parameters translate( + HttpServletRequest theServletRequest, + @IdParam(optional = true) IdType theId, + @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, + @OperationParam(name = "source", min = 0, max = 1) UriType theSourceValueSet, + @OperationParam(name = "coding", min = 0, max = 1) Coding theSourceCoding, + @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theSourceCodeableConcept, + @OperationParam(name = "target", min = 0, max = 1) UriType theTargetValueSet, + @OperationParam(name = "targetsystem", min = 0, max = 1) UriType theTargetCodeSystem, + @OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse, + RequestDetails theRequestDetails + ) { + boolean haveSourceCode = theSourceCode != null + && theSourceCode.hasValue(); + boolean haveSourceCodeSystem = theSourceCodeSystem != null + && theSourceCodeSystem.hasValue(); + boolean haveSourceCodeSystemVersion = theSourceCodeSystemVersion != null + && theSourceCodeSystemVersion.hasValue(); + boolean haveSourceValueSet = theSourceValueSet != null + && theSourceValueSet.hasValue(); + boolean haveSourceCoding = theSourceCoding != null + && theSourceCoding.hasCode(); + boolean haveSourceCodeableConcept= theSourceCodeableConcept != null + && theSourceCodeableConcept.hasCoding() + && theSourceCodeableConcept.getCodingFirstRep().hasCode(); + boolean haveTargetValueSet = theTargetValueSet != null + && theTargetValueSet.hasValue(); + boolean haveTargetCodeSystem = theTargetCodeSystem != null + && theTargetCodeSystem.hasValue(); + boolean haveReverse = theReverse != null; + boolean haveId = theId != null && theId.hasIdPart(); + + // + if ((!haveSourceCode && !haveSourceCoding && !haveSourceCodeableConcept) + || moreThanOneTrue(haveSourceCode, haveSourceCoding, haveSourceCodeableConcept)) { + throw new InvalidRequestException("One (and only one) of the in parameters (code, coding, codeableConcept) must be provided, to identify the code that is to be translated."); + } + + TranslationRequest translationRequest = new TranslationRequest(); + try { + // Convert from DSTU3 to R4 + if (haveSourceCode) { + translationRequest.getCodeableConcept().addCoding().setCodeElement(VersionConvertor_30_40.convertCode(theSourceCode)); + + if (haveSourceCodeSystem) { + translationRequest.getCodeableConcept().getCodingFirstRep().setSystemElement(VersionConvertor_30_40.convertUri(theSourceCodeSystem)); + } + + if (haveSourceCodeSystemVersion) { + translationRequest.getCodeableConcept().getCodingFirstRep().setVersionElement(VersionConvertor_30_40.convertString(theSourceCodeSystemVersion)); + } + } else if (haveSourceCoding) { + translationRequest.getCodeableConcept().addCoding(VersionConvertor_30_40.convertCoding(theSourceCoding)); + } else { + translationRequest.setCodeableConcept(VersionConvertor_30_40.convertCodeableConcept(theSourceCodeableConcept)); + } + + if (haveSourceValueSet) { + translationRequest.setSource(VersionConvertor_30_40.convertUri(theSourceValueSet)); + } + + if (haveTargetValueSet) { + translationRequest.setTarget(VersionConvertor_30_40.convertUri(theTargetValueSet)); + } + + if (haveTargetCodeSystem) { + translationRequest.setTargetSystem(VersionConvertor_30_40.convertUri(theTargetCodeSystem)); + } + + if (haveReverse) { + translationRequest.setReverse(VersionConvertor_30_40.convertBoolean(theReverse)); + } + + if (haveId) { + translationRequest.setResourceId(theId.getIdPartAsLong()); + } + } catch (FHIRException fe) { + throw new InternalErrorException(fe); + } + + startRequest(theServletRequest); + try { + IFhirResourceDaoConceptMap dao = (IFhirResourceDaoConceptMap) getDao(); + TranslationResult result = dao.translate(translationRequest, theRequestDetails); + + // Convert from R4 to DSTU3 + return VersionConvertor_30_40.convertParameters(result.toParameters()); + } catch (FHIRException fe) { + throw new InternalErrorException(fe); + } finally { + endRequest(theServletRequest); + } + } + + private static boolean moreThanOneTrue(boolean... theBooleans) { + boolean haveOne = false; + for (boolean next : theBooleans) { + if (next) { + if (haveOne) { + return true; + } else { + haveOne = true; + } + } + } + return false; + } +} 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 new file mode 100644 index 00000000000..57f5cc883c9 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderConceptMapR4.java @@ -0,0 +1,142 @@ +package ca.uhn.fhir.jpa.provider.r4; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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.dao.IFhirResourceDaoConceptMap; +import ca.uhn.fhir.jpa.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.jpa.util.JpaConstants; +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 ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.r4.model.*; + +import javax.servlet.http.HttpServletRequest; + +public class BaseJpaResourceProviderConceptMapR4 extends JpaResourceProviderR4 { + @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { + @OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1), + @OperationParam(name = "message", type = StringType.class, min = 0, max = 1), + }) + public Parameters translate( + HttpServletRequest theServletRequest, + @IdParam(optional = true) IdType theId, + @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, + @OperationParam(name = "source", min = 0, max = 1) UriType theSourceValueSet, + @OperationParam(name = "coding", min = 0, max = 1) Coding theSourceCoding, + @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theSourceCodeableConcept, + @OperationParam(name = "target", min = 0, max = 1) UriType theTargetValueSet, + @OperationParam(name = "targetsystem", min = 0, max = 1) UriType theTargetCodeSystem, + @OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse, + RequestDetails theRequestDetails + ) { + boolean haveSourceCode = theSourceCode != null + && theSourceCode.hasCode(); + boolean haveSourceCodeSystem = theSourceCodeSystem != null + && theSourceCodeSystem.hasValue(); + boolean haveSourceCodeSystemVersion = theSourceCodeSystemVersion != null + && theSourceCodeSystemVersion.hasValue(); + boolean haveSourceValueSet = theSourceValueSet != null + && theSourceValueSet.hasValue(); + boolean haveSourceCoding = theSourceCoding != null + && theSourceCoding.hasCode(); + boolean haveSourceCodeableConcept= theSourceCodeableConcept != null + && theSourceCodeableConcept.hasCoding() + && theSourceCodeableConcept.getCodingFirstRep().hasCode(); + boolean haveTargetValueSet = theTargetValueSet != null + && theTargetValueSet.hasValue(); + boolean haveTargetCodeSystem = theTargetCodeSystem != null + && theTargetCodeSystem.hasValue(); + boolean haveReverse = theReverse != null; + boolean haveId = theId != null && theId.hasIdPart(); + + // + if ((!haveSourceCode && !haveSourceCoding && !haveSourceCodeableConcept) + || moreThanOneTrue(haveSourceCode, haveSourceCoding, haveSourceCodeableConcept)) { + throw new InvalidRequestException("One (and only one) of the in parameters (code, coding, codeableConcept) must be provided, to identify the code that is to be translated."); + } + + TranslationRequest translationRequest = new TranslationRequest(); + + if (haveSourceCode) { + translationRequest.getCodeableConcept().addCoding().setCodeElement(theSourceCode); + + if (haveSourceCodeSystem) { + translationRequest.getCodeableConcept().getCodingFirstRep().setSystemElement(theSourceCodeSystem); + } + + if (haveSourceCodeSystemVersion) { + translationRequest.getCodeableConcept().getCodingFirstRep().setVersionElement(theSourceCodeSystemVersion); + } + } else if (haveSourceCoding) { + translationRequest.getCodeableConcept().addCoding(theSourceCoding); + } else { + translationRequest.setCodeableConcept(theSourceCodeableConcept); + } + + if (haveSourceValueSet) { + translationRequest.setSource(theSourceValueSet); + } + + if (haveTargetValueSet) { + translationRequest.setTarget(theTargetValueSet); + } + + if (haveTargetCodeSystem) { + translationRequest.setTargetSystem(theTargetCodeSystem); + } + + if (haveReverse) { + translationRequest.setReverse(theReverse); + } + + if (haveId) { + translationRequest.setResourceId(theId.getIdPartAsLong()); + } + + startRequest(theServletRequest); + try { + IFhirResourceDaoConceptMap dao = (IFhirResourceDaoConceptMap) getDao(); + TranslationResult result = dao.translate(translationRequest, theRequestDetails); + return result.toParameters(); + } finally { + endRequest(theServletRequest); + } + } + + private static boolean moreThanOneTrue(boolean... theBooleans) { + boolean haveOne = false; + for (boolean next : theBooleans) { + if (next) { + if (haveOne) { + return true; + } else { + haveOne = true; + } + } + } + return false; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 202c0c0bf13..59c4b9f819c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -27,24 +27,32 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.data.*; import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; +import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; 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.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.ValidateUtil; +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 com.google.common.collect.ArrayListMultimap; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils; import org.apache.lucene.search.Query; +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.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; import org.springframework.beans.factory.annotation.Autowired; @@ -58,9 +66,12 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; +import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.*; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -69,14 +80,26 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc { + public static final int DEFAULT_FETCH_SIZE = 250; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvcImpl.class); private static final Object PLACEHOLDER_OBJECT = new Object(); private static boolean ourForceSaveDeferredAlwaysForUnitTest; + private static boolean ourLastResultsFromTranslationCache; // For testing. + private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing. @Autowired protected ITermCodeSystemDao myCodeSystemDao; @Autowired protected ITermConceptDao myConceptDao; @Autowired + protected ITermConceptMapDao myConceptMapDao; + @Autowired + protected ITermConceptMapGroupDao myConceptMapGroupDao; + @Autowired + protected ITermConceptMapGroupElementDao myConceptMapGroupElementDao; + @Autowired + protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao; + @Autowired protected ITermConceptPropertyDao myConceptPropertyDao; @Autowired protected ITermConceptDesignationDao myConceptDesignationDao; @@ -102,6 +125,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc @Autowired(required = false) private IFhirResourceDaoCodeSystem myCodeSystemResourceDao; + private Cache> myTranslationCache; + private Cache> myTranslationWithReverseCache; + + + private int myFetchSize = DEFAULT_FETCH_SIZE; + private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set theAddedCodes, TermConcept theConcept) { if (theAddedCodes.add(theConcept.getCode())) { ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains(); @@ -160,6 +189,23 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return retVal; } + @PostConstruct + public void buildTranslationCaches() { + Long timeout = myDaoConfig.getTranslationCachesExpireAfterWriteInMinutes(); + + myTranslationCache = + Caffeine.newBuilder() + .maximumSize(10000) + .expireAfterWrite(timeout, TimeUnit.MINUTES) + .build(); + + myTranslationWithReverseCache = + Caffeine.newBuilder() + .maximumSize(10000) + .expireAfterWrite(timeout, TimeUnit.MINUTES) + .build(); + } + protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource); protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap); @@ -869,6 +915,112 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return csId; } + @Override + @Transactional + public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) { + ourLog.info("Storing TermConceptMap..."); + + ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied."); + ValidateUtil.isNotBlankOrThrowInvalidRequest(theConceptMap.getUrl(), "No URL supplied."); + + TermConceptMap termConceptMap = new TermConceptMap(); + termConceptMap.setResource(theResourceTable); + termConceptMap.setUrl(theConceptMap.getUrl()); + + // Get existing entity so it can be deleted. + Optional optionalExistingTermConceptMapById = myConceptMapDao.findTermConceptMapByResourcePid(termConceptMap.getResourcePid()); + + /* + * For now we always delete old versions. At some point, it would be nice to allow configuration to keep old versions. + */ + + if (optionalExistingTermConceptMapById.isPresent()) { + Long id = optionalExistingTermConceptMapById.get().getId(); + ourLog.info("Deleting existing TermConceptMap {} and its children...", id); + myConceptMapGroupElementTargetDao.deleteTermConceptMapGroupElementTargetById(id); + myConceptMapGroupElementDao.deleteTermConceptMapGroupElementById(id); + myConceptMapGroupDao.deleteTermConceptMapGroupById(id); + myConceptMapDao.deleteTermConceptMapById(id); + ourLog.info("Done deleting existing TermConceptMap {} and its children.", id); + + ourLog.info("Flushing..."); + myConceptMapGroupElementTargetDao.flush(); + myConceptMapGroupElementDao.flush(); + myConceptMapGroupDao.flush(); + myConceptMapDao.flush(); + ourLog.info("Done flushing."); + } + + /* + * Do the upload. + */ + String conceptMapUrl = termConceptMap.getUrl(); + Optional optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl); + if (!optionalExistingTermConceptMapByUrl.isPresent()) { + try { + String source = theConceptMap.getSourceUriType().getValueAsString(); + if (isNotBlank(source)) { + termConceptMap.setSource(source); + } + String target = theConceptMap.getTargetUriType().getValueAsString(); + if (isNotBlank(target)) { + termConceptMap.setTarget(target); + } + } catch (FHIRException fe) { + throw new InternalErrorException(fe); + } + myConceptMapDao.save(termConceptMap); + + if (theConceptMap.hasGroup()) { + TermConceptMapGroup termConceptMapGroup; + for (ConceptMap.ConceptMapGroupComponent group : theConceptMap.getGroup()) { + termConceptMapGroup = new TermConceptMapGroup(); + termConceptMapGroup.setConceptMap(termConceptMap); + termConceptMapGroup.setSource(group.getSource()); + termConceptMapGroup.setSourceVersion(group.getSourceVersion()); + termConceptMapGroup.setTarget(group.getTarget()); + termConceptMapGroup.setTargetVersion(group.getTargetVersion()); + myConceptMapGroupDao.save(termConceptMapGroup); + + if (group.hasElement()) { + TermConceptMapGroupElement termConceptMapGroupElement; + for (ConceptMap.SourceElementComponent element : group.getElement()) { + termConceptMapGroupElement = new TermConceptMapGroupElement(); + termConceptMapGroupElement.setConceptMapGroup(termConceptMapGroup); + termConceptMapGroupElement.setCode(element.getCode()); + termConceptMapGroupElement.setDisplay(element.getDisplay()); + myConceptMapGroupElementDao.save(termConceptMapGroupElement); + + if (element.hasTarget()) { + TermConceptMapGroupElementTarget termConceptMapGroupElementTarget; + for (ConceptMap.TargetElementComponent target : element.getTarget()) { + termConceptMapGroupElementTarget = new TermConceptMapGroupElementTarget(); + termConceptMapGroupElementTarget.setConceptMapGroupElement(termConceptMapGroupElement); + termConceptMapGroupElementTarget.setCode(target.getCode()); + termConceptMapGroupElementTarget.setDisplay(target.getDisplay()); + termConceptMapGroupElementTarget.setEquivalence(target.getEquivalence()); + myConceptMapGroupElementTargetDao.saveAndFlush(termConceptMapGroupElementTarget); + } + } + } + } + } + } + } else { + TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapByUrl.get(); + + String msg = myContext.getLocalizer().getMessage( + BaseHapiTerminologySvcImpl.class, + "cannotCreateDuplicateConceptMapUrl", + conceptMapUrl, + existingTermConceptMap.getResourcePid()); + + throw new UnprocessableEntityException(msg); + } + + ourLog.info("Done storing TermConceptMap."); + } + @Override public boolean supportsSystem(String theSystem) { TermCodeSystem cs = getCodeSystem(theSystem); @@ -883,6 +1035,166 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return retVal; } + @Override + public List translate(TranslationRequest theTranslationRequest) { + List retVal = new ArrayList<>(); + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class); + Root root = query.from(TermConceptMapGroupElementTarget.class); + + Join elementJoin = root.join("myConceptMapGroupElement"); + Join groupJoin = elementJoin.join("myConceptMapGroup"); + Join conceptMapJoin = groupJoin.join("myConceptMap"); + + List translationQueries = theTranslationRequest.getTranslationQueries(); + List cachedTargets; + ArrayList predicates; + Coding coding; + for (TranslationQuery translationQuery : translationQueries) { + cachedTargets = myTranslationCache.getIfPresent(translationQuery); + if (cachedTargets == null) { + final List targets = new ArrayList<>(); + + predicates = new ArrayList<>(); + + coding = translationQuery.getCoding(); + if (coding.hasCode()) { + predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode())); + } else { + throw new InvalidRequestException("A code must be provided for translation to occur."); + } + + if (coding.hasSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem())); + } + + if (coding.hasVersion()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion())); + } + + if (translationQuery.hasTargetSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem().getValueAsString())); + } + + if (translationQuery.hasSource()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString())); + } + + if (translationQuery.hasTarget()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget().getValueAsString())); + } + + if (translationQuery.hasResourceId()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId())); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + // Use scrollable results. + final TypedQuery typedQuery = myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + Iterator scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults); + + while (scrollableResultsIterator.hasNext()) { + targets.add(scrollableResultsIterator.next()); + } + + ourLastResultsFromTranslationCache = false; // For testing. + myTranslationCache.get(translationQuery, k -> targets); + retVal.addAll(targets); + } else { + ourLastResultsFromTranslationCache = true; // For testing. + retVal.addAll(cachedTargets); + } + } + + return retVal; + } + + @Override + public List translateWithReverse(TranslationRequest theTranslationRequest) { + List retVal = new ArrayList<>(); + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class); + Root root = query.from(TermConceptMapGroupElement.class); + + Join targetJoin = root.join("myConceptMapGroupElementTargets"); + Join groupJoin = root.join("myConceptMapGroup"); + Join conceptMapJoin = groupJoin.join("myConceptMap"); + + List translationQueries = theTranslationRequest.getTranslationQueries(); + List cachedElements; + ArrayList predicates; + Coding coding; + for (TranslationQuery translationQuery : translationQueries) { + cachedElements = myTranslationWithReverseCache.getIfPresent(translationQuery); + if (cachedElements == null) { + final List elements = new ArrayList<>(); + + predicates = new ArrayList<>(); + + coding = translationQuery.getCoding(); + if (coding.hasCode()) { + predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); + } else { + throw new InvalidRequestException("A code must be provided for translation to occur."); + } + + if (coding.hasSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem())); + } + + if (coding.hasVersion()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); + } + + if (translationQuery.hasTargetSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem().getValueAsString())); + } + + if (translationQuery.hasSource()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource().getValueAsString())); + } + + if (translationQuery.hasTarget()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget().getValueAsString())); + } + + if (translationQuery.hasResourceId()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId())); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + // Use scrollable results. + final TypedQuery typedQuery = myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + Iterator scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults); + + while (scrollableResultsIterator.hasNext()) { + elements.add(scrollableResultsIterator.next()); + } + + ourLastResultsFromTranslationWithReverseCache = false; // For testing. + myTranslationWithReverseCache.get(translationQuery, k -> elements); + retVal.addAll(elements); + } else { + ourLastResultsFromTranslationWithReverseCache = true; // For testing. + retVal.addAll(cachedElements); + } + } + + return retVal; + } + private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, ArrayList theConceptsStack, IdentityHashMap theAllConcepts) { ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystemVersion() != null, "CodesystemValue is null"); @@ -921,6 +1233,54 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } + /** + * This method is present only for unit tests, do not call from client code + */ + @VisibleForTesting + static void clearOurLastResultsFromTranslationCache() { + ourLastResultsFromTranslationCache = false; + } + + /** + * This method is present only for unit tests, do not call from client code + */ + @VisibleForTesting + static void clearOurLastResultsFromTranslationWithReverseCache() { + ourLastResultsFromTranslationWithReverseCache = false; + } + + /** + * This method is present only for unit tests, do not call from client code + */ + @VisibleForTesting + void clearTranslationCache() { + myTranslationCache.invalidateAll(); + } + + /** + * This method is present only for unit tests, do not call from client code + */ + @VisibleForTesting() + void clearTranslationWithReverseCache() { + myTranslationWithReverseCache.invalidateAll(); + } + + /** + * This method is present only for unit tests, do not call from client code + */ + @VisibleForTesting + static boolean isOurLastResultsFromTranslationCache() { + return ourLastResultsFromTranslationCache; + } + + /** + * This method is present only for unit tests, do not call from client code + */ + @VisibleForTesting + static boolean isOurLastResultsFromTranslationWithReverseCache() { + return ourLastResultsFromTranslationWithReverseCache; + } + /** * This method is present only for unit tests, do not call from client code */ @@ -928,6 +1288,4 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) { ourForceSaveDeferredAlwaysForUnitTest = theForceSaveDeferredAlwaysForUnitTest; } - - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index d19a864aa2f..14009ee6281 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -40,9 +40,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * 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. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java index 35f0b13a34e..dbe52226f78 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java @@ -1,10 +1,9 @@ package ca.uhn.fhir.jpa.term; -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.*; import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; import java.util.List; @@ -69,6 +68,11 @@ public interface IHapiTerminologySvc { */ IIdType storeNewCodeSystemVersion(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List theValueSets, List theConceptMaps); + void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap); + boolean supportsSystem(String theCodeSystem); + List translate(TranslationRequest theTranslationRequest); + + List translateWithReverse(TranslationRequest theTranslationRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationMatch.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationMatch.java new file mode 100644 index 00000000000..7c1ae2f4048 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationMatch.java @@ -0,0 +1,74 @@ +package ca.uhn.fhir.jpa.term; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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 org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.r4.model.UriType; + +public class TranslationMatch { + private Coding myConcept; + private CodeType myEquivalence; + private UriType mySource; + + public TranslationMatch() { + super(); + } + + public Coding getConcept() { + return myConcept; + } + + public void setConcept(Coding theConcept) { + myConcept = theConcept; + } + + public CodeType getEquivalence() { + return myEquivalence; + } + + public void setEquivalence(CodeType theEquivalence) { + myEquivalence = theEquivalence; + } + + public UriType getSource() { + return mySource; + } + + public void setSource(UriType theSource) { + mySource = theSource; + } + + public void toParameterParts(ParametersParameterComponent theParam) { + if (myEquivalence != null) { + theParam.addPart().setName("equivalence").setValue(myEquivalence); + } + + if (myConcept != null) { + theParam.addPart().setName("concept").setValue(myConcept); + } + + if (mySource != null) { + theParam.addPart().setName("source").setValue(mySource); + } + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationQuery.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationQuery.java new file mode 100644 index 00000000000..4cd18d09ed5 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationQuery.java @@ -0,0 +1,128 @@ +package ca.uhn.fhir.jpa.term; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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 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.UriType; + +public class TranslationQuery { + private Coding myCoding; + private Long myResourceId; + private UriType mySource; + private UriType myTarget; + private UriType myTargetSystem; + + public TranslationQuery() { + super(); + + myCoding = new Coding(); + } + + public Coding getCoding() { + return myCoding; + } + + public void setCoding(Coding theCoding) { + myCoding = theCoding; + } + + public boolean hasResourceId() { + return myResourceId != null; + } + + public Long getResourceId() { + return myResourceId; + } + + public void setResourceId(Long theResourceId) { + myResourceId = theResourceId; + } + + public boolean hasSource() { + return mySource != null && mySource.hasValue(); + } + + public UriType getSource() { + return mySource; + } + + public void setSource(UriType theSource) { + mySource = theSource; + } + + public boolean hasTarget() { + return myTarget != null && myTarget.hasValue(); + } + + public UriType getTarget() { + return myTarget; + } + + public void setTarget(UriType theTarget) { + myTarget = theTarget; + } + + public boolean hasTargetSystem() { + return myTargetSystem != null && myTargetSystem.hasValue(); + } + + public UriType getTargetSystem() { + return myTargetSystem; + } + + public void setTargetSystem(UriType theTargetSystem) { + myTargetSystem = theTargetSystem; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (!(o instanceof TranslationQuery)) return false; + + TranslationQuery that = (TranslationQuery) o; + + return new EqualsBuilder() + .append(getCoding().getCode(), that.getCoding().getCode()) + .append(getCoding().getSystem(), that.getCoding().getSystem()) + .append(getCoding().getVersion(), that.getCoding().getVersion()) + .append(getResourceId(), that.getResourceId()) + .append(getSource(), that.getSource()) + .append(getTarget(), that.getTarget()) + .append(getTargetSystem(), that.getTargetSystem()) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(getCoding().getCode()) + .append(getCoding().getSystem()) + .append(getCoding().getVersion()) + .append(getResourceId()) + .append(getSource()) + .append(getTarget()) + .append(getTargetSystem()) + .toHashCode(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationRequest.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationRequest.java new file mode 100644 index 00000000000..339463ec9a9 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationRequest.java @@ -0,0 +1,155 @@ +package ca.uhn.fhir.jpa.term; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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 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.UriType; + +import java.util.ArrayList; +import java.util.List; + +public class TranslationRequest { + private CodeableConcept myCodeableConcept; + private Long myResourceId; + private BooleanType myReverse; + private UriType mySource; + private UriType myTarget; + private UriType myTargetSystem; + + public TranslationRequest() { + super(); + + myCodeableConcept = new CodeableConcept(); + } + + public CodeableConcept getCodeableConcept() { + return myCodeableConcept; + } + + public void setCodeableConcept(CodeableConcept theCodeableConcept) { + myCodeableConcept = theCodeableConcept; + } + + public boolean hasResourceId() { + return myResourceId != null; + } + + public Long getResourceId() { + return myResourceId; + } + + public void setResourceId(Long theResourceId) { + myResourceId = theResourceId; + } + + public boolean hasReverse() { + return myReverse != null; + } + + public BooleanType getReverse() { + return myReverse; + } + + public boolean getReverseAsBoolean() { + if (hasReverse()) { + return myReverse.booleanValue(); + } + + return false; + } + + public void setReverse(BooleanType theReverse) { + myReverse = theReverse; + } + + public void setReverse(boolean theReverse) { + myReverse = new BooleanType(theReverse); + } + + public boolean hasSource() { + return mySource != null && mySource.hasValue(); + } + + public UriType getSource() { + return mySource; + } + + public void setSource(UriType theSource) { + mySource = theSource; + } + + public boolean hasTarget() { + return myTarget != null && myTarget.hasValue(); + } + + public UriType getTarget() { + return myTarget; + } + + public void setTarget(UriType theTarget) { + myTarget = theTarget; + } + + public boolean hasTargetSystem() { + return myTargetSystem != null && myTargetSystem.hasValue(); + } + + public UriType getTargetSystem() { + return myTargetSystem; + } + + public void setTargetSystem(UriType theTargetSystem) { + myTargetSystem = theTargetSystem; + } + + public List getTranslationQueries() { + List retVal = new ArrayList<>(); + + TranslationQuery translationQuery; + for (Coding coding : getCodeableConcept().getCoding()) { + translationQuery = new TranslationQuery(); + + translationQuery.setCoding(coding); + + if (this.hasResourceId()) { + translationQuery.setResourceId(this.getResourceId()); + } + + if (this.hasSource()) { + translationQuery.setSource(this.getSource()); + } + + if (this.hasTarget()) { + translationQuery.setTarget(this.getTarget()); + } + + if (this.hasTargetSystem()) { + translationQuery.setTargetSystem(this.getTargetSystem()); + } + + retVal.add(translationQuery); + } + + return retVal; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationResult.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationResult.java new file mode 100644 index 00000000000..edd8487055e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TranslationResult.java @@ -0,0 +1,88 @@ +package ca.uhn.fhir.jpa.term; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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 org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.r4.model.StringType; + +import java.util.ArrayList; +import java.util.List; + +public class TranslationResult { + private List myMatches; + private StringType myMessage; + private BooleanType myResult; + + public TranslationResult() { + super(); + + myMatches = new ArrayList<>(); + } + + public List getMatches() { + return myMatches; + } + + public void setMatches(List theMatches) { + myMatches = theMatches; + } + + public boolean addMatch(TranslationMatch theMatch) { + return myMatches.add(theMatch); + } + + public StringType getMessage() { + return myMessage; + } + + public void setMessage(StringType theMessage) { + myMessage = theMessage; + } + + public BooleanType getResult() { + return myResult; + } + + public void setResult(BooleanType theMatched) { + myResult = theMatched; + } + + public Parameters toParameters() { + Parameters retVal = new Parameters(); + + if (myResult != null) { + retVal.addParameter().setName("result").setValue(myResult); + } + + if (myMessage != null) { + retVal.addParameter().setName("message").setValue(myMessage); + } + + for (TranslationMatch translationMatch : myMatches) { + ParametersParameterComponent matchParam = retVal.addParameter().setName("match"); + translationMatch.toParameterParts(matchParam); + } + + return retVal; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java index dd80d0b63e5..86ad478627a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java @@ -160,4 +160,8 @@ public class JpaConstants { */ public static final String OPERATION_META_ADD = "$meta-add"; + /** + * Operation name for the $translate operation + */ + public static final String OPERATION_TRANSLATE = "$translate"; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java new file mode 100644 index 00000000000..58b6bcec10f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java @@ -0,0 +1,63 @@ +package ca.uhn.fhir.jpa.util; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 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 org.apache.commons.lang3.Validate; +import org.hibernate.ScrollableResults; + +import java.util.Iterator; + +public class ScrollableResultsIterator extends BaseIterator implements Iterator { + private boolean hasNext; + private T myNext; + private ScrollableResults myScroll; + + public ScrollableResultsIterator(ScrollableResults theScroll) { + myScroll = theScroll; + } + + @SuppressWarnings("unchecked") + private void ensureHaveNext() { + if (myNext == null) { + if (myScroll.next()) { + hasNext = true; + myNext = (T) myScroll.get(0); + } else { + hasNext = false; + } + } + } + + @Override + public boolean hasNext() { + ensureHaveNext(); + return hasNext; + } + + @Override + public T next() { + ensureHaveNext(); + Validate.isTrue(hasNext); + T next = myNext; + myNext = null; + return next; + } +} 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 bf776616241..fbaa522e788 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 @@ -49,6 +49,15 @@ import static org.mockito.Mockito.when; public abstract class BaseJpaTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaTest.class); + + protected static final String CM_URL = "http://example.com/my_concept_map"; + protected static final String CS_URL = "http://example.com/my_code_system"; + protected static final String CS_URL_2 = "http://example.com/my_code_system2"; + protected static final String CS_URL_3 = "http://example.com/my_code_system3"; + protected static final String CS_URL_4 = "http://example.com/my_code_system4"; + protected static final String VS_URL = "http://example.com/my_value_set"; + protected static final String VS_URL_2 = "http://example.com/my_value_set2"; + protected ServletRequestDetails mySrd; protected ArrayList myServerInterceptorList; protected IRequestOperationCallback myRequestOperationCallback = mock(IRequestOperationCallback.class); 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 1c0a43d4381..849ba5ce391 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 @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.config.TestDstu3Config; import ca.uhn.fhir.jpa.dao.*; import ca.uhn.fhir.jpa.dao.data.*; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; +import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; @@ -14,20 +15,22 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.util.ResourceCountCache; -import ca.uhn.fhir.jpa.util.SingleItemLoadingCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; 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.interceptor.IServerInterceptor; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.Search; +import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.After; import org.junit.AfterClass; @@ -48,7 +51,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @RunWith(SpringJUnit4ClassRunner.class) @@ -91,7 +94,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { protected IFhirResourceDao myCompartmentDefinitionDao; @Autowired @Qualifier("myConceptMapDaoDstu3") - protected IFhirResourceDao myConceptMapDao; + protected IFhirResourceDaoConceptMap myConceptMapDao; @Autowired @Qualifier("myConditionDaoDstu3") protected IFhirResourceDao myConditionDao; @@ -229,6 +232,10 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { protected IFhirResourceDaoValueSet myValueSetDao; @Autowired private JpaValidationSupportChainDstu3 myJpaValidationSupportChainDstu3; + @Autowired + protected ITermConceptMapDao myTermConceptMapDao; + @Autowired + protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao; @After() public void afterCleanupDao() { @@ -315,4 +322,34 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { return uuid; } + /** + * Creates a single {@link org.hl7.fhir.dstu3.model.ConceptMap} entity that includes: + *
+ *
    + *
  • + * One group with two elements, each identifying one target apiece. + *
  • + *
  • + * One group with one element, identifying two targets. + *
  • + *
  • + * One group with one element, identifying a target that also appears + * in the first element of the first group. + *
  • + *
+ *
+ * The first two groups identify the same source code system and different target code systems. + *
+ * The first two groups also include an element with the same source code. + *
+ * + * @return A {@link org.hl7.fhir.dstu3.model.ConceptMap} entity for testing. + */ + public static ConceptMap createConceptMap() { + try { + return VersionConvertor_30_40.convertConceptMap(BaseJpaR4Test.createConceptMap()); + } catch (FHIRException fe) { + throw new InternalErrorException(fe); + } + } } 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 new file mode 100644 index 00000000000..62a41cde417 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ConceptMapTest.java @@ -0,0 +1,84 @@ +package ca.uhn.fhir.jpa.dao.dstu3; + +import ca.uhn.fhir.jpa.term.TranslationMatch; +import ca.uhn.fhir.jpa.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.dstu3.model.ConceptMap; +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.AfterClass; +import org.junit.Before; +import org.junit.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 static org.junit.Assert.*; + +public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ConceptMapTest.class); + + private IIdType myConceptMapId; + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @Before + @Transactional + public void before02() { + myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless(); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToMany() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + // + } + }); + } +} 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 8e8c44ba9b2..0e0f4f48c56 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 @@ -29,6 +29,10 @@ import org.hibernate.search.jpa.Search; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; +import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent; +import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -48,13 +52,12 @@ import java.io.IOException; import java.io.InputStream; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestR4Config.class}) public abstract class BaseJpaR4Test extends BaseJpaTest { - private static JpaValidationSupportChainR4 ourJpaValidationSupportChainR4; private static IFhirResourceDaoValueSet ourValueSetDao; @@ -102,7 +105,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { protected IFhirResourceDao myCompartmentDefinitionDao; @Autowired @Qualifier("myConceptMapDaoR4") - protected IFhirResourceDao myConceptMapDao; + protected IFhirResourceDaoConceptMap myConceptMapDao; @Autowired @Qualifier("myConditionDaoR4") protected IFhirResourceDao myConditionDao; @@ -240,6 +243,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { protected IFhirResourceDaoValueSet myValueSetDao; @Autowired private JpaValidationSupportChainR4 myJpaValidationSupportChainR4; + @Autowired + protected ITermConceptMapDao myTermConceptMapDao; + @Autowired + protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao; @After() public void afterCleanupDao() { @@ -327,4 +334,141 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { return uuid; } + /** + * Creates a single {@link org.hl7.fhir.r4.model.ConceptMap} entity that includes: + *
+ *
    + *
  • + * One group with two elements, each identifying one target apiece. + *
  • + *
  • + * One group with one element, identifying two targets. + *
  • + *
  • + * One group with one element, identifying a target that also appears + * in the first element of the first group. + *
  • + *
+ *
+ * The first two groups identify the same source code system and different target code systems. + *
+ * The first two groups also include an element with the same source code. + * + * @return A {@link org.hl7.fhir.r4.model.ConceptMap} entity for testing. + */ + public static ConceptMap createConceptMap() { + // + ConceptMap conceptMap = new ConceptMap(); + conceptMap.setUrl(CM_URL); + + conceptMap.setSource(new UriType(VS_URL)); + conceptMap.setTarget(new UriType(VS_URL_2)); + + // + ConceptMapGroupComponent group = conceptMap.addGroup(); + group.setSource(CS_URL); + group.setSourceVersion("Version 1"); + group.setTarget(CS_URL_2); + group.setTargetVersion("Version 2"); + + // + SourceElementComponent element = group.addElement(); + element.setCode("12345"); + element.setDisplay("Source Code 12345"); + + // + TargetElementComponent target = element.addTarget(); + target.setCode("34567"); + target.setDisplay("Target Code 34567"); + target.setEquivalence(ConceptMapEquivalence.EQUAL); + // End ConceptMap.group(0).element(0).target(0) + // + + // End ConceptMap.group(0).element(0) + // + + // + element = group.addElement(); + element.setCode("23456"); + element.setDisplay("Source Code 23456"); + + // + target = element.addTarget(); + target.setCode("45678"); + target.setDisplay("Target Code 45678"); + target.setEquivalence(ConceptMapEquivalence.WIDER); + // End ConceptMap.group(0).element(1).target(0) + // + + // End ConceptMap.group(0).element(1) + // + + // End ConceptMap.group(0) + // + + // + group = conceptMap.addGroup(); + group.setSource(CS_URL); + group.setSourceVersion("Version 3"); + group.setTarget(CS_URL_3); + group.setTargetVersion("Version 4"); + + // + element = group.addElement(); + element.setCode("12345"); + element.setDisplay("Source Code 12345"); + + // + target = element.addTarget(); + target.setCode("56789"); + target.setDisplay("Target Code 56789"); + target.setEquivalence(ConceptMapEquivalence.EQUAL); + // End ConceptMap.group(1).element(0).target(0) + // + + // + target = element.addTarget(); + target.setCode("67890"); + target.setDisplay("Target Code 67890"); + target.setEquivalence(ConceptMapEquivalence.WIDER); + // End ConceptMap.group(1).element(0).target(1) + // + + // End ConceptMap.group(1).element(0) + // + + // End ConceptMap.group(1) + // + + // + group = conceptMap.addGroup(); + group.setSource(CS_URL_4); + group.setSourceVersion("Version 5"); + group.setTarget(CS_URL_2); + group.setTargetVersion("Version 2"); + + // + element = group.addElement(); + element.setCode("78901"); + element.setDisplay("Source Code 78901"); + + // + target = element.addTarget(); + target.setCode("34567"); + target.setDisplay("Target Code 34567"); + target.setEquivalence(ConceptMapEquivalence.NARROWER); + // End ConceptMap.group(2).element(0).target(0) + // + + // End ConceptMap.group(2).element(0) + // + + // End ConceptMap.group(2) + // + + // End ConceptMap + // + + return conceptMap; + } } 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 new file mode 100644 index 00000000000..3a0cd357688 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java @@ -0,0 +1,980 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.jpa.term.TranslationMatch; +import ca.uhn.fhir.jpa.term.TranslationRequest; +import ca.uhn.fhir.jpa.term.TranslationResult; +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.instance.model.api.IIdType; +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.UriType; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.Assert.*; + +public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4ConceptMapTest.class); + + private IIdType myConceptMapId; + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @Before + @Transactional + public void before02() { + myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless(); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToMany() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + // + } + }); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToOne() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_2)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + // + } + }); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeUnmapped() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("BOGUS"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertFalse(translationResult.getResult().booleanValue()); + assertEquals("No matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(0, translationResult.getMatches().size()); + // + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithCodeOnly() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(3, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(2); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystem() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(3, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(2); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystemAndVersion1() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * source code system version #1 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345") + .setVersion("Version 1"); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystemAndVersion3() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * source code system version #3 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345") + .setVersion("Version 3"); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceAndTargetSystem2() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #2 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_2)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceAndTargetSystem3() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #3 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source value set + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + translationRequest.setSource(new UriType(VS_URL)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(3, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(2); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithTargetValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * target value set + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + translationRequest.setTarget(new UriType(VS_URL_2)); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(3, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + Coding concept = translationMatch.getConcept(); + assertEquals("34567", concept.getCode()); + assertEquals("Target Code 34567", concept.getDisplay()); + assertEquals(CS_URL_2, concept.getSystem()); + assertEquals("Version 2", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("56789", concept.getCode()); + assertEquals("Target Code 56789", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(2); + assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode()); + concept = translationMatch.getConcept(); + assertEquals("67890", concept.getCode()); + assertEquals("Target Code 67890", concept.getDisplay()); + assertEquals(CS_URL_3, concept.getSystem()); + assertEquals("Version 4", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverse() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(new UriType(CS_URL_4)); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseByCodeSystemsAndSourceCodeUnmapped() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_3) + .setCode("BOGUS"); + translationRequest.setTargetSystem(new UriType(CS_URL)); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertFalse(translationResult.getResult().booleanValue()); + assertEquals("No matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(0, translationResult.getMatches().size()); + // + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithCodeOnly() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("12345", concept.getCode()); + assertEquals("Source Code 12345", concept.getDisplay()); + assertEquals(CS_URL, concept.getSystem()); + assertEquals("Version 1", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertNull(translationMatch.getEquivalence()); + concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceSystem() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("12345", concept.getCode()); + assertEquals("Source Code 12345", concept.getDisplay()); + assertEquals(CS_URL, concept.getSystem()); + assertEquals("Version 1", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertNull(translationMatch.getEquivalence()); + concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceSystemAndVersion() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * source code system version + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567") + .setVersion("Version 2"); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("12345", concept.getCode()); + assertEquals("Source Code 12345", concept.getDisplay()); + assertEquals(CS_URL, concept.getSystem()); + assertEquals("Version 1", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertNull(translationMatch.getEquivalence()); + concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem1() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #1 + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(new UriType(CS_URL)); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("12345", concept.getCode()); + assertEquals("Source Code 12345", concept.getDisplay()); + assertEquals(CS_URL, concept.getSystem()); + assertEquals("Version 1", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem4() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #4 + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(new UriType(CS_URL_4)); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(1, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source value set + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setSource(new UriType(VS_URL_2)); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("12345", concept.getCode()); + assertEquals("Source Code 12345", concept.getDisplay()); + assertEquals(CS_URL, concept.getSystem()); + assertEquals("Version 1", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertNull(translationMatch.getEquivalence()); + concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithTargetValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * target value set + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setTarget(new UriType(VS_URL)); + translationRequest.setReverse(true); + + TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null); + + assertTrue(translationResult.getResult().booleanValue()); + assertEquals("Matches found!", translationResult.getMessage().getValueAsString()); + + assertEquals(2, translationResult.getMatches().size()); + + TranslationMatch translationMatch = translationResult.getMatches().get(0); + assertNull(translationMatch.getEquivalence()); + Coding concept = translationMatch.getConcept(); + assertEquals("12345", concept.getCode()); + assertEquals("Source Code 12345", concept.getDisplay()); + assertEquals(CS_URL, concept.getSystem()); + assertEquals("Version 1", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + + translationMatch = translationResult.getMatches().get(1); + assertNull(translationMatch.getEquivalence()); + concept = translationMatch.getConcept(); + assertEquals("78901", concept.getCode()); + assertEquals("Source Code 78901", concept.getDisplay()); + assertEquals(CS_URL_4, concept.getSystem()); + assertEquals("Version 5", concept.getVersion()); + assertFalse(concept.getUserSelected()); + assertEquals(CM_URL, translationMatch.getSource().getValueAsString()); + } + }); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java index e2c29d85153..a66c7d9afda 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java @@ -26,6 +26,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.dstu3.model.Patient; import org.junit.After; import org.junit.AfterClass; @@ -201,4 +203,56 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { TestUtil.clearAllStaticFieldsForUnitTest(); } + public static int getNumberOfParametersByName(Parameters theParameters, String theName) { + int retVal = 0; + + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + retVal++; + } + } + + return retVal; + } + + public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) { + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + return param; + } + } + + return new ParametersParameterComponent(); + } + + public static List getParametersByName(Parameters theParameters, String theName) { + List params = new ArrayList<>(); + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + params.add(param); + } + } + + return params; + } + + public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) { + for (ParametersParameterComponent part : theParameter.getPart()) { + if (part.getName().equals(theName)) { + return part; + } + } + + return new ParametersParameterComponent(); + } + + public static boolean hasParameterByName(Parameters theParameters, String theName) { + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + return true; + } + } + + return false; + } } 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 new file mode 100644 index 00000000000..9d12b26e56c --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ConceptMapTest.java @@ -0,0 +1,89 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +import static org.junit.Assert.*; + +public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDstu3Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMapTest.class); + + private IIdType myConceptMapId; + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @Before + @Transactional + public void before02() { + myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless(); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToMany() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3)); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + + 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(2, 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("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getValueAsString()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index d4eaa6482fc..91c5aba4e89 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -27,6 +27,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r4.model.Patient; import org.junit.After; import org.junit.AfterClass; @@ -215,4 +217,56 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { TestUtil.clearAllStaticFieldsForUnitTest(); } + public static int getNumberOfParametersByName(Parameters theParameters, String theName) { + int retVal = 0; + + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + retVal++; + } + } + + return retVal; + } + + public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) { + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + return param; + } + } + + return new ParametersParameterComponent(); + } + + public static List getParametersByName(Parameters theParameters, String theName) { + List params = new ArrayList<>(); + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + params.add(param); + } + } + + return params; + } + + public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) { + for (ParametersParameterComponent part : theParameter.getPart()) { + if (part.getName().equals(theName)) { + return part; + } + } + + return new ParametersParameterComponent(); + } + + public static boolean hasParameterByName(Parameters theParameters, String theName) { + for (ParametersParameterComponent param : theParameters.getParameter()) { + if (param.getName().equals(theName)) { + return true; + } + } + + return false; + } } 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 new file mode 100644 index 00000000000..cc455dedaea --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java @@ -0,0 +1,1657 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +import static org.junit.Assert.*; + +public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR4ConceptMapTest.class); + + private IIdType myConceptMapId; + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @Before + @Transactional + public void before02() { + myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless(); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToMany() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3)); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToOne() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + 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("12345")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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 = getParameterByName(respParams, "match"); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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 testTranslateByCodeSystemsAndSourceCodeUnmapped() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3)); + inParams.addParameter().setName("code").setValue(new CodeType("BOGUS")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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()); + + assertFalse(hasParameterByName(respParams, "match")); + } + + @Test + public void testTranslateUsingPredicatesWithCodeOnly() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(3, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateUsingPredicatesCoding() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system version #1 + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("coding").setValue( + new Coding().setSystem(CS_URL).setCode("12345").setVersion("Version 1")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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 = getParameterByName(respParams, "match"); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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 testTranslateUsingPredicatesWithCodeableConcept() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system versions #1 and #3 + */ + CodeableConcept codeableConcept = new CodeableConcept(); + codeableConcept.addCoding(new Coding().setSystem(CS_URL).setCode("12345").setVersion("Version 1")); + codeableConcept.addCoding(new Coding().setSystem(CS_URL).setCode("23456").setVersion("Version 1")); + codeableConcept.addCoding(new Coding().setSystem(CS_URL).setCode("12345").setVersion("Version 3")); + Parameters inParams = new Parameters(); + inParams.addParameter().setName("codeableConcept").setValue(codeableConcept); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(4, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("45678", coding.getCode()); + assertEquals("Target Code 45678", 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()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(3); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystem() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(3, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystemAndVersion1() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system version #1 + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("version").setValue(new StringType("Version 1")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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 = getParameterByName(respParams, "match"); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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 testTranslateUsingPredicatesWithSourceSystemAndVersion3() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system version #3 + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("version").setValue(new StringType("Version 3")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateUsingPredicatesWithSourceAndTargetSystem2() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * target code system #2 + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_2)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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 = getParameterByName(respParams, "match"); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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 testTranslateUsingPredicatesWithSourceAndTargetSystem3() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * target code system #3 + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateUsingPredicatesWithSourceValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source value set + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("source").setValue(new UriType(VS_URL)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(3, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateUsingPredicatesWithTargetValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * target value set + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + inParams.addParameter().setName("target").setValue(new UriType(VS_URL_2)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(3, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithInstance() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("12345")); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .operation() + .onInstance(myConceptMapId) + .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(3, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(3, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("34567", coding.getCode()); + assertEquals("Target Code 34567", 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()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("equal", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("56789", coding.getCode()); + assertEquals("Target Code 56789", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(3, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertEquals("wider", ((CodeType) part.getValue()).getCode()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("67890", coding.getCode()); + assertEquals("Target Code 67890", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_3, coding.getSystem()); + assertEquals("Version 4", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverse() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * target code system + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_4)); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseByCodeSystemsAndSourceCodeUnmapped() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_3)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("code").setValue(new CodeType("BOGUS")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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()); + + assertFalse(hasParameterByName(respParams, "match")); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithCodeOnly() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesCoding() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system version + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("coding").setValue( + new Coding().setSystem(CS_URL_2).setCode("34567").setVersion("Version 2")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithCodeableConcept() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system version + * reverse = true + */ + CodeableConcept codeableConcept = new CodeableConcept(); + codeableConcept.addCoding(new Coding().setSystem(CS_URL_2).setCode("34567").setVersion("Version 2")); + codeableConcept.addCoding(new Coding().setSystem(CS_URL_2).setCode("45678").setVersion("Version 2")); + codeableConcept.addCoding(new Coding().setSystem(CS_URL_3).setCode("56789").setVersion("Version 4")); + codeableConcept.addCoding(new Coding().setSystem(CS_URL_3).setCode("67890").setVersion("Version 4")); + Parameters inParams = new Parameters(); + inParams.addParameter().setName("codeableConcept").setValue(codeableConcept); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(4, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(2); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("23456", coding.getCode()); + assertEquals("Source Code 23456", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(3); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 3", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceSystem() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceSystemAndVersion() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * source code system version + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("version").setValue(new StringType("Version 2")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem1() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * target code system #1 + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL)); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem4() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source code system + * target code system #4 + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("system").setValue(new UriType(CS_URL_2)); + inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_4)); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * source value set + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("source").setValue(new UriType(VS_URL_2)); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithTargetValueSet() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + /* + * Provided: + * source code + * target value set + * reverse = true + */ + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("target").setValue(new UriType(VS_URL)); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } + + @Test + public void testTranslateWithReverseAndInstance() { + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("code").setValue(new CodeType("34567")); + inParams.addParameter().setName("reverse").setValue(new BooleanType(true)); + + ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams)); + + Parameters respParams = myClient + .operation() + .onInstance(myConceptMapId) + .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(2, getNumberOfParametersByName(respParams, "match")); + + param = getParametersByName(respParams, "match").get(0); + assertEquals(2, param.getPart().size()); + ParametersParameterComponent part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + Coding coding = (Coding) part.getValue(); + assertEquals("12345", coding.getCode()); + assertEquals("Source Code 12345", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL, coding.getSystem()); + assertEquals("Version 1", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + + param = getParametersByName(respParams, "match").get(1); + assertEquals(2, param.getPart().size()); + part = getPartByName(param, "equivalence"); + assertFalse(part.hasValue()); + part = getPartByName(param, "concept"); + coding = (Coding) part.getValue(); + assertEquals("78901", coding.getCode()); + assertEquals("Source Code 78901", coding.getDisplay()); + assertFalse(coding.getUserSelected()); + assertEquals(CS_URL_4, coding.getSystem()); + assertEquals("Version 5", coding.getVersion()); + part = getPartByName(param, "source"); + assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString()); + } +} 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 0b0214562b2..209ed2dd840 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 @@ -430,5 +430,4 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } - } 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 new file mode 100644 index 00000000000..8c542f726e6 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java @@ -0,0 +1,1401 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; +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.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; +import org.hl7.fhir.r4.model.UriType; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.util.AopTestUtils; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; + +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; + +public class TerminologySvcImplR4Test extends BaseJpaR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplR4Test.class); + + private IIdType myConceptMapId; + + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + + @Before + public void beforeClearCache() { + BaseHapiTerminologySvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc); + baseHapiTerminologySvc.clearTranslationCache(); + BaseHapiTerminologySvcImpl.clearOurLastResultsFromTranslationCache(); + baseHapiTerminologySvc.clearTranslationWithReverseCache(); + BaseHapiTerminologySvcImpl.clearOurLastResultsFromTranslationWithReverseCache(); + } + + private void persistConceptMap() { + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless(); + } + }); + } + + @Test + public void testDuplicateConceptMapUrls() { + persistConceptMap(); + + expectedException.expect(UnprocessableEntityException.class); + expectedException.expectMessage("Can not create multiple ConceptMap resources with ConceptMap.url \"http://example.com/my_concept_map\", already have one with resource ID: " + myConceptMapId.getIdPart()); + + persistConceptMap(); + } + + @Test + public void testStoreTermConceptMapAndChildren() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + Optional optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(CM_URL); + assertTrue(optionalConceptMap.isPresent()); + + TermConceptMap conceptMap = optionalConceptMap.get(); + + ourLog.info("ConceptMap:\n" + conceptMap.toString()); + + assertEquals(VS_URL, conceptMap.getSource()); + assertEquals(VS_URL_2, conceptMap.getTarget()); + assertEquals(CM_URL, conceptMap.getUrl()); + assertEquals(3, conceptMap.getConceptMapGroups().size()); + + // + TermConceptMapGroup group = conceptMap.getConceptMapGroups().get(0); + + ourLog.info("ConceptMap.group(0):\n" + group.toString()); + + assertEquals(CS_URL, group.getSource()); + assertEquals("Version 1", group.getSourceVersion()); + assertEquals(VS_URL, group.getSourceValueSet()); + assertEquals(CS_URL_2, group.getTarget()); + assertEquals("Version 2", group.getTargetVersion()); + assertEquals(VS_URL_2, group.getTargetValueSet()); + assertEquals(CM_URL, group.getConceptMapUrl()); + assertEquals(2, group.getConceptMapGroupElements().size()); + + // + TermConceptMapGroupElement element = group.getConceptMapGroupElements().get(0); + + ourLog.info("ConceptMap.group(0).element(0):\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); + + // + TermConceptMapGroupElementTarget target = element.getConceptMapGroupElementTargets().get(0); + + ourLog.info("ConceptMap.group(0).element(0).target(0):\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + // End ConceptMap.group(0).element(0).target(0) + // + + // End ConceptMap.group(0).element(0) + // + + // + element = group.getConceptMapGroupElements().get(1); + + ourLog.info("ConceptMap.group(0).element(1):\n" + element.toString()); + + assertEquals("23456", element.getCode()); + assertEquals("Source Code 23456", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); + + // + target = element.getConceptMapGroupElementTargets().get(0); + + ourLog.info("ConceptMap.group(0).element(1).target(0):\n" + target.toString()); + + assertEquals("45678", target.getCode()); + assertEquals("Target Code 45678", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + // End ConceptMap.group(0).element(1).target(0) + // + + // End ConceptMap.group(0).element(1) + // + + // End ConceptMap.group(0) + // + + // + group = conceptMap.getConceptMapGroups().get(1); + + ourLog.info("ConceptMap.group(1):\n" + group.toString()); + + assertEquals(CS_URL, group.getSource()); + assertEquals("Version 3", group.getSourceVersion()); + assertEquals(CS_URL_3, group.getTarget()); + assertEquals("Version 4", group.getTargetVersion()); + assertEquals(CM_URL, group.getConceptMapUrl()); + assertEquals(1, group.getConceptMapGroupElements().size()); + + // + element = group.getConceptMapGroupElements().get(0); + + ourLog.info("ConceptMap.group(1).element(0):\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 3", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals(2, element.getConceptMapGroupElementTargets().size()); + + // + target = element.getConceptMapGroupElementTargets().get(0); + + ourLog.info("ConceptMap.group(1).element(0).target(0):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + // End ConceptMap.group(1).element(0).target(0) + // + + // + target = element.getConceptMapGroupElementTargets().get(1); + + ourLog.info("ConceptMap.group(1).element(0).target(1):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + // End ConceptMap.group(1).element(0).target(1) + // + + // End ConceptMap.group(1).element(0) + // + + // End ConceptMap.group(1) + // + + // + group = conceptMap.getConceptMapGroups().get(2); + + ourLog.info("ConceptMap.group(2):\n" + group.toString()); + + assertEquals(CS_URL_4, group.getSource()); + assertEquals("Version 5", group.getSourceVersion()); + assertEquals(CS_URL_2, group.getTarget()); + assertEquals("Version 2", group.getTargetVersion()); + assertEquals(CM_URL, group.getConceptMapUrl()); + assertEquals(1, group.getConceptMapGroupElements().size()); + + // + element = group.getConceptMapGroupElements().get(0); + + ourLog.info("ConceptMap.group(2).element(0):\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + assertEquals(1, element.getConceptMapGroupElementTargets().size()); + + // + target = element.getConceptMapGroupElementTargets().get(0); + + ourLog.info("ConceptMap.group(2).element(0).target(0):\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.NARROWER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + // End ConceptMap.group(2).element(0).target(0) + // + + // End ConceptMap.group(2).element(0) + // + + // End ConceptMap.group(2) + // + + // End ConceptMap + // + } + }); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToMany() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + // + } + }); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeOneToOne() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_2)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("ConceptMap.group.element.target:\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // + TermConceptMapGroupElement element = target.getConceptMapGroupElement(); + + ourLog.info("ConceptMap.group.element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + TermConceptMapGroup group = element.getConceptMapGroup(); + + ourLog.info("ConceptMap.group:\n" + group.toString()); + + assertEquals(CS_URL, group.getSource()); + assertEquals("Version 1", group.getSourceVersion()); + assertEquals(VS_URL, group.getSourceValueSet()); + assertEquals(CS_URL_2, group.getTarget()); + assertEquals("Version 2", group.getTargetVersion()); + assertEquals(VS_URL_2, group.getTargetValueSet()); + assertEquals(CM_URL, group.getConceptMapUrl()); + + TermConceptMap conceptMap = group.getConceptMap(); + + ourLog.info("ConceptMap:\n" + conceptMap.toString()); + + assertNotNull(conceptMap); + assertEquals(VS_URL, conceptMap.getSource()); + assertEquals(VS_URL_2, conceptMap.getTarget()); + assertEquals(CM_URL, conceptMap.getUrl()); + // + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + // + } + }); + } + + @Test + public void testTranslateByCodeSystemsAndSourceCodeUnmapped() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("BOGUS"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertTrue(targets.isEmpty()); + // + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithCodeOnly() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(2); + + ourLog.info("target(2):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystem() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(2); + + ourLog.info("target(2):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystemAndVersion1() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * source code system version #1 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345") + .setVersion("Version 1"); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target:\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceSystemAndVersion3() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * source code system version #3 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345") + .setVersion("Version 3"); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceAndTargetSystem2() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #2 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_2)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target:\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(1, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceAndTargetSystem3() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #3 + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL) + .setCode("12345"); + translationRequest.setTargetSystem(new UriType(CS_URL_3)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(2, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithSourceValueSet() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source value set + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + translationRequest.setSource(new UriType(VS_URL)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(2); + + ourLog.info("target(2):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateUsingPredicatesWithTargetValueSet() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * target value set + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("12345"); + translationRequest.setTarget(new UriType(VS_URL_2)); + + List targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + + TermConceptMapGroupElementTarget target = targets.get(0); + + ourLog.info("target(0):\n" + target.toString()); + + assertEquals("34567", target.getCode()); + assertEquals("Target Code 34567", target.getDisplay()); + assertEquals(CS_URL_2, target.getSystem()); + assertEquals("Version 2", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(1); + + ourLog.info("target(1):\n" + target.toString()); + + assertEquals("56789", target.getCode()); + assertEquals("Target Code 56789", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.EQUAL, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + target = targets.get(2); + + ourLog.info("target(2):\n" + target.toString()); + + assertEquals("67890", target.getCode()); + assertEquals("Target Code 67890", target.getDisplay()); + assertEquals(CS_URL_3, target.getSystem()); + assertEquals("Version 4", target.getSystemVersion()); + assertEquals(ConceptMapEquivalence.WIDER, target.getEquivalence()); + assertEquals(VS_URL_2, target.getValueSet()); + assertEquals(CM_URL, target.getConceptMapUrl()); + + // Test caching. + targets = myTermSvc.translate(translationRequest); + assertNotNull(targets); + assertEquals(3, targets.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationCache()); + } + }); + } + + @Test + public void testTranslateWithReverse() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(new UriType(CS_URL_4)); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseByCodeSystemsAndSourceCodeUnmapped() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + // + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_3) + .setCode("BOGUS"); + translationRequest.setTargetSystem(new UriType(CS_URL)); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertTrue(elements.isEmpty()); + // + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithCodeOnly() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + element = elements.get(1); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceSystem() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + element = elements.get(1); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceSystemAndVersion() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * source code system version + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567") + .setVersion("Version 2"); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + element = elements.get(1); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem1() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #1 + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(new UriType(CS_URL)); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem4() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source code system + * target code system #4 + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setSystem(CS_URL_2) + .setCode("34567"); + translationRequest.setTargetSystem(new UriType(CS_URL_4)); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(1, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithSourceValueSet() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * source value set + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setSource(new UriType(VS_URL_2)); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + element = elements.get(1); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } + + @Test + public void testTranslateWithReverseUsingPredicatesWithTargetValueSet() { + persistConceptMap(); + ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId); + + ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + + new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus theStatus) { + /* + * Provided: + * source code + * target value set + * reverse = true + */ + TranslationRequest translationRequest = new TranslationRequest(); + translationRequest.getCodeableConcept().addCoding() + .setCode("34567"); + translationRequest.setTarget(new UriType(VS_URL)); + translationRequest.setReverse(true); + + List elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertFalse(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + + TermConceptMapGroupElement element = elements.get(0); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("12345", element.getCode()); + assertEquals("Source Code 12345", element.getDisplay()); + assertEquals(CS_URL, element.getSystem()); + assertEquals("Version 1", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + element = elements.get(1); + + ourLog.info("element:\n" + element.toString()); + + assertEquals("78901", element.getCode()); + assertEquals("Source Code 78901", element.getDisplay()); + assertEquals(CS_URL_4, element.getSystem()); + assertEquals("Version 5", element.getSystemVersion()); + assertEquals(VS_URL, element.getValueSet()); + assertEquals(CM_URL, element.getConceptMapUrl()); + + // Test caching. + elements = myTermSvc.translateWithReverse(translationRequest); + assertNotNull(elements); + assertEquals(2, elements.size()); + assertTrue(BaseHapiTerminologySvcImpl.isOurLastResultsFromTranslationWithReverseCache()); + } + }); + } +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java index f3acbc931ea..d38fb1907f1 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java @@ -18,7 +18,7 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicSecurityInterceptor.class); private HashSet myTokens; - + public PublicSecurityInterceptor() { String passwordsString = System.getProperty("fhir.tdlpass"); String[] passwords = passwordsString.split(","); diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index 90e1a6cf67e..284871ebda7 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -5,11 +5,7 @@ import java.util.*; import org.apache.commons.lang3.StringUtils; -#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem')) -import ca.uhn.fhir.jpa.provider${package_suffix}.BaseJpaResourceProvider${className}${versionCapitalized}; -#else -import ca.uhn.fhir.jpa.provider${package_suffix}.JpaResourceProvider${versionCapitalized}; -#end +import ca.uhn.fhir.jpa.provider${package_suffix}.*; import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.annotation.*; @@ -26,7 +22,7 @@ import ca.uhn.fhir.rest.api.SortSpec; public class ${className}ResourceProvider extends ## We have specialized base classes for RPs that handle certain resource types. These ## RPs implement type specific operations -#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem')) +#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem' || ($version != 'dstu2' && ${className} == 'ConceptMap'))) BaseJpaResourceProvider${className}${versionCapitalized} #else JpaResourceProvider${versionCapitalized}<${className}> diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm index 13aa1fdbd82..c5fd9c91777 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm @@ -44,7 +44,11 @@ #foreach ( $res in $resources ) +#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap') + class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}"> +#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter')) class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}"> #else class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}"> @@ -59,4 +63,4 @@ #end - \ No newline at end of file + diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm index cb9cee934cc..2cad6d3e9c5 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm @@ -58,13 +58,21 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp IFhirResourceDaoCodeSystem #elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'CodeSystem' ) IFhirResourceDaoCodeSystem +#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap' ) + IFhirResourceDaoConceptMap +#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap' ) + IFhirResourceDaoConceptMap #elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter')) IFhirResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}> #else IFhirResourceDao<${resourcePackage}.${res.declaringClassNameComplete}> #end dao${res.declaringClassNameComplete}${versionCapitalized}() { -#if ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem')) +#if ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap' ) + ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); +#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap' ) + ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); +#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem')) ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}(); #else ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}> retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}>(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 499e2d02efd..80676fabdc5 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -120,6 +120,10 @@ Fix a bug in the DSTU2 QuestionnaireResponseValidator which prevented validation on groups with only one question. Thanks David Gileadi for the pull request! + + The ConceptMap]] operation $translate]]> has been + implemented. + @@ -694,7 +698,7 @@ ALTER TABLE hfj_res_ver ALTER COLUMN res_text DROP NOT NULL; in FHIR resource (XML/JSON) format. - The Binary resource endpoint now supports the `X-Security-Context` header when + The Binary resource endpoint now supports the X-Security-Context]]> header when reading or writing Binary contents using their native Content-Type (i.e exchanging the raw binary with the server, as opposed to exchanging a FHIR resource).