From df4b6c798c8989a3e3a91d84ad8d981189b8b545 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Tue, 24 Sep 2019 09:21:42 -0400 Subject: [PATCH 01/23] Modified LOINC Terms to RadLex RPIDs ID and URL; pattern is mappings of main LOINC terms to say 'loinc' but parts and other codes are specified. --- .../jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index 4ccaaf9391b..57898fdc4f9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -42,8 +42,8 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme public static final String LOINC_SCT_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct"; private static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT"; - public static final String LOINC_TERM_TO_RPID_PART_MAP_ID = "loinc-term-to-rpids"; - public static final String LOINC_TERM_TO_RPID_PART_MAP_URI = "http://loinc.org/cm/loinc-term-to-rpids"; + public static final String LOINC_TERM_TO_RPID_PART_MAP_ID = "loinc-to-rpids"; + public static final String LOINC_TERM_TO_RPID_PART_MAP_URI = "http://loinc.org/cm/loinc-to-rpids"; public static final String LOINC_TERM_TO_RPID_PART_MAP_NAME = "LOINC Terms to RadLex RPIDs"; public static final String LOINC_PART_TO_RID_PART_MAP_ID = "loinc-part-to-rids"; From 46a6b7aaef677d1e5ce86de01592d0b49f2dc391 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Tue, 24 Sep 2019 15:30:40 -0400 Subject: [PATCH 02/23] Troubleshooting lack of ValueSet pre-expansion when updating a ValueSet via transaction bundle. --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 8 +- .../r4/ResourceProviderR4ValueSetTest.java | 188 +++++++++++++++++- 2 files changed, 190 insertions(+), 6 deletions(-) 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 d66593320bc..46ab97242e2 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 @@ -1870,7 +1870,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, @Override @Transactional public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) { - ourLog.info("Storing TermConceptMap {}", theConceptMap.getIdElement().getValue()); + ourLog.info("Storing TermConceptMap for {}", theConceptMap.getIdElement().toVersionless().getValueAsString()); ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied"); ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theConceptMap.getUrl(), "ConceptMap has no value for ConceptMap.url"); @@ -1981,7 +1981,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, throw new UnprocessableEntityException(msg); } - ourLog.info("Done storing TermConceptMap[{}]", termConceptMap.getId()); + ourLog.info("Done storing TermConceptMap[{}] for {}", termConceptMap.getId(), theConceptMap.getIdElement().toVersionless().getValueAsString()); } @Override @@ -2077,7 +2077,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, @Override @Transactional public void storeTermValueSet(ResourceTable theResourceTable, ValueSet theValueSet) { - ourLog.info("Storing TermValueSet {}", theValueSet.getIdElement().getValue()); + ourLog.info("Storing TermValueSet for {}", theValueSet.getIdElement().toVersionless().getValueAsString()); ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied"); ValidateUtil.isNotBlankOrThrowUnprocessableEntity(theValueSet.getUrl(), "ValueSet has no value for ValueSet.url"); @@ -2111,7 +2111,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, throw new UnprocessableEntityException(msg); } - ourLog.info("Done storing TermValueSet[{}]", termValueSet.getId()); + ourLog.info("Done storing TermValueSet[{}] for {}", termValueSet.getId(), theValueSet.getIdElement().toVersionless().getValueAsString()); } @Override diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 967eac53868..94aeccc0712 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -3,8 +3,7 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; -import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; @@ -36,6 +35,7 @@ import org.springframework.transaction.support.TransactionTemplate; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.Optional; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET; @@ -57,12 +57,28 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { loadAndPersistValueSet(theVerb); } + private void loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb theVerb) throws IOException { + loadAndPersistCodeSystemWithDesignations(theVerb); + loadAndPersistValueSet(theVerb); + } + + private void loadAndPersistCodeSystemAndValueSetWithDesignationsAndExclude(HttpVerb theVerb) throws IOException { + loadAndPersistCodeSystemWithDesignations(theVerb); + loadAndPersistValueSetWithExclude(theVerb); + } + private void loadAndPersistCodeSystem(HttpVerb theVerb) throws IOException { CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml"); codeSystem.setId("CodeSystem/cs"); persistCodeSystem(codeSystem, theVerb); } + private void loadAndPersistCodeSystemWithDesignations(HttpVerb theVerb) throws IOException { + CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-with-designations.xml"); + codeSystem.setId("CodeSystem/cs"); + persistCodeSystem(codeSystem, theVerb); + } + private void persistCodeSystem(CodeSystem theCodeSystem, HttpVerb theVerb) { switch (theVerb) { case POST: @@ -93,6 +109,12 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { persistValueSet(valueSet, theVerb); } + private void loadAndPersistValueSetWithExclude(HttpVerb theVerb) throws IOException { + ValueSet valueSet = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs-with-exclude.xml"); + valueSet.setId("ValueSet/vs"); + persistValueSet(valueSet, theVerb); + } + private void persistValueSet(ValueSet theValueSet, HttpVerb theVerb) { switch (theVerb) { case POST: @@ -687,6 +709,168 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } } + @Test + public void testUpdateValueSetTriggersAnotherPreExpansion() throws Exception { + myDaoConfig.setPreExpandValueSetsExperimental(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); + + CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); + + ValueSet valueSet = myValueSetDao.read(myExtensionalVsId); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet)); + + String initialValueSetName = valueSet.getName(); + validateTermValueSetNotExpanded(initialValueSetName); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildren(initialValueSetName, codeSystem); + + ValueSet updatedValueSet = valueSet; + updatedValueSet.setName(valueSet.getName().concat(" - MODIFIED")); + persistValueSet(updatedValueSet, HttpVerb.PUT); + updatedValueSet = myValueSetDao.read(myExtensionalVsId); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet)); + + String updatedValueSetName = valueSet.getName(); + validateTermValueSetNotExpanded(updatedValueSetName); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildren(updatedValueSetName, codeSystem); + } + + @Test + public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { + myDaoConfig.setPreExpandValueSetsExperimental(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); + + CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); + + ValueSet valueSet = myValueSetDao.read(myExtensionalVsId); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet)); + + String initialValueSetName = valueSet.getName(); + validateTermValueSetNotExpanded(initialValueSetName); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildren(initialValueSetName, codeSystem); + + ValueSet updatedValueSet = valueSet; + updatedValueSet.setName(valueSet.getName().concat(" - MODIFIED")); + + String requestUrl = ourClient.getServerBase().concat("/").concat(myExtensionalVsId.getValueAsString()); + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + bundle + .addEntry() + .setFullUrl(updatedValueSet.getUrl()) + .setResource(updatedValueSet) + .getRequest() + .setMethod(Bundle.HTTPVerb.PUT) + .setUrl(requestUrl); + ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + ourClient.transaction().withBundle(bundle).execute(); + + updatedValueSet = myValueSetDao.read(myExtensionalVsId); + ourLog.info("Updated ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedValueSet)); + + String updatedValueSetName = valueSet.getName(); + validateTermValueSetNotExpanded(updatedValueSetName); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + validateTermValueSetExpandedAndChildren(updatedValueSetName, codeSystem); + } + + private void validateTermValueSetNotExpanded(String theValueSetName) { + runInTransaction(()->{ + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findByUrl("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(0, termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.NOT_EXPANDED, termValueSet.getExpansionStatus()); + }); + } + + private void validateTermValueSetExpandedAndChildren(String theValueSetName, CodeSystem theCodeSystem) { + runInTransaction(()->{ + Optional optionalValueSetByResourcePid = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable); + assertTrue(optionalValueSetByResourcePid.isPresent()); + + Optional optionalValueSetByUrl = myTermValueSetDao.findByUrl("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); + assertTrue(optionalValueSetByUrl.isPresent()); + + TermValueSet termValueSet = optionalValueSetByUrl.get(); + assertSame(optionalValueSetByResourcePid.get(), termValueSet); + ourLog.info("ValueSet:\n" + termValueSet.toString()); + assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", termValueSet.getUrl()); + assertEquals(theValueSetName, termValueSet.getName()); + assertEquals(theCodeSystem.getConcept().size(), termValueSet.getConcepts().size()); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, termValueSet.getExpansionStatus()); + + TermValueSetConcept concept = termValueSet.getConcepts().get(0); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8450-9", concept.getCode()); + assertEquals("Systolic blood pressure--expiration", concept.getDisplay()); + assertEquals(2, concept.getDesignations().size()); + assertEquals(0, concept.getOrder()); + + TermValueSetConceptDesignation designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk - expiratie", designation.getValue()); + + designation = concept.getDesignations().get(1); + assertEquals("sv", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systoliskt blodtryck - utgång", designation.getValue()); + + concept = termValueSet.getConcepts().get(1); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("11378-7", concept.getCode()); + assertEquals("Systolic blood pressure at First encounter", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(1, concept.getOrder()); + + // ... + + concept = termValueSet.getConcepts().get(22); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8491-3", concept.getCode()); + assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay()); + assertEquals(1, concept.getDesignations().size()); + assertEquals(22, concept.getOrder()); + + designation = concept.getDesignations().get(0); + assertEquals("nl", designation.getLanguage()); + assertEquals("http://snomed.info/sct", designation.getUseSystem()); + assertEquals("900000000000013009", designation.getUseCode()); + assertEquals("Synonym", designation.getUseDisplay()); + assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue()); + + concept = termValueSet.getConcepts().get(23); + ourLog.info("Concept:\n" + concept.toString()); + assertEquals("http://acme.org", concept.getSystem()); + assertEquals("8492-1", concept.getCode()); + assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay()); + assertEquals(0, concept.getDesignations().size()); + assertEquals(23, concept.getOrder()); + }); + } + @Test public void testValidateCodeOperationByCodeAndSystemInstance() throws Exception { loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); From bf5d6acef4657f2fe0b64dfaffc9d680fa616039 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Tue, 24 Sep 2019 15:36:06 -0400 Subject: [PATCH 03/23] More troubleshooting. --- .../jpa/provider/r4/ResourceProviderR4ValueSetTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 94aeccc0712..efa66baf175 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -758,16 +758,16 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { ValueSet updatedValueSet = valueSet; updatedValueSet.setName(valueSet.getName().concat(" - MODIFIED")); - String requestUrl = ourClient.getServerBase().concat("/").concat(myExtensionalVsId.getValueAsString()); + String url = ourClient.getServerBase().concat("/").concat(myExtensionalVsId.getValueAsString()); Bundle bundle = new Bundle(); bundle.setType(Bundle.BundleType.TRANSACTION); bundle .addEntry() - .setFullUrl(updatedValueSet.getUrl()) + .setFullUrl(url) .setResource(updatedValueSet) .getRequest() .setMethod(Bundle.HTTPVerb.PUT) - .setUrl(requestUrl); + .setUrl(url); ourLog.info("Transaction Bundle:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); ourClient.transaction().withBundle(bundle).execute(); From 21b91ac0e700fdd942d5901fb0f283af8762c22b Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 24 Sep 2019 16:16:13 -0400 Subject: [PATCH 04/23] Make sure we call the updateEntity method on the correct DAO type when processing inside a transaction --- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 6 ++++-- .../dao/FhirResourceDaoSubscriptionDstu2.java | 4 ++-- .../main/java/ca/uhn/fhir/jpa/dao/IJpaDao.java | 18 ++++++++++++++++++ .../uhn/fhir/jpa/dao/TransactionProcessor.java | 7 +++++-- .../dstu3/FhirResourceDaoCodeSystemDstu3.java | 4 ++-- .../dstu3/FhirResourceDaoConceptMapDstu3.java | 4 ++-- .../FhirResourceDaoSubscriptionDstu3.java | 4 ++-- .../dstu3/FhirResourceDaoValueSetDstu3.java | 4 ++-- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 4 ++-- .../dao/r4/FhirResourceDaoConceptMapR4.java | 4 ++-- .../dao/r4/FhirResourceDaoSubscriptionR4.java | 4 ++-- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 5 +++-- .../dao/r5/FhirResourceDaoCodeSystemR5.java | 4 ++-- .../dao/r5/FhirResourceDaoConceptMapR5.java | 2 +- .../dao/r5/FhirResourceDaoSubscriptionR5.java | 4 ++-- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 4 ++-- .../r4/ResourceProviderR4ValueSetTest.java | 2 +- 17 files changed, 54 insertions(+), 30 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IJpaDao.java 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 64414a391ea..0a3c7eff1a4 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 @@ -111,7 +111,7 @@ import static org.apache.commons.lang3.StringUtils.*; @SuppressWarnings("WeakerAccess") @Repository -public abstract class BaseHapiFhirDao implements IDao, ApplicationContextAware { +public abstract class BaseHapiFhirDao implements IDao, IJpaDao, ApplicationContextAware { public static final long INDEX_STATUS_INDEXED = 1L; public static final long INDEX_STATUS_INDEXING_FAILED = 2L; @@ -1006,7 +1006,8 @@ public abstract class BaseHapiFhirDao implements IDao, } @SuppressWarnings("unchecked") - protected ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, ResourceTable + @Override + public ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { Validate.notNull(theEntity); @@ -1245,6 +1246,7 @@ public abstract class BaseHapiFhirDao implements IDao, return theEntity; } + @Override public ResourceTable updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion, ResourceTable theEntity, IIdType theResourceId, IBaseResource theOldResource) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java index 6ce3e00419a..ddf27ce58e8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java @@ -73,8 +73,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2 { + @SuppressWarnings("unchecked") + ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable + theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry); + + ResourceTable updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion, + ResourceTable theEntity, IIdType theResourceId, IBaseResource theOldResource); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index 5dcdd2e90eb..c0a5274fe8c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -909,10 +909,13 @@ public class TransactionProcessor { IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; + IFhirResourceDao dao = myDaoRegistry.getResourceDao(nextResource.getClass()); + IJpaDao jpaDao = (IJpaDao) dao; + if (updatedEntities.contains(nextOutcome.getEntity())) { - myDao.updateInternal(theRequest, nextResource, true, false, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource()); + jpaDao.updateInternal(theRequest, nextResource, true, false, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource()); } else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) { - myDao.updateEntity(theRequest, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true); + jpaDao.updateEntity(theRequest, nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index b8acd0d8f0d..6759858c732 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -125,8 +125,8 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3 } @Override - protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + public 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); if (myDaoConfig.isPreExpandValueSetsExperimental()) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 7fc850e4a8b..bc9768ca162 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -128,8 +128,8 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4 i } @Override - protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); CodeSystem cs = (CodeSystem) theResource; 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 index baedded13f4..b553e42baa6 100644 --- 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 @@ -158,8 +158,8 @@ public class FhirResourceDaoConceptMapR4 extends FhirResourceDaoR4 i } @Override - protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + public 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); if (retVal.getDeleted() == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java index 1455ef2191d..286e456dc76 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java @@ -75,8 +75,8 @@ public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4 imple } @Override - protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + public 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); if (myDaoConfig.isPreExpandValueSetsExperimental()) { @@ -407,4 +407,5 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple return retVal; } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java index bb8d651839f..515355bc044 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCodeSystemR5.java @@ -128,8 +128,8 @@ public class FhirResourceDaoCodeSystemR5 extends FhirResourceDaoR5 i } @Override - protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); CodeSystem cs = (CodeSystem) theResource; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java index 467dae02d64..dab3f9c764d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoConceptMapR5.java @@ -157,7 +157,7 @@ public class FhirResourceDaoConceptMapR5 extends FhirResourceDaoR5 i } @Override - protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, + public 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); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoSubscriptionR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoSubscriptionR5.java index 535e6a49d35..da052fbba12 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoSubscriptionR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoSubscriptionR5.java @@ -73,8 +73,8 @@ public class FhirResourceDaoSubscriptionR5 extends FhirResourceDaoR5 imple } @Override - protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, - boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { + public 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); if (myDaoConfig.isPreExpandValueSetsExperimental()) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index efa66baf175..1072a4c7e66 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -742,7 +742,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { myDaoConfig.setPreExpandValueSetsExperimental(true); - loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); + loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); From 5743efa066335b75eedd053a24ed95729ea80a56 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Tue, 24 Sep 2019 16:46:05 -0400 Subject: [PATCH 05/23] Minor tweak to test. --- .../fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index 1072a4c7e66..efa66baf175 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -742,7 +742,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { myDaoConfig.setPreExpandValueSetsExperimental(true); - loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); + loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); From 6b22d705c7d70e9cc7a4862e9f20161f3fc459d7 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 13:24:50 -0400 Subject: [PATCH 06/23] Incremental progress fixing tests after enabling pre-expansion by default. --- .../java/ca/uhn/fhir/jpa/dao/DaoConfig.java | 117 ++++++++---------- .../dstu3/FhirResourceDaoValueSetDstu3.java | 6 +- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 6 +- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 6 +- .../BaseJpaResourceProviderValueSetDstu3.java | 8 +- .../r4/BaseJpaResourceProviderValueSetR4.java | 8 +- .../r5/BaseJpaResourceProviderValueSetR5.java | 8 +- .../jpa/term/BaseHapiTerminologySvcImpl.java | 35 +++--- .../FhirResourceDaoDstu3ValueSetTest.java | 2 +- .../dao/r4/FhirResourceDaoR4ValueSetTest.java | 6 +- .../ResourceProviderDstu3ValueSetTest.java | 20 +-- .../provider/r4/ResourceProviderR4Test.java | 4 +- .../r4/ResourceProviderR4ValueSetTest.java | 18 +-- .../jpa/term/TerminologySvcImplR4Test.java | 82 ++++++------ 14 files changed, 163 insertions(+), 163 deletions(-) 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 32a8f556bb9..fa1ac8383d7 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 @@ -149,21 +149,21 @@ public class DaoConfig { private boolean myFilterParameterEnabled = false; private StoreMetaSourceInformationEnum myStoreMetaSourceInformation = StoreMetaSourceInformationEnum.SOURCE_URI_AND_REQUEST_ID; /** - * EXPERIMENTAL - Do not use in production! Do not change default of {@code false}! + * Do not change default of {@code true}! */ - private boolean myPreExpandValueSetsExperimental = false; + private boolean myPreExpandValueSets = true; /** - * EXPERIMENTAL - Do not use in production! Do not change default of {@code 0}! + * Do not change default of {@code 0}! */ - private int myPreExpandValueSetsDefaultOffsetExperimental = 0; + private int myPreExpandValueSetsDefaultOffset = 0; /** - * EXPERIMENTAL - Do not use in production! Do not change default of {@code 1000}! + * Do not change default of {@code 1000}! */ - private int myPreExpandValueSetsDefaultCountExperimental = 1000; + private int myPreExpandValueSetsDefaultCount = 1000; /** - * EXPERIMENTAL - Do not use in production! Do not change default of {@code 1000}! + * Do not change default of {@code 1000}! */ - private int myPreExpandValueSetsMaxCountExperimental = 1000; + private int myPreExpandValueSetsMaxCount = 1000; /** * Constructor @@ -920,7 +920,7 @@ public class DaoConfig { *

* Default is {@literal true} beginning in HAPI FHIR 2.4, since this * feature is now specified in the FHIR specification. (Previously it - * was an experimental/rpposed feature) + * was an experimental/proposed feature) *

* * @since 1.5 @@ -1621,34 +1621,6 @@ public class DaoConfig { myModelConfig.setWebsocketContextPath(theWebsocketContextPath); } - /** - * EXPERIMENTAL - Do not use in production! - *

- * If set to {@code true}, ValueSets and expansions are stored in terminology tables. This is to facilitate - * future optimization of the $expand operation on large ValueSets. - *

- *

- * The default value for this setting is {@code false}. - *

- */ - public boolean isPreExpandValueSetsExperimental() { - return myPreExpandValueSetsExperimental; - } - - /** - * EXPERIMENTAL - Do not use in production! - *

- * If set to {@code true}, ValueSets and expansions are stored in terminology tables. This is to facilitate - * future optimization of the $expand operation on large ValueSets. - *

- *

- * The default value for this setting is {@code false}. - *

- */ - public void setPreExpandValueSetsExperimental(boolean thePreExpandValueSetsExperimental) { - myPreExpandValueSetsExperimental = thePreExpandValueSetsExperimental; - } - /** * If set to true the _filter search parameter will be enabled on this server. Note that _filter * is very powerful, but also potentially dangerous as it can allow a user to create a query for which there @@ -1720,83 +1692,104 @@ public class DaoConfig { } /** - * EXPERIMENTAL - Do not use in production! + *

+ * If set to {@code true}, ValueSets and expansions are stored in terminology tables. This is to facilitate + * optimization of the $expand operation on large ValueSets. + *

+ *

+ * The default value for this setting is {@code true}. + *

+ */ + public boolean isPreExpandValueSets() { + return myPreExpandValueSets; + } + + /** + *

+ * If set to {@code true}, ValueSets and expansions are stored in terminology tables. This is to facilitate + * optimization of the $expand operation on large ValueSets. + *

+ *

+ * The default value for this setting is {@code true}. + *

+ */ + public void setPreExpandValueSets(boolean thePreExpandValueSets) { + myPreExpandValueSets = thePreExpandValueSets; + } + + /** *

* This is the default value of {@code offset} parameter for the ValueSet {@code $expand} operation when - * {@link DaoConfig#isPreExpandValueSetsExperimental()} returns {@code true}. + * {@link DaoConfig#isPreExpandValueSets()} returns {@code true}. *

*

* The default value for this setting is {@code 0}. *

*/ - public int getPreExpandValueSetsDefaultOffsetExperimental() { - return myPreExpandValueSetsDefaultOffsetExperimental; + public int getPreExpandValueSetsDefaultOffset() { + return myPreExpandValueSetsDefaultOffset; } /** - * EXPERIMENTAL - Do not use in production! *

* This is the default value of {@code count} parameter for the ValueSet {@code $expand} operation when - * {@link DaoConfig#isPreExpandValueSetsExperimental()} returns {@code true}. + * {@link DaoConfig#isPreExpandValueSets()} returns {@code true}. *

*

* The default value for this setting is {@code 1000}. *

*/ - public int getPreExpandValueSetsDefaultCountExperimental() { - return myPreExpandValueSetsDefaultCountExperimental; + public int getPreExpandValueSetsDefaultCount() { + return myPreExpandValueSetsDefaultCount; } /** - * EXPERIMENTAL - Do not use in production! *

* This is the default value of {@code count} parameter for the ValueSet {@code $expand} operation when - * {@link DaoConfig#isPreExpandValueSetsExperimental()} returns {@code true}. + * {@link DaoConfig#isPreExpandValueSets()} returns {@code true}. *

*

- * If {@code thePreExpandValueSetsDefaultCountExperimental} is greater than - * {@link DaoConfig#getPreExpandValueSetsMaxCountExperimental()}, the lesser value is used. + * If {@code thePreExpandValueSetsDefaultCount} is greater than + * {@link DaoConfig#getPreExpandValueSetsMaxCount()}, the lesser value is used. *

*

* The default value for this setting is {@code 1000}. *

*/ - public void setPreExpandValueSetsDefaultCountExperimental(int thePreExpandValueSetsDefaultCountExperimental) { - myPreExpandValueSetsDefaultCountExperimental = Math.min(thePreExpandValueSetsDefaultCountExperimental, getPreExpandValueSetsMaxCountExperimental()); + public void setPreExpandValueSetsDefaultCount(int thePreExpandValueSetsDefaultCount) { + myPreExpandValueSetsDefaultCount = Math.min(thePreExpandValueSetsDefaultCount, getPreExpandValueSetsMaxCount()); } /** - * EXPERIMENTAL - Do not use in production! *

* This is the max value of {@code count} parameter for the ValueSet {@code $expand} operation when - * {@link DaoConfig#isPreExpandValueSetsExperimental()} returns {@code true}. + * {@link DaoConfig#isPreExpandValueSets()} returns {@code true}. *

*

* The default value for this setting is {@code 1000}. *

*/ - public int getPreExpandValueSetsMaxCountExperimental() { - return myPreExpandValueSetsMaxCountExperimental; + public int getPreExpandValueSetsMaxCount() { + return myPreExpandValueSetsMaxCount; } /** - * EXPERIMENTAL - Do not use in production! *

* This is the max value of {@code count} parameter for the ValueSet {@code $expand} operation when - * {@link DaoConfig#isPreExpandValueSetsExperimental()} returns {@code true}. + * {@link DaoConfig#isPreExpandValueSets()} returns {@code true}. *

*

- * If {@code thePreExpandValueSetsMaxCountExperimental} is lesser than - * {@link DaoConfig#getPreExpandValueSetsDefaultCountExperimental()}, the default {@code count} is lowered to the + * If {@code thePreExpandValueSetsMaxCount} is lesser than + * {@link DaoConfig#getPreExpandValueSetsDefaultCount()}, the default {@code count} is lowered to the * new max {@code count}. *

*

* The default value for this setting is {@code 1000}. *

*/ - public void setPreExpandValueSetsMaxCountExperimental(int thePreExpandValueSetsMaxCountExperimental) { - myPreExpandValueSetsMaxCountExperimental = thePreExpandValueSetsMaxCountExperimental; - setPreExpandValueSetsDefaultCountExperimental(Math.min(getPreExpandValueSetsDefaultCountExperimental(), getPreExpandValueSetsMaxCountExperimental())); + public void setPreExpandValueSetsMaxCount(int thePreExpandValueSetsMaxCount) { + myPreExpandValueSetsMaxCount = thePreExpandValueSetsMaxCount; + setPreExpandValueSetsDefaultCount(Math.min(getPreExpandValueSetsDefaultCount(), getPreExpandValueSetsMaxCount())); } public enum IndexEnabledEnum { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 241f757096f..b133d7c90d7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -310,6 +310,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 boolean haveIdentifierParam = theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false; ValueSet vs = null; + boolean isBuiltInValueSet = false; if (theId != null) { vs = read(theId, theRequestDetails); } else if (haveIdentifierParam) { @@ -317,6 +318,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 if (vs == null) { throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); } + isBuiltInValueSet = true; } else { if (theCode == null || theCode.isEmpty()) { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); @@ -332,7 +334,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 if (vs != null) { ValidateCodeResult result; - if (myDaoConfig.isPreExpandValueSetsExperimental() && myTerminologySvc.isValueSetPreExpandedForCodeValidation(vs)) { + if (myDaoConfig.isPreExpandValueSets() && !isBuiltInValueSet && myTerminologySvc.isValueSetPreExpandedForCodeValidation(vs)) { result = myTerminologySvc.validateCodeIsInPreExpandedValueSet(vs, toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); } else { ValueSet expansion = doExpand(vs); @@ -399,7 +401,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); - if (myDaoConfig.isPreExpandValueSetsExperimental()) { + if (myDaoConfig.isPreExpandValueSets()) { if (retVal.getDeleted() == null) { try { ValueSet valueSet = (ValueSet) theResource; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java index a33e273f4a5..2f28f4dad1a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java @@ -306,6 +306,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple boolean haveIdentifierParam = theValueSetIdentifier != null && !theValueSetIdentifier.isEmpty(); ValueSet vs = null; + boolean isBuiltInValueSet = false; if (theId != null) { vs = read(theId, theRequestDetails); } else if (haveIdentifierParam) { @@ -313,6 +314,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple if (vs == null) { throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); } + isBuiltInValueSet = true; } else { if (theCode == null || theCode.isEmpty()) { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); @@ -328,7 +330,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple if (vs != null) { ValidateCodeResult result; - if (myDaoConfig.isPreExpandValueSetsExperimental() && myTerminologySvc.isValueSetPreExpandedForCodeValidation(vs)) { + if (myDaoConfig.isPreExpandValueSets() && !isBuiltInValueSet && myTerminologySvc.isValueSetPreExpandedForCodeValidation(vs)) { result = myTerminologySvc.validateCodeIsInPreExpandedValueSet(vs, toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); } else { ValueSet expansion = doExpand(vs); @@ -395,7 +397,7 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); - if (myDaoConfig.isPreExpandValueSetsExperimental()) { + if (myDaoConfig.isPreExpandValueSets()) { if (retVal.getDeleted() == null) { ValueSet valueSet = (ValueSet) theResource; myHapiTerminologySvc.storeTermValueSet(retVal, valueSet); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 9995f6da23c..549a7b6aa0b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -312,6 +312,7 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5 imple boolean haveIdentifierParam = theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false; ValueSet vs = null; + boolean isBuiltInValueSet = false; if (theId != null) { vs = read(theId, theRequestDetails); } else if (haveIdentifierParam) { @@ -319,6 +320,7 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5 imple if (vs == null) { throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); } + isBuiltInValueSet = true; } else { if (theCode == null || theCode.isEmpty()) { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); @@ -334,7 +336,7 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5 imple if (vs != null) { ValidateCodeResult result; - if (myDaoConfig.isPreExpandValueSetsExperimental() && myTerminologySvc.isValueSetPreExpandedForCodeValidation(vs)) { + if (myDaoConfig.isPreExpandValueSets() && !isBuiltInValueSet && myTerminologySvc.isValueSetPreExpandedForCodeValidation(vs)) { result = myTerminologySvc.validateCodeIsInPreExpandedValueSet(vs, toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept); } else { ValueSet expansion = doExpand(vs); @@ -401,7 +403,7 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5 imple boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); - if (myDaoConfig.isPreExpandValueSetsExperimental()) { + if (myDaoConfig.isPreExpandValueSets()) { if (retVal.getDeleted() == null) { ValueSet valueSet = (ValueSet) theResource; myHapiTerminologySvc.storeTermValueSet(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java index 884941f053c..0d28d7ecf73 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderValueSetDstu3.java @@ -68,7 +68,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options."); } - int offset = myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(); + int offset = myDaoConfig.getPreExpandValueSetsDefaultOffset(); if (theOffset != null && theOffset.hasValue()) { if (theOffset.getValue() >= 0) { offset = theOffset.getValue(); @@ -77,7 +77,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst } } - int count = myDaoConfig.getPreExpandValueSetsDefaultCountExperimental(); + int count = myDaoConfig.getPreExpandValueSetsDefaultCount(); if (theCount != null && theCount.hasValue()) { if (theCount.getValue() >= 0) { count = theCount.getValue(); @@ -85,7 +85,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst throw new InvalidRequestException("count parameter for $expand operation must be >= 0 when specified. count: " + theCount.getValue()); } } - int countMax = myDaoConfig.getPreExpandValueSetsMaxCountExperimental(); + int countMax = myDaoConfig.getPreExpandValueSetsMaxCount(); if (count > countMax) { ourLog.warn("count parameter for $expand operation of {} exceeds maximum value of {}; using maximum value.", count, countMax); count = countMax; @@ -94,7 +94,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst startRequest(theServletRequest); try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); - if (myDaoConfig.isPreExpandValueSetsExperimental()) { + if (myDaoConfig.isPreExpandValueSets()) { if (haveId) { return dao.expand(theId, toFilterString(theFilter), offset, count, theRequestDetails); } else if (haveIdentifier) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java index 5828b3f1356..f7f9f8c2a33 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderValueSetR4.java @@ -60,7 +60,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4= 0) { offset = theOffset.getValue(); @@ -69,7 +69,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4= 0) { count = theCount.getValue(); @@ -77,7 +77,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4= 0 when specified. count: " + theCount.getValue()); } } - int countMax = myDaoConfig.getPreExpandValueSetsMaxCountExperimental(); + int countMax = myDaoConfig.getPreExpandValueSetsMaxCount(); if (count > countMax) { ourLog.warn("count parameter for $expand operation of {} exceeds maximum value of {}; using maximum value.", count, countMax); count = countMax; @@ -86,7 +86,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4 dao = (IFhirResourceDaoValueSet) getDao(); - if (myDaoConfig.isPreExpandValueSetsExperimental()) { + if (myDaoConfig.isPreExpandValueSets()) { if (haveId) { return dao.expand(theId, toFilterString(theFilter), offset, count, theRequestDetails); } else if (haveIdentifier) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java index f05a8f4cdaa..6ef5a0fd6ae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderValueSetR5.java @@ -60,7 +60,7 @@ public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5= 0) { offset = theOffset.getValue(); @@ -69,7 +69,7 @@ public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5= 0) { count = theCount.getValue(); @@ -77,7 +77,7 @@ public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5= 0 when specified. count: " + theCount.getValue()); } } - int countMax = myDaoConfig.getPreExpandValueSetsMaxCountExperimental(); + int countMax = myDaoConfig.getPreExpandValueSetsMaxCount(); if (count > countMax) { ourLog.warn("count parameter for $expand operation of {} exceeds maximum value of {}; using maximum value.", count, countMax); count = countMax; @@ -86,7 +86,7 @@ public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5 dao = (IFhirResourceDaoValueSet) getDao(); - if (myDaoConfig.isPreExpandValueSetsExperimental()) { + if (myDaoConfig.isPreExpandValueSets()) { if (haveId) { return dao.expand(theId, toFilterString(theFilter), offset, count, theRequestDetails); } else if (haveIdentifier) { 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 46ab97242e2..a88c8c4a3bd 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 @@ -63,9 +63,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.quartz.Job; import org.quartz.JobExecutionContext; -import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -204,22 +204,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } } - private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { - bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); - } - - private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { - Query textQuery = qb - .phrase() - .withSlop(2) - .onField("myDisplay").boostedTo(4.0f) - .andField("myDisplayEdgeNGram").boostedTo(2.0f) - // .andField("myDisplayNGram").boostedTo(1.0f) - // .andField("myDisplayPhonetic").boostedTo(0.5f) - .sentence(nextFilter.getValue().toLowerCase()).createQuery(); - bool.must(textQuery); - } - private boolean addToSet(Set theSetToPopulate, TermConcept theConcept) { boolean retVal = theSetToPopulate.add(theConcept); if (retVal) { @@ -894,6 +878,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, throw new InvalidRequestException("Invalid filter, property " + theFilter.getProperty() + " is LOINC-specific and cannot be used with system: " + theSystem); } break; + // FIXME: DM 2019-09-24 - Need to implement LOINC-specific filters for "ancestor" and "descendant". case "copyright": if (isCodeSystemLoinc(theSystem)) { handleFilterLoincCopyright(theQb, theBool, theFilter); @@ -923,6 +908,22 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } } + private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { + bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); + } + + private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { + Query textQuery = qb + .phrase() + .withSlop(2) + .onField("myDisplay").boostedTo(4.0f) + .andField("myDisplayEdgeNGram").boostedTo(2.0f) + // .andField("myDisplayNGram").boostedTo(1.0f) + // .andField("myDisplayPhonetic").boostedTo(0.5f) + .sentence(nextFilter.getValue().toLowerCase()).createQuery(); + bool.must(textQuery); + } + private void handleFilterConceptAndCode(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { TermConcept code = findCode(theSystem, theFilter.getValue()) .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theFilter.getValue())); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java index a3c2e1dec11..0180911b8b2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValueSetTest.java @@ -229,7 +229,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { } @Test - public void testValiedateCodeAgainstBuiltInValueSetAndCodeSystemWithValidCode() { + public void testValidateCodeAgainstBuiltInValueSetAndCodeSystemWithValidCode() { IPrimitiveType display = null; Coding coding = null; CodeableConcept codeableConcept = null; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java index 10c0949f03d..0f1baea4010 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java @@ -22,7 +22,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test { @After public void after() { - myDaoConfig.setPreExpandValueSetsExperimental(new DaoConfig().isPreExpandValueSetsExperimental()); + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); } @AfterClass @@ -128,7 +128,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test { @Test public void testValidateCodeOperationByResourceIdAndCodeableConceptWithExistingValueSetAndPreExpansionEnabled() { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); UriType valueSetIdentifier = null; IIdType id = myExtensionalVsId; @@ -169,7 +169,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test { @Test public void testValidateCodeOperationByResourceIdAndCodeAndSystemWithExistingValueSetAndPreExpansionEnabled() { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); UriType valueSetIdentifier = null; IIdType id = myExtensionalVsId; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 43a56659ca6..4adc61472fb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -140,7 +140,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 createLocalVsWithUnknownCode(codeSystem); } - private void createLocalCsAndVs() { + private void createLocalCs() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setContent(CodeSystemContentMode.COMPLETE); @@ -155,8 +155,6 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 .addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA")) .addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB")); myCodeSystemDao.create(codeSystem, mySrd); - - createLocalVs(codeSystem); } @@ -242,6 +240,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 // Include vs = new ValueSet(); + vs.setUrl("http://www.example.org/vs"); vs.getCompose() .addInclude() .setSystem(CS_URL); @@ -282,6 +281,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 // Include vs = new ValueSet(); + vs.setUrl("http://www.example.org/vs"); vs.getCompose() .addInclude() .setSystem(CS_URL); @@ -329,7 +329,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 @Test public void testExpandByIdWithPreExpansion() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -445,7 +445,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 @Test public void testExpandByUrlWithPreExpansion() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -468,7 +468,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 @Test public void testExpandByUrlWithPreExpansionAndBogusUrl() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -746,7 +746,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 public void testValidateCodeOperationByCodeAndSystemInstanceOnInstance() throws IOException { loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); - createLocalCsAndVs(); + createLocalCs(); createLocalVsWithIncludeConcept(); String url = ourServerBase + @@ -789,7 +789,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 * Technically this is the wrong param name */ @Test - public void testValiedateCodeAgainstBuiltInSystem() throws Exception { + public void testValidateCodeAgainstBuiltInSystem() throws Exception { loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); Parameters respParam = ourClient @@ -819,7 +819,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 * Technically this is the right param name */ @Test - public void testValiedateCodeAgainstBuiltInSystemByUrl() throws Exception { + public void testValidateCodeAgainstBuiltInSystemByUrl() throws Exception { loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); Parameters respParam = ourClient @@ -847,7 +847,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 @After public void afterResetPreExpansionDefault() { - myDaoConfig.setPreExpandValueSetsExperimental(new DaoConfig().isPreExpandValueSetsExperimental()); + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); } @AfterClass diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 2dddbe66a89..235920d2517 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -846,7 +846,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } @Override - public void interceptResponse(IHttpResponse theResponse) { // TODO Auto-generated method stu + public void interceptResponse(IHttpResponse theResponse) { // TODO Auto-generated method stub } }); @@ -3919,7 +3919,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .returnBundle(Bundle.class) .execute(); final String uuid2 = toSearchUuidFromLinkNext(result2); - assertNotEquals(uuid1, uuid2); + assertNotEquals(uuid1, uuid2); // TODO: DM 2019-09-24 - This test fails intermittently; I suspect it's a timing issue with sleepOneClick(); } /* diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index efa66baf175..038b06e4a15 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -244,7 +244,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testExpandByIdWithPreExpansion() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -296,7 +296,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testExpandByIdWithFilterWithPreExpansion() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -355,7 +355,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testExpandByUrlWithPreExpansion() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -378,7 +378,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testExpandByUrlWithPreExpansionAndBogusUrl() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -420,7 +420,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testExpandByValueSetWithPreExpansion() throws IOException { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystem(HttpVerb.POST); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); @@ -711,7 +711,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testUpdateValueSetTriggersAnotherPreExpansion() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -740,7 +740,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @Test public void testUpdateValueSetTriggersAnotherPreExpansionUsingTransactionBundle() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -951,7 +951,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } @Test - public void testValiedateCodeAgainstBuiltInSystem() { + public void testValidateCodeAgainstBuiltInSystem() { Parameters respParam = ourClient .operation() .onType(ValueSet.class) @@ -1023,7 +1023,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { @After public void afterResetPreExpansionDefault() { - myDaoConfig.setPreExpandValueSetsExperimental(new DaoConfig().isPreExpandValueSetsExperimental()); + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); } @AfterClass 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 index da2f2c27963..e4a99c064fa 100644 --- 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 @@ -65,7 +65,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @After public void after() { myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); - myDaoConfig.setPreExpandValueSetsExperimental(new DaoConfig().isPreExpandValueSetsExperimental()); + myDaoConfig.setPreExpandValueSets(new DaoConfig().isPreExpandValueSets()); } private IIdType createCodeSystem() { @@ -629,7 +629,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testDeleteValueSet() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -641,7 +641,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); @@ -666,7 +666,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testDeleteValueSetWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -678,7 +678,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); TermValueSet termValueSet = myTermValueSetDao.findByResourcePid(myExtensionalVsIdOnResourceTable).get(); @@ -723,7 +723,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testDuplicateValueSetUrls() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); // DM 2019-03-05 - We pre-load our custom CodeSystem otherwise pre-expansion of the ValueSet will fail. loadAndPersistCodeSystemAndValueSet(HttpVerb.POST); @@ -736,7 +736,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildren() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -750,7 +750,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myCaptureQueriesListener.clear(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); @@ -760,7 +760,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { assertThat(myCaptureQueriesListener.getDeleteQueriesForCurrentThread(), empty()); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); @@ -821,7 +821,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { public void testExpandExistingValueSetNotPreExpanded() throws Exception { loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); @@ -829,11 +829,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { ValueSet valueSet = myValueSetDao.read(myExtensionalVsId); ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet)); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(0, expandedValueSet.getExpansion().getParameter().size()); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getContains().size()); @@ -885,11 +885,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { assertEquals("Systolic blood pressure 8 hour minimum", containsComponent.getDisplay()); assertFalse(containsComponent.hasDesignation()); - expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(0, expandedValueSet.getExpansion().getParameter().size()); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getContains().size()); @@ -944,7 +944,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -956,11 +956,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); @@ -1019,7 +1019,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithCount() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -1031,11 +1031,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), 23); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), 23); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); @@ -1088,7 +1088,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithCountWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -1100,11 +1100,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), 23); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), 23); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); @@ -1157,7 +1157,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithCountOfZero() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -1169,11 +1169,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), 0); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), 0); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); @@ -1185,7 +1185,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithCountOfZeroWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -1197,11 +1197,11 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), 0); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), 0); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); - assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffsetExperimental(), expandedValueSet.getExpansion().getOffset()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); @@ -1213,7 +1213,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithOffset() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -1225,7 +1225,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, 1, myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, 1, myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); @@ -1274,7 +1274,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithOffsetWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -1286,7 +1286,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); - ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, 1, myDaoConfig.getPreExpandValueSetsDefaultCountExperimental()); + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, 1, myDaoConfig.getPreExpandValueSetsDefaultCount()); ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); @@ -1335,7 +1335,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithOffsetAndCount() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -1390,7 +1390,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testExpandTermValueSetAndChildrenWithOffsetAndCountWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -2046,7 +2046,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testStoreTermValueSetAndChildren() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -2148,7 +2148,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testStoreTermValueSetAndChildrenWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); @@ -2250,7 +2250,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testStoreTermValueSetAndChildrenWithExclude() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignationsAndExclude(HttpVerb.POST); @@ -2352,7 +2352,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testStoreTermValueSetAndChildrenWithExcludeWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignationsAndExclude(HttpVerb.PUT); @@ -3600,7 +3600,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testValidateCodeIsInPreExpandedValueSet() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -3650,7 +3650,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test { @Test public void testValidateCodeIsInPreExpandedValueSetWithClientAssignedId() throws Exception { - myDaoConfig.setPreExpandValueSetsExperimental(true); + myDaoConfig.setPreExpandValueSets(true); loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.PUT); From 30588bf8ee44c16a43d34e8df865885a7e4ad01e Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 14:29:50 -0400 Subject: [PATCH 07/23] Completed fixing tests after enabling pre-expansion by default. --- .../jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java | 6 ++++-- .../fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java | 4 +++- .../jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java | 3 ++- .../ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index 441de7433ee..45b9d107d25 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -537,6 +537,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { @Test public void testIndexNoDuplicatesUri() { ValueSet res = new ValueSet(); + res.setUrl("http://www.example.org/vs"); res.getCompose().addInclude().setSystem("http://foo"); res.getCompose().addInclude().setSystem("http://bar"); res.getCompose().addInclude().setSystem("http://foo"); @@ -549,7 +550,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { Class type = ResourceIndexedSearchParamUri.class; List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); ourLog.info(toStringMultiline(results)); - assertEquals(2, results.size()); + assertEquals(3, results.size()); List actual = toUnqualifiedVersionlessIds(myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_REFERENCE, new UriParam("http://foo")))); assertThat(actual, contains(id)); @@ -2161,13 +2162,14 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { } @Test - public void testSearchUriWrongParam() throws Exception { + public void testSearchUriWrongParam() { ValueSet v1 = new ValueSet(); v1.getUrlElement().setValue("http://foo"); String id1 = myValueSetDao.create(v1).getId().toUnqualifiedVersionless().getValue(); ValueSet v2 = new ValueSet(); v2.getExpansion().getIdentifierElement().setValue("http://foo"); + v2.getUrlElement().setValue("http://www.example.org/vs"); String id2 = myValueSetDao.create(v2).getId().toUnqualifiedVersionless().getValue(); { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 46c25066285..834526e728f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -914,6 +914,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { @Test public void testIndexNoDuplicatesUri() { ValueSet res = new ValueSet(); + res.setUrl("http://www.example.org/vs"); res.getCompose().addInclude().setSystem("http://foo"); res.getCompose().addInclude().setSystem("http://bar"); res.getCompose().addInclude().setSystem("http://foo"); @@ -927,7 +928,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { Class type = ResourceIndexedSearchParamUri.class; List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); ourLog.info(toStringMultiline(results)); - assertEquals(2, results.size()); + assertEquals(3, results.size()); }); List actual = toUnqualifiedVersionlessIds(myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_REFERENCE, new UriParam("http://foo")))); @@ -2944,6 +2945,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { ValueSet v2 = new ValueSet(); v2.getExpansion().getIdentifierElement().setValue("http://foo"); + v2.getUrlElement().setValue("http://www.example.org/vs"); String id2 = myValueSetDao.create(v2).getId().toUnqualifiedVersionless().getValue(); { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java index d38c66269b5..638c19de59e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java @@ -772,6 +772,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test { @Test public void testIndexNoDuplicatesUri() { ValueSet res = new ValueSet(); + res.setUrl("http://www.example.org/vs"); res.getCompose().addInclude().setSystem("http://foo"); res.getCompose().addInclude().setSystem("http://bar"); res.getCompose().addInclude().setSystem("http://foo"); @@ -785,7 +786,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test { Class type = ResourceIndexedSearchParamUri.class; List results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i WHERE i.myMissing = false", type).getResultList(); ourLog.info(toStringMultiline(results)); - assertEquals(2, results.size()); + assertEquals(3, results.size()); }); List actual = toUnqualifiedVersionlessIds(myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ValueSet.SP_REFERENCE, new UriParam("http://foo")))); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 235920d2517..c07d8e9e48c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -3919,7 +3919,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .returnBundle(Bundle.class) .execute(); final String uuid2 = toSearchUuidFromLinkNext(result2); - assertNotEquals(uuid1, uuid2); // TODO: DM 2019-09-24 - This test fails intermittently; I suspect it's a timing issue with sleepOneClick(); + assertNotEquals(uuid1, uuid2); // TODO: DM 2019-09-24 - This assertion fails intermittently. } /* From 729d3a3d3bffc365d6d656835337052330c91318 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 15:32:39 -0400 Subject: [PATCH 08/23] Fix intermittent test failure. --- .../jpa/provider/r4/ResourceProviderR4Test.java | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index c07d8e9e48c..b569dd546e3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -70,6 +70,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; +import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast; import static ca.uhn.fhir.jpa.util.TestUtil.sleepOneClick; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hamcrest.Matchers.*; @@ -3900,10 +3901,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); - /* - * First, make sure that we don't reuse a search if - * it's not marked with an expiry - */ + { myDaoConfig.setReuseCachedSearchResultsForMillis(10L); Bundle result1 = ourClient @@ -3912,20 +3910,16 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .returnBundle(Bundle.class) .execute(); final String uuid1 = toSearchUuidFromLinkNext(result1); - sleepOneClick(); + sleepAtLeast(11L); Bundle result2 = ourClient .search() .forResource("Organization") .returnBundle(Bundle.class) .execute(); final String uuid2 = toSearchUuidFromLinkNext(result2); - assertNotEquals(uuid1, uuid2); // TODO: DM 2019-09-24 - This assertion fails intermittently. + assertNotEquals(uuid1, uuid2); } - /* - * Now try one but mark it with an expiry time - * in the future - */ { myDaoConfig.setReuseCachedSearchResultsForMillis(1000L); Bundle result1 = ourClient @@ -3946,7 +3940,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .returnBundle(Bundle.class) .execute(); - // Expiry doesn't affect reusablility final String uuid2 = toSearchUuidFromLinkNext(result2); assertEquals(uuid1, uuid2); From 9b4671c05e9acc124f1e8c14e90619b574b4df8d Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 16:52:08 -0400 Subject: [PATCH 09/23] Added a bunch of tests for ancestor/descendant filters. --- .../jpa/term/TerminologySvcImplDstu3Test.java | 621 ++++++++++++++++-- 1 file changed, 577 insertions(+), 44 deletions(-) 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 3dd95a3e799..b38e178ac22 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 @@ -19,7 +19,9 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.ValueSet; import org.junit.After; import org.junit.AfterClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.TransactionStatus; @@ -38,6 +40,9 @@ import static org.junit.Assert.*; public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplDstu3Test.class); + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + private static final String CS_URL = "http://example.com/my_code_system"; private static final String CS_URL_2 = "http://example.com/my_code_system2"; @@ -306,7 +311,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { outcome = myTermSvc.expandValueSet(vs); codes = toCodesContains(outcome.getExpansion().getContains()); assertThat(codes, empty()); - } @Test @@ -485,12 +489,10 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { .setProperty("copyright") .setOp(ValueSet.FilterOperator.ISA) .setValue("LOINC"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Don't know how to handle op=ISA on property copyright", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Don't know how to handle op=ISA on property copyright"); + myTermSvc.expandValueSet(vs); } @Test @@ -504,18 +506,16 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { // Include vs = new ValueSet(); include = vs.getCompose().addInclude(); - include.setSystem(LOINC_URI); + include.setSystem(CS_URL); include .addFilter() .setProperty("copyright") .setOp(ValueSet.FilterOperator.EQUAL) .setValue("LOINC"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Invalid filter, property copyright is LOINC-specific and cannot be used with system: http://example.com/my_code_system", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Invalid filter, property copyright is LOINC-specific and cannot be used with system: http://example.com/my_code_system"); + myTermSvc.expandValueSet(vs); } @Test @@ -534,12 +534,281 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { .setProperty("copyright") .setOp(ValueSet.FilterOperator.EQUAL) .setValue("bogus"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Don't know how to handle value=bogus on property copyright", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Don't know how to handle value=bogus on property copyright"); + myTermSvc.expandValueSet(vs); + } + +/* + FIXME: DM 2019-09-25 - Remove this comment once ancestor/descendant filters are working. + code1 = 50015-7 + code2 = 43343-3 + code3 = 43343-4 + code4 = 47239-9 + + code1 + -code2 + -code3 + -code4 + + testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndEqual + exclude + ancestor = code1->[code1] + ancestor = code2->[code1,code2] + ancestor = code3->[code1,code2,code3,code4] + ancestor = code4->[code1,code2,code3,code4] + + testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndIn + exclude + ancestor in code1,code2,code3,code4->[code1] + + testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndEqual + include + ancestor = code1->[code2,code3,code4] + ancestor = code2->[code3,code4] + ancestor = code3->[] + ancestor = code4->[] + + testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndIn + include + ancestor in code1,code2,code3,code4->[code2,code3,code4] + + testExpandValueSetPropertyFilterLoincAncestorWithUnsupportedOp + testExpandValueSetPropertyFilterLoincAncestorWithUnsupportedSystem + */ + + @Test + public void testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndEqual() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent exclude; + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("50015-7"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7")); + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-3"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3")); + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-4"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9")); + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndIn() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent exclude; + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.IN) + .setValue("50015-7,43343-3,43343-4,47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndEqual() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("50015-7"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9")); + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-3"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-4", "47239-9")); + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-4"); + outcome = myTermSvc.expandValueSet(vs); + assertEquals(0, outcome.getExpansion().getContains().size()); + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("47239-9"); + outcome = myTermSvc.expandValueSet(vs); + assertEquals(0, outcome.getExpansion().getContains().size()); + } + + @Test + public void testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndIn() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.IN) + .setValue("50015-7,43343-3,43343-4,47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincAncestorWithUnsupportedOp() { + createLoincSystemWithSomeCodes(); + + ValueSet vs; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.ISA) + .setValue("50015-7"); + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Don't know how to handle op=ISA on property ancestor"); + myTermSvc.expandValueSet(vs); + } + + @Test + public void testExpandValueSetPropertyFilterLoincAncestorWithUnsupportedSystem() { + createCodeSystem(); + createLoincSystemWithSomeCodes(); + + ValueSet vs; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(CS_URL); + include + .addFilter() + .setProperty("ancestor") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("50015-7"); + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Invalid filter, property ancestor is LOINC-specific and cannot be used with system: http://example.com/my_code_system"); + myTermSvc.expandValueSet(vs); } @Test @@ -747,12 +1016,10 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { .setProperty("child") .setOp(ValueSet.FilterOperator.ISA) .setValue("50015-7"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Don't know how to handle op=ISA on property child", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Don't know how to handle op=ISA on property child"); + myTermSvc.expandValueSet(vs); } @Test @@ -772,12 +1039,282 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { .setProperty("child") .setOp(ValueSet.FilterOperator.EQUAL) .setValue("50015-7"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Invalid filter, property child is LOINC-specific and cannot be used with system: http://example.com/my_code_system", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Invalid filter, property child is LOINC-specific and cannot be used with system: http://example.com/my_code_system"); + myTermSvc.expandValueSet(vs); + } + +/* + FIXME: DM 2019-09-25 - Remove this comment once ancestor/descendant filters are working. + code1 = 50015-7 + code2 = 43343-3 + code3 = 43343-4 + code4 = 47239-9 + + code1 + -code2 + -code3 + -code4 + + testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndEqual + exclude + descendant = code1->[code1,code2,code3,code4] + descendant = code2->[code2,code3,code4] + descendant = code3->[code3,code4] + descendant = code4->[code3,code4] + + testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndIn + exclude + descendant in code1,code2,code3,code4->[code3,code4] + + testExpandValueSetPropertyFilterLoincDescendantWithIncludeAndEqual + include + descendant = code1->[] + descendant = code2->[code1] + descendant = code3->[code1,code2] + descendant = code4->[code1,code2] + + testExpandValueSetPropertyFilterLoincDescendantWithIncludeAndIn + include + descendant in code1,code2,code3,code4->[code1,code2] + + testExpandValueSetPropertyFilterLoincDescendantWithUnsupportedOp + testExpandValueSetPropertyFilterLoincDescendantWithUnsupportedSystem +*/ + + @Test + public void testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndEqual() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent exclude; + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("50015-7"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9")); + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-3"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-3", "43343-4", "47239-9")); + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-4"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-4", "47239-9")); + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-4", "47239-9")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndIn() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent exclude; + + // Include + vs = new ValueSet(); + vs.getCompose() + .addInclude() + .setSystem(LOINC_URI); + // Exclude + exclude = vs.getCompose().addExclude(); + exclude.setSystem(LOINC_URI); + exclude + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.IN) + .setValue("50015-7,43343-3,43343-4,47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("43343-4", "47239-9")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincDescendantWithIncludeAndEqual() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("50015-7"); + outcome = myTermSvc.expandValueSet(vs); + assertEquals(0, outcome.getExpansion().getContains().size()); + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-3"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7")); + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("43343-4"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3")); + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincDescendantWithIncludeAndIn() { + createLoincSystemWithSomeCodes(); + + List codes; + ValueSet vs; + ValueSet outcome; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.IN) + .setValue("50015-7,43343-3,43343-4,47239-9"); + outcome = myTermSvc.expandValueSet(vs); + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("50015-7", "43343-3")); + } + + @Test + public void testExpandValueSetPropertyFilterLoincDescendantWithUnsupportedOp() { + createLoincSystemWithSomeCodes(); + + ValueSet vs; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(LOINC_URI); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.ISA) + .setValue("50015-7"); + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Don't know how to handle op=ISA on property descendant"); + myTermSvc.expandValueSet(vs); + } + + @Test + public void testExpandValueSetPropertyFilterLoincDescendantWithUnsupportedSystem() { + createCodeSystem(); + createLoincSystemWithSomeCodes(); + + ValueSet vs; + ValueSet.ConceptSetComponent include; + + // Include + vs = new ValueSet(); + include = vs.getCompose().addInclude(); + include.setSystem(CS_URL); + include + .addFilter() + .setProperty("descendant") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("50015-7"); + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Invalid filter, property descendant is LOINC-specific and cannot be used with system: http://example.com/my_code_system"); + myTermSvc.expandValueSet(vs); } @Test @@ -984,12 +1521,10 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { .setProperty("parent") .setOp(ValueSet.FilterOperator.ISA) .setValue("50015-7"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Don't know how to handle op=ISA on property parent", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Don't know how to handle op=ISA on property parent"); + myTermSvc.expandValueSet(vs); } @Test @@ -1009,12 +1544,10 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { .setProperty("parent") .setOp(ValueSet.FilterOperator.EQUAL) .setValue("50015-7"); - try { - myTermSvc.expandValueSet(vs); - } catch (InvalidRequestException e) { - assertEquals(400, e.getStatusCode()); - assertEquals("Invalid filter, property parent is LOINC-specific and cannot be used with system: http://example.com/my_code_system", e.getMessage()); - } + + expectedException.expect(InvalidRequestException.class); + expectedException.expectMessage("Invalid filter, property parent is LOINC-specific and cannot be used with system: http://example.com/my_code_system"); + myTermSvc.expandValueSet(vs); } @Test From 7c5cf9f1f44f0e4478b3954c467ae15dd25f270c Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 17:33:45 -0400 Subject: [PATCH 10/23] Incremental progress for ancestor/descendant filters. --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) 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 a88c8c4a3bd..017fbb6a329 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 @@ -872,19 +872,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, break; case "parent": case "child": - if (isCodeSystemLoinc(theSystem)) { - handleFilterLoincParentChild(theQb, theBool, theFilter); - } else { - throw new InvalidRequestException("Invalid filter, property " + theFilter.getProperty() + " is LOINC-specific and cannot be used with system: " + theSystem); - } + isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + handleFilterLoincParentChild(theQb, theBool, theFilter); + break; + case "ancestor": + case "descendant": + isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + handleFilterLoincAncestorDescendant(theQb, theBool, theFilter); break; - // FIXME: DM 2019-09-24 - Need to implement LOINC-specific filters for "ancestor" and "descendant". case "copyright": - if (isCodeSystemLoinc(theSystem)) { - handleFilterLoincCopyright(theQb, theBool, theFilter); - } else { - throw new InvalidRequestException("Invalid filter, property " + theFilter.getProperty() + " is LOINC-specific and cannot be used with system: " + theSystem); - } + isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + handleFilterLoincCopyright(theQb, theBool, theFilter); break; default: handleFilterRegex(theBool, theFilter); @@ -892,6 +890,13 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } } + private boolean isCodeSystemLoingOrThrowInvalidRequestException(String theSystem, String theProperty) { + if (!isCodeSystemLoinc(theSystem)) { + throw new InvalidRequestException("Invalid filter, property " + theProperty + " is LOINC-specific and cannot be used with system: " + theSystem); + } + return true; + } + private boolean isCodeSystemLoinc(String theSystem) { return IHapiTerminologyLoaderSvc.LOINC_URI.equals(theSystem); } @@ -965,6 +970,30 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, return new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + theProperty, theValue); } + private void handleFilterLoincAncestorDescendant(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { + addLoincFilterAncestorDescendantEqual(theBool, theFilter.getProperty(), theFilter.getValue()); + } else if (theFilter.getOp() == ValueSet.FilterOperator.IN) { + addLoincFilterAncestorDescendantIn(theBool, theFilter); + } else { + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + } + } + + private void addLoincFilterAncestorDescendantEqual(BooleanJunction theBool, String theProperty, String theValue) { + logFilteringValueOnProperty(theValue, theProperty); + // FIXME: DM 2019-09-25 - Filter with op=EQUAL on ancestor/descendant + } + + private void addLoincFilterAncestorDescendantIn(BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + String[] values = theFilter.getValue().split(","); + List terms = new ArrayList<>(); + for (String value : values) { + logFilteringValueOnProperty(value, theFilter.getProperty()); + // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor/descendant + } + } + private void handleFilterLoincCopyright(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { From 4f7db37d8e9ed9b6800c790e063c9afcbb468ecb Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 21:36:36 -0400 Subject: [PATCH 11/23] Additional progress for ancestor/descendant filters; only ancestor/in remains. --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 97 +++++++++++++++---- .../jpa/term/TerminologySvcImplDstu3Test.java | 4 + 2 files changed, 82 insertions(+), 19 deletions(-) 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 017fbb6a329..57220a5ab49 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 @@ -876,9 +876,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, handleFilterLoincParentChild(theQb, theBool, theFilter); break; case "ancestor": + isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + handleFilterLoincAncestor(theSystem, theQb, theBool, theFilter); + break; case "descendant": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestorDescendant(theQb, theBool, theFilter); + handleFilterLoincDescendant(theSystem, theQb, theBool, theFilter); break; case "copyright": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); @@ -942,12 +945,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } private void handleFilterLoincParentChild(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { - addLoincFilterParentChildEqual(theBool, theFilter.getProperty(), theFilter.getValue()); - } else if (theFilter.getOp() == ValueSet.FilterOperator.IN) { - addLoincFilterParentChildIn(theBool, theFilter); - } else { - throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + switch (theFilter.getOp()) { + case EQUAL: + addLoincFilterParentChildEqual(theBool, theFilter.getProperty(), theFilter.getValue()); + break; + case IN: + addLoincFilterParentChildIn(theBool, theFilter); + break; + default: + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } @@ -970,28 +976,81 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, return new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + theProperty, theValue); } - private void handleFilterLoincAncestorDescendant(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { - addLoincFilterAncestorDescendantEqual(theBool, theFilter.getProperty(), theFilter.getValue()); - } else if (theFilter.getOp() == ValueSet.FilterOperator.IN) { - addLoincFilterAncestorDescendantIn(theBool, theFilter); - } else { - throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + private void handleFilterLoincAncestor(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + switch (theFilter.getOp()) { + case EQUAL: + addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter); + break; + case IN: + addLoincFilterAncestorIn(theSystem, theQb, theBool, theFilter); + break; + default: + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterAncestorDescendantEqual(BooleanJunction theBool, String theProperty, String theValue) { - logFilteringValueOnProperty(theValue, theProperty); - // FIXME: DM 2019-09-25 - Filter with op=EQUAL on ancestor/descendant + private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + TermConcept code = findCode(theSystem, theFilter.getValue()) + .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theFilter.getValue())); + + logFilteringValueOnProperty(theFilter.getValue(), theFilter.getProperty()); + theBool.must(theQb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); } - private void addLoincFilterAncestorDescendantIn(BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { logFilteringValueOnProperty(value, theFilter.getProperty()); - // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor/descendant + // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor } + theBool.must(new TermsQuery(terms)); + } + + private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + switch (theFilter.getOp()) { + case EQUAL: + addLoincFilterDescendantEqual(theSystem, theBool, theFilter); + break; + case IN: + addLoincFilterDescendantIn(theSystem, theQb, theBool, theFilter); + break; + default: + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + } + } + + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); + } + + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getDescendantTerms(theSystem, theProperty, theValue); + theBool.must(new TermsQuery(terms)); + } + + private void addLoincFilterDescendantIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + String[] values = theFilter.getValue().split(","); + List terms = new ArrayList<>(); + for (String value : values) { + terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value)); + } + theBool.must(new TermsQuery(terms)); + } + + private List getDescendantTerms(String theSystem, String theProperty, String theValue) { + List retVal = new ArrayList<>(); + + TermConcept code = findCode(theSystem, theValue) + .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theValue)); + + String[] parentPids = code.getParentPidsAsString().split(" "); + for (String parentPid : parentPids) { + retVal.add(new Term("myId", parentPid)); + } + logFilteringValueOnProperty(theValue, theProperty); + + return retVal; } private void handleFilterLoincCopyright(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { 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 b38e178ac22..35b92796f17 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 @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; 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.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -150,6 +151,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { LOINC_URI, code2.getCode(), code2.getDisplay()); + code1.addChild(code2, TermConceptParentChildLink.RelationshipTypeEnum.ISA); cs.getConcepts().add(code1); code2.addPropertyString("SYSTEM", "Ser"); @@ -164,11 +166,13 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { LOINC_URI, code3.getCode(), code3.getDisplay()); + code2.addChild(code3, TermConceptParentChildLink.RelationshipTypeEnum.ISA); code2.addPropertyCoding( "child", LOINC_URI, code4.getCode(), code4.getDisplay()); + code2.addChild(code4, TermConceptParentChildLink.RelationshipTypeEnum.ISA); cs.getConcepts().add(code2); code3.addPropertyString("SYSTEM", "Ser"); From e098cea07f0f576689f1e1589a9c255541936b22 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Thu, 26 Sep 2019 10:39:55 -0400 Subject: [PATCH 12/23] Throw UnsupportedOperationException for LOINC ancestor filter with IN operator; ignore tests. See #1512 in GitHub. --- .../fhir/jpa/term/BaseHapiTerminologySvcImpl.java | 13 ++++++------- .../fhir/jpa/term/TerminologySvcImplDstu3Test.java | 7 +++---- 2 files changed, 9 insertions(+), 11 deletions(-) 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 57220a5ab49..9c99debc668 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 @@ -998,13 +998,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - String[] values = theFilter.getValue().split(","); - List terms = new ArrayList<>(); - for (String value : values) { - logFilteringValueOnProperty(value, theFilter.getProperty()); - // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor - } - theBool.must(new TermsQuery(terms)); + throw new UnsupportedOperationException(); + // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor; see #1512 in GitHub. +// String[] values = theFilter.getValue().split(","); +// for (String value : values) { +// logFilteringValueOnProperty(value, theFilter.getProperty()); +// } } private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { 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 35b92796f17..2c146d42512 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 @@ -18,10 +18,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.ValueSet; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Rule; -import org.junit.Test; +import org.junit.*; import org.junit.rules.ExpectedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -660,6 +657,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9")); } + @Ignore("Not yet implemented; see #1512 in GitHub.") @Test public void testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndIn() { createLoincSystemWithSomeCodes(); @@ -747,6 +745,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { assertEquals(0, outcome.getExpansion().getContains().size()); } + @Ignore("Not yet implemented; see #1512 in GitHub.") @Test public void testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndIn() { createLoincSystemWithSomeCodes(); From efae77a3302a47a7336be2d0004cac9838eb3818 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Thu, 26 Sep 2019 11:36:55 -0400 Subject: [PATCH 13/23] Added changelog entries. --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 1 + src/changes/changes.xml | 44 ++++++++++++++++--- 2 files changed, 38 insertions(+), 7 deletions(-) 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 9c99debc668..d4b8e0f40e9 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 @@ -1000,6 +1000,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { throw new UnsupportedOperationException(); // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor; see #1512 in GitHub. + // FIXME: DM 2019-09-26 - Once implemented, fix changelog entry for #1454 in changes.xml // String[] values = theFilter.getValue().split(","); // for (String value : values) { // logFilteringValueOnProperty(value, theFilter.getProperty()); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index bb0164e3909..7eac542c8e7 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -42,7 +42,7 @@ Improovement: + Improvement: A significant performance improvement was made to the parsers (particularly the Json Parser) when serializing resources. This work yields improvements of 20-50% in raw encode speed when encoding large resources. Thanks to David Maplesden for the pull request! @@ -90,11 +90,11 @@ The informational message returned in an OperationOutcome when a delete failed due to cascades not being enabled contained an incorrect example. This has been corrected. - - In some cases, deleting a CodeSystem resource would fail because the underlying - codes were not correctly deleted from the terminology service tables. This is - fixed. - + + In some cases, deleting a CodeSystem resource would fail because the underlying + codes were not correctly deleted from the terminology service tables. This is + fixed. + Two foreign keys have been dropped from the HFJ_SEARCH_RESULT table used by the FHIR search query cache. These constraints did not add value and caused unneccessary contention when used under high load. @@ -114,7 +114,7 @@ leaving the Bundle.entry.request.method blank in DSTU3 transactions and setting the request payload as a Binary resource containing a valid patch. - + The HAPI FHIR CLI server now uses H2 as its database platform instead of Derby. Note that this means that data in any existing installations will need to be re-uploaded to the new database platform. @@ -218,6 +218,36 @@ handled by method implementations that did not have any @IncludeParam]]> defined. This is now corrected. Thanks to Tuomo Ala-Vannesluoma for reporting and providing a test case! + + The ValueSet operation $expand]]> has been optimized for large ValueSets. ValueSets are + now persistence-backed by the terminology tables, which are populated by a scheduled pre-expansion process. + A ValueSet previously stored in an existing FHIR repository will need to be re-created or updated to make + it a candidate for pre-expansion. ValueSets that have yet to be pre-expanded will continue to be expanded + in-memory. + + + The ValueSet operation $validate-code]]> has been optimized for large ValueSets. + Codes in ValueSets that have yet to be pre-expanded will continue to be validated in-memory. + + + LOINC filenames for terminology upload are now configurable using the + loincupload.properties]]> file. + + + Support for the LOINC EXTERNAL_COPYRIGHT_NOTICE]]> property and + copyright]]> filter has been added. + + + Support for the LOINC parent]]> and child]]> filters has been + added. Both filters can be used with either of the =]]> or + in]]> operators. + + + Support for the LOINC ancestor]]> and descendant]]> filters has + been added. The descendant]]> filter can be used with either of the + =]]> or in]]> operators. At present, the + ancestor]]> filter can only be used with the =]]> operator. + From b6400764c4d7eb8d94560f499e1f0430d64f70bd Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Thu, 26 Sep 2019 11:42:26 -0400 Subject: [PATCH 14/23] Removed scratch notes. --- .../jpa/term/TerminologySvcImplDstu3Test.java | 76 ------------------- 1 file changed, 76 deletions(-) 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 2c146d42512..e2153aa4e63 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 @@ -541,44 +541,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { myTermSvc.expandValueSet(vs); } -/* - FIXME: DM 2019-09-25 - Remove this comment once ancestor/descendant filters are working. - code1 = 50015-7 - code2 = 43343-3 - code3 = 43343-4 - code4 = 47239-9 - - code1 - -code2 - -code3 - -code4 - - testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndEqual - exclude - ancestor = code1->[code1] - ancestor = code2->[code1,code2] - ancestor = code3->[code1,code2,code3,code4] - ancestor = code4->[code1,code2,code3,code4] - - testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndIn - exclude - ancestor in code1,code2,code3,code4->[code1] - - testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndEqual - include - ancestor = code1->[code2,code3,code4] - ancestor = code2->[code3,code4] - ancestor = code3->[] - ancestor = code4->[] - - testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndIn - include - ancestor in code1,code2,code3,code4->[code2,code3,code4] - - testExpandValueSetPropertyFilterLoincAncestorWithUnsupportedOp - testExpandValueSetPropertyFilterLoincAncestorWithUnsupportedSystem - */ - @Test public void testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndEqual() { createLoincSystemWithSomeCodes(); @@ -1048,44 +1010,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { myTermSvc.expandValueSet(vs); } -/* - FIXME: DM 2019-09-25 - Remove this comment once ancestor/descendant filters are working. - code1 = 50015-7 - code2 = 43343-3 - code3 = 43343-4 - code4 = 47239-9 - - code1 - -code2 - -code3 - -code4 - - testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndEqual - exclude - descendant = code1->[code1,code2,code3,code4] - descendant = code2->[code2,code3,code4] - descendant = code3->[code3,code4] - descendant = code4->[code3,code4] - - testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndIn - exclude - descendant in code1,code2,code3,code4->[code3,code4] - - testExpandValueSetPropertyFilterLoincDescendantWithIncludeAndEqual - include - descendant = code1->[] - descendant = code2->[code1] - descendant = code3->[code1,code2] - descendant = code4->[code1,code2] - - testExpandValueSetPropertyFilterLoincDescendantWithIncludeAndIn - include - descendant in code1,code2,code3,code4->[code1,code2] - - testExpandValueSetPropertyFilterLoincDescendantWithUnsupportedOp - testExpandValueSetPropertyFilterLoincDescendantWithUnsupportedSystem -*/ - @Test public void testExpandValueSetPropertyFilterLoincDescendantWithExcludeAndEqual() { createLoincSystemWithSomeCodes(); From 5f17fb30b4ae62c59af8cb6068b233132bc7fe36 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Thu, 26 Sep 2019 20:15:55 -0400 Subject: [PATCH 15/23] Address review comments. --- .../jpa/config/dstu3/BaseDstu3Config.java | 7 +++++- .../uhn/fhir/jpa/config/r4/BaseR4Config.java | 11 ++++++---- .../uhn/fhir/jpa/config/r5/BaseR5Config.java | 6 +++++ .../java/ca/uhn/fhir/jpa/dao/DaoConfig.java | 22 +++++++++++++++++++ .../dstu3/FhirResourceDaoValueSetDstu3.java | 15 ++++++++++--- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 14 +++++++++--- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 14 +++++++++--- .../JpaValidationSupportChainDstu3.java | 3 ++- .../JpaValidationSupportChainR4.java | 10 ++++----- .../JpaValidationSupportChainR5.java | 3 ++- 10 files changed, 84 insertions(+), 21 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java index 1f446d1d52f..95499fcb207 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java @@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3; import ca.uhn.fhir.jpa.provider.GraphQLProvider; -import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryDstu3; @@ -21,6 +20,7 @@ import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3; import ca.uhn.fhir.validation.IValidatorModule; import org.apache.commons.lang3.time.DateUtils; +import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.CachingValidationSupport; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; @@ -99,6 +99,11 @@ public class BaseDstu3Config extends BaseConfig { return val; } + @Bean + public DefaultProfileValidationSupport defaultProfileValidationSupport() { + return new DefaultProfileValidationSupport(); + } + @Bean public JpaValidationSupportChainDstu3 jpaValidationSupportChain() { return new JpaValidationSupportChainDstu3(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java index 2bb9f1e81d3..9c5cfd5d17b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java @@ -8,7 +8,7 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4; -import ca.uhn.fhir.jpa.graphql.JpaStorageServices; +import ca.uhn.fhir.jpa.provider.GraphQLProvider; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR4; @@ -20,14 +20,12 @@ import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4; import ca.uhn.fhir.validation.IValidatorModule; import org.apache.commons.lang3.time.DateUtils; +import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; -import ca.uhn.fhir.jpa.provider.GraphQLProvider; import org.hl7.fhir.r4.hapi.validation.CachingValidationSupport; import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.utils.GraphQLEngine; import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -102,6 +100,11 @@ public class BaseR4Config extends BaseConfig { return val; } + @Bean + public DefaultProfileValidationSupport defaultProfileValidationSupport() { + return new DefaultProfileValidationSupport(); + } + @Bean public JpaValidationSupportChainR4 jpaValidationSupportChain() { return new JpaValidationSupportChainR4(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/BaseR5Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/BaseR5Config.java index 0edd674017f..3bce313f662 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/BaseR5Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r5/BaseR5Config.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR5; import ca.uhn.fhir.validation.IValidatorModule; import org.apache.commons.lang3.time.DateUtils; +import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.r5.hapi.ctx.IValidationSupport; import org.hl7.fhir.r5.hapi.validation.CachingValidationSupport; import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator; @@ -99,6 +100,11 @@ public class BaseR5Config extends BaseConfig { return val; } + @Bean + public DefaultProfileValidationSupport defaultProfileValidationSupport() { + return new DefaultProfileValidationSupport(); + } + @Bean public JpaValidationSupportChainR5 jpaValidationSupportChain() { return new JpaValidationSupportChainR5(); 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 fa1ac8383d7..1b8c00a7f3e 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 @@ -150,18 +150,26 @@ public class DaoConfig { private StoreMetaSourceInformationEnum myStoreMetaSourceInformation = StoreMetaSourceInformationEnum.SOURCE_URI_AND_REQUEST_ID; /** * Do not change default of {@code true}! + * + * @since 4.1.0 */ private boolean myPreExpandValueSets = true; /** * Do not change default of {@code 0}! + * + * @since 4.1.0 */ private int myPreExpandValueSetsDefaultOffset = 0; /** * Do not change default of {@code 1000}! + * + * @since 4.1.0 */ private int myPreExpandValueSetsDefaultCount = 1000; /** * Do not change default of {@code 1000}! + * + * @since 4.1.0 */ private int myPreExpandValueSetsMaxCount = 1000; @@ -1699,6 +1707,8 @@ public class DaoConfig { *

* The default value for this setting is {@code true}. *

+ * + * @since 4.1.0 */ public boolean isPreExpandValueSets() { return myPreExpandValueSets; @@ -1712,6 +1722,8 @@ public class DaoConfig { *

* The default value for this setting is {@code true}. *

+ * + * @since 4.1.0 */ public void setPreExpandValueSets(boolean thePreExpandValueSets) { myPreExpandValueSets = thePreExpandValueSets; @@ -1725,6 +1737,8 @@ public class DaoConfig { *

* The default value for this setting is {@code 0}. *

+ * + * @since 4.1.0 */ public int getPreExpandValueSetsDefaultOffset() { return myPreExpandValueSetsDefaultOffset; @@ -1738,6 +1752,8 @@ public class DaoConfig { *

* The default value for this setting is {@code 1000}. *

+ * + * @since 4.1.0 */ public int getPreExpandValueSetsDefaultCount() { return myPreExpandValueSetsDefaultCount; @@ -1755,6 +1771,8 @@ public class DaoConfig { *

* The default value for this setting is {@code 1000}. *

+ * + * @since 4.1.0 */ public void setPreExpandValueSetsDefaultCount(int thePreExpandValueSetsDefaultCount) { myPreExpandValueSetsDefaultCount = Math.min(thePreExpandValueSetsDefaultCount, getPreExpandValueSetsMaxCount()); @@ -1768,6 +1786,8 @@ public class DaoConfig { *

* The default value for this setting is {@code 1000}. *

+ * + * @since 4.1.0 */ public int getPreExpandValueSetsMaxCount() { return myPreExpandValueSetsMaxCount; @@ -1786,6 +1806,8 @@ public class DaoConfig { *

* The default value for this setting is {@code 1000}. *

+ * + * @since 4.1.0 */ public void setPreExpandValueSetsMaxCount(int thePreExpandValueSetsMaxCount) { myPreExpandValueSetsMaxCount = thePreExpandValueSetsMaxCount; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index b133d7c90d7..891c243e76a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ElementUtil; import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.model.*; @@ -63,9 +64,13 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 @Autowired private IHapiTerminologySvc myHapiTerminologySvc; + @Autowired + private DefaultProfileValidationSupport myDefaultProfileValidationSupport; + @Autowired @Qualifier("myJpaValidationSupportChainDstu3") private IValidationSupport myValidationSupport; + @Autowired private IFhirResourceDaoCodeSystem myCodeSystemDao; @@ -314,11 +319,15 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3 if (theId != null) { vs = read(theId, theRequestDetails); } else if (haveIdentifierParam) { - vs = myValidationSupport.fetchResource(getContext(), ValueSet.class, theValueSetIdentifier.getValue()); + vs = myDefaultProfileValidationSupport.fetchValueSet(getContext(), theValueSetIdentifier.getValue()); if (vs == null) { - throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); + vs = myValidationSupport.fetchValueSet(getContext(), theValueSetIdentifier.getValue()); + if (vs == null) { + throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); + } + } else { + isBuiltInValueSet = true; } - isBuiltInValueSet = true; } else { if (theCode == null || theCode.isEmpty()) { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java index 2f28f4dad1a..b881f2a611b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoValueSetR4.java @@ -33,6 +33,7 @@ import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; import org.hl7.fhir.r4.model.*; @@ -57,6 +58,9 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple @Autowired private IHapiTerminologySvc myHapiTerminologySvc; + @Autowired + private DefaultProfileValidationSupport myDefaultProfileValidationSupport; + @Autowired @Qualifier("myJpaValidationSupportChainR4") private IValidationSupport myValidationSupport; @@ -310,11 +314,15 @@ public class FhirResourceDaoValueSetR4 extends FhirResourceDaoR4 imple if (theId != null) { vs = read(theId, theRequestDetails); } else if (haveIdentifierParam) { - vs = myValidationSupport.fetchResource(getContext(), ValueSet.class, theValueSetIdentifier.getValue()); + vs = myDefaultProfileValidationSupport.fetchValueSet(getContext(), theValueSetIdentifier.getValue()); if (vs == null) { - throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); + vs = myValidationSupport.fetchValueSet(getContext(), theValueSetIdentifier.getValue()); + if (vs == null) { + throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); + } + } else { + isBuiltInValueSet = true; } - isBuiltInValueSet = true; } else { if (theCode == null || theCode.isEmpty()) { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 549a7b6aa0b..320d584a177 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -33,6 +33,7 @@ import org.apache.commons.codec.binary.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r5.hapi.ctx.IValidationSupport; import org.hl7.fhir.r5.model.*; @@ -57,6 +58,9 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5 imple @Autowired private IHapiTerminologySvc myHapiTerminologySvc; + @Autowired + private DefaultProfileValidationSupport myDefaultProfileValidationSupport; + @Autowired @Qualifier("myJpaValidationSupportChainR5") private IValidationSupport myValidationSupport; @@ -316,11 +320,15 @@ public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5 imple if (theId != null) { vs = read(theId, theRequestDetails); } else if (haveIdentifierParam) { - vs = myValidationSupport.fetchResource(getContext(), ValueSet.class, theValueSetIdentifier.getValue()); + vs = myDefaultProfileValidationSupport.fetchValueSet(getContext(), theValueSetIdentifier.getValue()); if (vs == null) { - throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); + vs = myValidationSupport.fetchValueSet(getContext(), theValueSetIdentifier.getValue()); + if (vs == null) { + throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue()); + } + } else { + isBuiltInValueSet = true; } - isBuiltInValueSet = true; } else { if (theCode == null || theCode.isEmpty()) { throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate."); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java index 27f8239d779..312841e0f26 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainDstu3.java @@ -38,7 +38,8 @@ public class JpaValidationSupportChainDstu3 extends ValidationSupportChain { @Autowired @Qualifier("myJpaValidationSupportDstu3") public ca.uhn.fhir.jpa.dao.dstu3.IJpaValidationSupportDstu3 myJpaValidationSupportDstu3; - private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport(); + @Autowired + private DefaultProfileValidationSupport myDefaultProfileValidationSupport; @Autowired private IHapiTerminologySvcDstu3 myTerminologyService; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR4.java index d92babe1fd6..710650ac6b1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR4.java @@ -20,10 +20,8 @@ package ca.uhn.fhir.jpa.validation; * #L% */ -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport; import org.hl7.fhir.r4.hapi.validation.SnapshotGeneratingValidationSupport; @@ -32,11 +30,13 @@ import org.hl7.fhir.r4.model.StructureDefinition; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; public class JpaValidationSupportChainR4 extends ValidationSupportChain { - private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport(); + @Autowired + private DefaultProfileValidationSupport myDefaultProfileValidationSupport; @Autowired private FhirContext myFhirContext; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR5.java index 5e7ad25d99f..8101855809a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChainR5.java @@ -35,7 +35,8 @@ import javax.annotation.PreDestroy; public class JpaValidationSupportChainR5 extends ValidationSupportChain { - private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport(); + @Autowired + private DefaultProfileValidationSupport myDefaultProfileValidationSupport; @Autowired private FhirContext myFhirContext; From 0386448b3fdf93f904f247b2c1564be6cec6fca0 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Mon, 30 Sep 2019 09:21:32 -0400 Subject: [PATCH 16/23] Initial commit for work on ancestor filter with IN operator for LOINC. --- .../java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java | 1 + 1 file changed, 1 insertion(+) 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 d4b8e0f40e9..f1a8d5b49f3 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 @@ -999,6 +999,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { throw new UnsupportedOperationException(); + // FIXME: DM 2019-09-30 - Working on this presently. // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor; see #1512 in GitHub. // FIXME: DM 2019-09-26 - Once implemented, fix changelog entry for #1454 in changes.xml // String[] values = theFilter.getValue().split(","); From 557a8ccc66ba08cda232e9d4798275391a8b0317 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 30 Sep 2019 09:30:39 -0400 Subject: [PATCH 17/23] Add top level support for ElasticSearch (#1514) * ElasticSearch work * Add ElasticSearch properties builder * Clean up POM * Remove redundant project * Try to troubleshoot embedded elasticsearch * Another test attempt * Add credentials to elasticsearch config * Work on lastn * Address review comments * A couple of test fixes --- .../java/ca/uhn/fhir/rest/api/Constants.java | 4 + hapi-fhir-jpaserver-base/pom.xml | 11 + .../fhir/jpa/entity/ResourceSearchView.java | 4 - ...asticsearchHibernatePropertiesBuilder.java | 90 +++++++ .../ElasticsearchMappingProvider.java | 10 +- .../config/TestDstu3WithoutLuceneConfig.java | 46 ---- .../ca/uhn/fhir/jpa/config/TestR4Config.java | 3 +- .../config/TestR4ConfigWithElasticSearch.java | 74 ++++++ ...va => TestR4WithLuceneDisabledConfig.java} | 5 +- ...eDaoDstu3SearchWithLuceneDisabledTest.java | 234 ------------------ .../dao/r4/FhirResourceDaoR4SearchFtTest.java | 96 ++++--- .../FhirResourceDaoR4SearchOptimizedTest.java | 7 +- ...ourceDaoR4SearchWithElasticSearchTest.java | 230 +++++++++++++++++ ...urceDaoR4SearchWithLuceneDisabledTest.java | 6 +- .../src/test/resources/logback-test.xml | 7 +- hapi-fhir-jpaserver-elasticsearch/pom.xml | 23 -- .../demo/elasticsearch/FhirServerConfig.java | 2 +- .../server/method/OperationParameter.java | 70 +++++- .../server/provider/BaseLastNProvider.java | 43 ++++ .../fhir/rest/server/BaseR4ServerTest.java | 44 ++++ .../fhir/rest/server/LastNProviderTest.java | 64 +++++ .../server/ServerMethodSelectionR4Test.java | 37 +-- pom.xml | 25 +- src/changes/changes.xml | 8 +- 24 files changed, 706 insertions(+), 437 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchHibernatePropertiesBuilder.java rename {hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search => hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic}/ElasticsearchMappingProvider.java (87%) delete mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3WithoutLuceneConfig.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4ConfigWithElasticSearch.java rename hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/{TestR4WithoutLuceneConfig.java => TestR4WithLuceneDisabledConfig.java} (92%) delete mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchWithLuceneDisabledTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java delete mode 100644 hapi-fhir-jpaserver-elasticsearch/pom.xml create mode 100644 hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java create mode 100644 hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java create mode 100644 hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 907f04bca90..04eaae58453 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -237,6 +237,10 @@ public class Constants { public static final int STATUS_HTTP_202_ACCEPTED = 202; public static final String HEADER_X_PROGRESS = "X-Progress"; public static final String HEADER_RETRY_AFTER = "Retry-After"; + /** + * Operation name for the $lastn operation + */ + public static final String OPERATION_LASTN = "$lastn"; static { CHARSET_UTF8 = StandardCharsets.UTF_8; diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 69f60311de9..d7c8c3373db 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -483,6 +483,8 @@ org.glassfish javax.el + + org.hibernate hibernate-search-orm @@ -495,6 +497,10 @@ org.apache.lucene lucene-analyzers-phonetic + + org.hibernate + hibernate-search-elasticsearch + @@ -568,6 +574,11 @@ greenmail-spring test + + pl.allegro.tech + embedded-elasticsearch + 2.10.0 + com.github.ben-manes.caffeine diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java index 3d94ee48daf..2dbc8f881c0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java @@ -62,17 +62,13 @@ public class ResourceSearchView implements IBaseResourceEntity, Serializable { @Id @Column(name = "PID") private Long myId; - @Column(name = "RES_ID") private Long myResourceId; - @Column(name = "RES_TYPE", length = Constants.MAX_RESOURCE_NAME_LENGTH) private String myResourceType; - @Column(name = "RES_VERSION") @Enumerated(EnumType.STRING) private FhirVersionEnum myFhirVersion; - @Column(name = "RES_VER") private Long myResourceVersion; @Column(name = "PROV_REQUEST_ID", length = Constants.REQUEST_ID_LENGTH) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchHibernatePropertiesBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchHibernatePropertiesBuilder.java new file mode 100644 index 00000000000..d422687feae --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchHibernatePropertiesBuilder.java @@ -0,0 +1,90 @@ +package ca.uhn.fhir.jpa.search.elastic; + +import org.hibernate.search.cfg.Environment; +import org.hibernate.search.elasticsearch.cfg.ElasticsearchEnvironment; +import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus; +import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy; + +import java.util.Properties; + +/** + * This class is used to inject appropriate properties into a hibernate + * Properties object being used to create an entitymanager for a HAPI + * FHIR JPA server. + */ +public class ElasticsearchHibernatePropertiesBuilder { + + private ElasticsearchIndexStatus myRequiredIndexStatus = ElasticsearchIndexStatus.YELLOW; + private String myRestUrl; + private String myUsername; + private String myPassword; + private IndexSchemaManagementStrategy myIndexSchemaManagementStrategy = IndexSchemaManagementStrategy.CREATE; + private long myIndexManagementWaitTimeoutMillis = 10000L; + private boolean myDebugRefreshAfterWrite = false; + private boolean myDebugPrettyPrintJsonLog = false; + + public ElasticsearchHibernatePropertiesBuilder setUsername(String theUsername) { + myUsername = theUsername; + return this; + } + + public ElasticsearchHibernatePropertiesBuilder setPassword(String thePassword) { + myPassword = thePassword; + return this; + } + + public void apply(Properties theProperties) { + + // Don't use the Lucene properties as they conflict + theProperties.remove("hibernate.search.model_mapping"); + + // the below properties are used for ElasticSearch integration + theProperties.put("hibernate.search.default." + Environment.INDEX_MANAGER_IMPL_NAME, "elasticsearch"); + theProperties.put("hibernate.search." + ElasticsearchEnvironment.ANALYSIS_DEFINITION_PROVIDER, ElasticsearchMappingProvider.class.getName()); + + theProperties.put("hibernate.search.default.elasticsearch.host", myRestUrl); + theProperties.put("hibernate.search.default.elasticsearch.username", myUsername); + theProperties.put("hibernate.search.default.elasticsearch.password", myPassword); + + theProperties.put("hibernate.search.default." + ElasticsearchEnvironment.INDEX_SCHEMA_MANAGEMENT_STRATEGY, myIndexSchemaManagementStrategy.getExternalName()); + theProperties.put("hibernate.search.default." + ElasticsearchEnvironment.INDEX_MANAGEMENT_WAIT_TIMEOUT, Long.toString(myIndexManagementWaitTimeoutMillis)); + theProperties.put("hibernate.search.default." + ElasticsearchEnvironment.REQUIRED_INDEX_STATUS, myRequiredIndexStatus.getElasticsearchString()); + + // Only for unit tests + theProperties.put("hibernate.search.default." + ElasticsearchEnvironment.REFRESH_AFTER_WRITE, Boolean.toString(myDebugRefreshAfterWrite)); + theProperties.put("hibernate.search." + ElasticsearchEnvironment.LOG_JSON_PRETTY_PRINTING, Boolean.toString(myDebugPrettyPrintJsonLog)); + + } + + public ElasticsearchHibernatePropertiesBuilder setRequiredIndexStatus(ElasticsearchIndexStatus theRequiredIndexStatus) { + myRequiredIndexStatus = theRequiredIndexStatus; + return this; + } + + public ElasticsearchHibernatePropertiesBuilder setRestUrl(String theRestUrl) { + myRestUrl = theRestUrl; + return this; + } + + public ElasticsearchHibernatePropertiesBuilder setIndexSchemaManagementStrategy(IndexSchemaManagementStrategy theIndexSchemaManagementStrategy) { + myIndexSchemaManagementStrategy = theIndexSchemaManagementStrategy; + return this; + } + + public ElasticsearchHibernatePropertiesBuilder setIndexManagementWaitTimeoutMillis(long theIndexManagementWaitTimeoutMillis) { + myIndexManagementWaitTimeoutMillis = theIndexManagementWaitTimeoutMillis; + return this; + } + + public ElasticsearchHibernatePropertiesBuilder setDebugRefreshAfterWrite(boolean theDebugRefreshAfterWrite) { + myDebugRefreshAfterWrite = theDebugRefreshAfterWrite; + return this; + } + + public ElasticsearchHibernatePropertiesBuilder setDebugPrettyPrintJsonLog(boolean theDebugPrettyPrintJsonLog) { + myDebugPrettyPrintJsonLog = theDebugPrettyPrintJsonLog; + return this; + } + + +} diff --git a/hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search/ElasticsearchMappingProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchMappingProvider.java similarity index 87% rename from hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search/ElasticsearchMappingProvider.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchMappingProvider.java index 3e0d7495c75..f82209cbdb2 100644 --- a/hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search/ElasticsearchMappingProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/elastic/ElasticsearchMappingProvider.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.search; +package ca.uhn.fhir.jpa.search.elastic; /*- * #%L @@ -20,9 +20,8 @@ package ca.uhn.fhir.jpa.search; * #L% */ -import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory; -import org.hibernate.search.elasticsearch.analyzer.definition.ElasticsearchAnalysisDefinitionRegistryBuilder; import org.hibernate.search.elasticsearch.analyzer.definition.ElasticsearchAnalysisDefinitionProvider; +import org.hibernate.search.elasticsearch.analyzer.definition.ElasticsearchAnalysisDefinitionRegistryBuilder; public class ElasticsearchMappingProvider implements ElasticsearchAnalysisDefinitionProvider { @@ -39,10 +38,7 @@ public class ElasticsearchMappingProvider implements ElasticsearchAnalysisDefini builder.analyzer("autocompletePhoneticAnalyzer") .withTokenizer("standard") - .withTokenFilters("standard", "stop", "snowball_english", "phonetic_doublemetaphone"); - builder.tokenFilter("phonetic_doublemetaphone") - .type("phonetic") - .param("encoder", "double_metaphone"); + .withTokenFilters("standard", "stop", "snowball_english"); builder.tokenFilter("snowball_english").type("snowball").param("language", "English"); builder.analyzer("autocompleteNGramAnalyzer") diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3WithoutLuceneConfig.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3WithoutLuceneConfig.java deleted file mode 100644 index e8170a00138..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu3WithoutLuceneConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -package ca.uhn.fhir.jpa.config; - -import java.util.Properties; - -import org.hibernate.dialect.H2Dialect; -import org.hibernate.jpa.HibernatePersistenceProvider; -import org.springframework.beans.factory.annotation.Autowire; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl; -import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; - -@Configuration -@EnableTransactionManagement() -public class TestDstu3WithoutLuceneConfig extends TestDstu3Config { - - /** - * Disable fulltext searching - */ - @Override - public IFulltextSearchSvc searchDaoDstu3() { - return null; - } - - @Override - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); - retVal.setJpaProperties(jpaProperties()); - return retVal; - } - - private Properties jpaProperties() { - Properties extraProperties = new Properties(); - extraProperties.put("hibernate.format_sql", "false"); - extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); - extraProperties.put("hibernate.dialect", H2Dialect.class.getName()); - extraProperties.put("hibernate.search.autoregister_listeners", "false"); - return extraProperties; - } - -} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index 7caa7447e24..c7da23d9005 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -138,7 +138,8 @@ public class TestR4Config extends BaseJavaConfigR4 { return retVal; } - private Properties jpaProperties() { + @Bean + public Properties jpaProperties() { Properties extraProperties = new Properties(); extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4ConfigWithElasticSearch.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4ConfigWithElasticSearch.java new file mode 100644 index 00000000000..f04edb8baf8 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4ConfigWithElasticSearch.java @@ -0,0 +1,74 @@ +package ca.uhn.fhir.jpa.config; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder; +import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus; +import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic; +import pl.allegro.tech.embeddedelasticsearch.PopularProperties; + +import javax.annotation.PreDestroy; +import java.io.IOException; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Configuration +public class TestR4ConfigWithElasticSearch extends TestR4Config { + + private static final Logger ourLog = LoggerFactory.getLogger(TestR4ConfigWithElasticSearch.class); + private static final String ELASTIC_VERSION = "6.5.4"; + + @Override + @Bean + public Properties jpaProperties() { + Properties retVal = super.jpaProperties(); + + // Force elasticsearch to start first + int httpPort = embeddedElasticSearch().getHttpPort(); + ourLog.info("ElasticSearch started on port: {}", httpPort); + + new ElasticsearchHibernatePropertiesBuilder() + .setDebugRefreshAfterWrite(true) + .setDebugPrettyPrintJsonLog(true) + .setIndexSchemaManagementStrategy(IndexSchemaManagementStrategy.CREATE) + .setIndexManagementWaitTimeoutMillis(10000) + .setRequiredIndexStatus(ElasticsearchIndexStatus.YELLOW) + .setRestUrl("http://localhost:" + httpPort) + .setUsername("") + .setPassword("") + .apply(retVal); + + return retVal; + } + + @Bean + public EmbeddedElastic embeddedElasticSearch() { + EmbeddedElastic embeddedElastic = null; + try { + embeddedElastic = EmbeddedElastic.builder() + .withElasticVersion(ELASTIC_VERSION) + .withSetting(PopularProperties.TRANSPORT_TCP_PORT, 0) + .withSetting(PopularProperties.HTTP_PORT, 0) + .withSetting(PopularProperties.CLUSTER_NAME, UUID.randomUUID()) + .withStartTimeout(60, TimeUnit.SECONDS) + .build() + .start(); + } catch (IOException | InterruptedException e) { + throw new ConfigurationException(e); + } + + return embeddedElastic; + } + + + @PreDestroy + public void stop() { + embeddedElasticSearch().stop(); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4WithoutLuceneConfig.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4WithLuceneDisabledConfig.java similarity index 92% rename from hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4WithoutLuceneConfig.java rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4WithLuceneDisabledConfig.java index dedb0215b16..7ec227c1896 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4WithoutLuceneConfig.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4WithLuceneDisabledConfig.java @@ -15,7 +15,7 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; @Configuration @EnableTransactionManagement() -public class TestR4WithoutLuceneConfig extends TestR4Config { +public class TestR4WithLuceneDisabledConfig extends TestR4Config { /** * Disable fulltext searching @@ -34,7 +34,8 @@ public class TestR4WithoutLuceneConfig extends TestR4Config { return retVal; } - private Properties jpaProperties() { + @Override + public Properties jpaProperties() { Properties extraProperties = new Properties(); extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchWithLuceneDisabledTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchWithLuceneDisabledTest.java deleted file mode 100644 index aaa0ca40027..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchWithLuceneDisabledTest.java +++ /dev/null @@ -1,234 +0,0 @@ -package ca.uhn.fhir.jpa.dao.dstu3; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc; -import ca.uhn.fhir.jpa.config.TestDstu3WithoutLuceneConfig; -import ca.uhn.fhir.jpa.dao.*; -import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; -import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; -import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; -import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.util.TestUtil; -import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; -import org.hl7.fhir.dstu3.model.*; -import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.transaction.PlatformTransactionManager; - -import javax.persistence.EntityManager; - -import static org.junit.Assert.*; - -// @RunWith(SpringJUnit4ClassRunner.class) -// @ContextConfiguration(classes= {TestDstu3WithoutLuceneConfig.class}) -// @SuppressWarnings("unchecked") -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = {TestDstu3WithoutLuceneConfig.class}) -public class FhirResourceDaoDstu3SearchWithLuceneDisabledTest extends BaseJpaTest { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3SearchWithLuceneDisabledTest.class); - @Autowired - protected DaoConfig myDaoConfig; - @Autowired - protected PlatformTransactionManager myTxManager; - @Autowired - protected ISearchParamPresenceSvc mySearchParamPresenceSvc; - @Autowired - protected ISearchCoordinatorSvc mySearchCoordinatorSvc; - @Autowired - protected ISearchParamRegistry mySearchParamRegistry; - @Autowired - @Qualifier("myAllergyIntoleranceDaoDstu3") - private IFhirResourceDao myAllergyIntoleranceDao; - @Autowired - @Qualifier("myAppointmentDaoDstu3") - private IFhirResourceDao myAppointmentDao; - @Autowired - @Qualifier("myAuditEventDaoDstu3") - private IFhirResourceDao myAuditEventDao; - @Autowired - @Qualifier("myBundleDaoDstu3") - private IFhirResourceDao myBundleDao; - @Autowired - @Qualifier("myCarePlanDaoDstu3") - private IFhirResourceDao myCarePlanDao; - @Autowired - @Qualifier("myCodeSystemDaoDstu3") - private IFhirResourceDao myCodeSystemDao; - @Autowired - @Qualifier("myCompartmentDefinitionDaoDstu3") - private IFhirResourceDao myCompartmentDefinitionDao; - @Autowired - @Qualifier("myConceptMapDaoDstu3") - private IFhirResourceDao myConceptMapDao; - @Autowired - @Qualifier("myConditionDaoDstu3") - private IFhirResourceDao myConditionDao; - @Autowired - @Qualifier("myDeviceDaoDstu3") - private IFhirResourceDao myDeviceDao; - @Autowired - @Qualifier("myDiagnosticReportDaoDstu3") - private IFhirResourceDao myDiagnosticReportDao; - @Autowired - @Qualifier("myEncounterDaoDstu3") - private IFhirResourceDao myEncounterDao; - // @PersistenceContext() - @Autowired - private EntityManager myEntityManager; - @Autowired - private FhirContext myFhirCtx; - @Autowired - @Qualifier("myImmunizationDaoDstu3") - private IFhirResourceDao myImmunizationDao; - @Autowired - @Qualifier("myLocationDaoDstu3") - private IFhirResourceDao myLocationDao; - @Autowired - @Qualifier("myMediaDaoDstu3") - private IFhirResourceDao myMediaDao; - @Autowired - @Qualifier("myMedicationDaoDstu3") - private IFhirResourceDao myMedicationDao; - @Autowired - @Qualifier("myMedicationRequestDaoDstu3") - private IFhirResourceDao myMedicationRequestDao; - @Autowired - @Qualifier("myNamingSystemDaoDstu3") - private IFhirResourceDao myNamingSystemDao; - @Autowired - @Qualifier("myObservationDaoDstu3") - private IFhirResourceDao myObservationDao; - @Autowired - @Qualifier("myOperationDefinitionDaoDstu3") - private IFhirResourceDao myOperationDefinitionDao; - @Autowired - @Qualifier("myOrganizationDaoDstu3") - private IFhirResourceDao myOrganizationDao; - @Autowired - @Qualifier("myPatientDaoDstu3") - private IFhirResourceDaoPatient myPatientDao; - @Autowired - @Qualifier("myPractitionerDaoDstu3") - private IFhirResourceDao myPractitionerDao; - @Autowired - @Qualifier("myQuestionnaireDaoDstu3") - private IFhirResourceDao myQuestionnaireDao; - @Autowired - @Qualifier("myQuestionnaireResponseDaoDstu3") - private IFhirResourceDao myQuestionnaireResponseDao; - @Autowired - @Qualifier("myResourceProvidersDstu3") - private Object myResourceProviders; - @Autowired - @Qualifier("myStructureDefinitionDaoDstu3") - private IFhirResourceDao myStructureDefinitionDao; - @Autowired - @Qualifier("mySubscriptionDaoDstu3") - private IFhirResourceDaoSubscription mySubscriptionDao; - @Autowired - @Qualifier("mySubstanceDaoDstu3") - private IFhirResourceDao mySubstanceDao; - @Autowired - @Qualifier("mySystemDaoDstu3") - private IFhirSystemDao mySystemDao; - @Autowired - @Qualifier("mySystemProviderDstu3") - private JpaSystemProviderDstu3 mySystemProvider; - @Autowired - @Qualifier("myJpaValidationSupportChainDstu3") - private IValidationSupport myValidationSupport; - @Autowired - private IResourceReindexingSvc myResourceReindexingSvc; - @Autowired - private IBulkDataExportSvc myBulkDataExportSvc; - - @Before - public void beforePurgeDatabase() { - purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataExportSvc); - } - - @Before - public void beforeResetConfig() { - myDaoConfig.setHardSearchLimit(1000); - myDaoConfig.setHardTagListLimit(1000); - myDaoConfig.setIncludeLimit(2000); - } - - @Override - protected FhirContext getContext() { - return myFhirCtx; - } - - @Override - protected PlatformTransactionManager getTxManager() { - return myTxManager; - } - - @Test - public void testSearchWithContent() throws Exception { - String methodName = "testEverythingIncludesBackReferences"; - - Organization org = new Organization(); - org.setName(methodName); - IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap map = new SearchParameterMap(); - map.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, new StringParam(methodName)); - try { - myOrganizationDao.search(map).size(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("Fulltext search is not enabled on this service, can not process parameter: _content", e.getMessage()); - } - } - - @Test - public void testSearchWithRegularParam() throws Exception { - String methodName = "testEverythingIncludesBackReferences"; - - Organization org = new Organization(); - org.setName(methodName); - IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap map = new SearchParameterMap(); - map.add(Organization.SP_NAME, new StringParam(methodName)); - myOrganizationDao.search(map); - - } - - @Test - public void testSearchWithText() throws Exception { - String methodName = "testEverythingIncludesBackReferences"; - - Organization org = new Organization(); - org.setName(methodName); - IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap map = new SearchParameterMap(); - map.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, new StringParam(methodName)); - try { - myOrganizationDao.search(map).size(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("Fulltext search is not enabled on this service, can not process parameter: _text", e.getMessage()); - } - } - - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - -} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java index 3f4fe575d4e..a95ee5c5ec8 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java @@ -1,28 +1,29 @@ package ca.uhn.fhir.jpa.dao.r4; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; - -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.r4.model.Observation.ObservationStatus; -import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.*; - import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; 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.Observation.ObservationStatus; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchFtTest.class); @Before @@ -30,12 +31,6 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { myDaoConfig.setReuseCachedSearchResultsForMillis(null); } - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @Test public void testCodeTextSearch() { Observation obs1 = new Observation(); @@ -52,7 +47,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { IIdType id2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless(); SearchParameterMap map; - + map = new SearchParameterMap(); map.add(Observation.SP_CODE, new TokenParam(null, "systolic").setModifier(TokenParamModifier.TEXT)); assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1))); @@ -72,7 +67,6 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { } - @Test public void testResourceTextSearch() { Observation obs1 = new Observation(); @@ -81,15 +75,15 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { obs1.setValue(new Quantity(123)); obs1.getNoteFirstRep().setText("obs1"); IIdType id1 = myObservationDao.create(obs1, mySrd).getId().toUnqualifiedVersionless(); - + Observation obs2 = new Observation(); obs2.getCode().setText("Diastolic Blood Pressure"); obs2.setStatus(ObservationStatus.FINAL); obs2.setValue(new Quantity(81)); IIdType id2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless(); - + SearchParameterMap map; - + map = new SearchParameterMap(); map.add(Constants.PARAM_CONTENT, new StringParam("systolic")); assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1))); @@ -112,22 +106,21 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { obs1.setValue(new StringType("Systolic Blood Pressure")); obs1.setStatus(ObservationStatus.FINAL); IIdType id1 = myObservationDao.create(obs1, mockSrd()).getId().toUnqualifiedVersionless(); - + Observation obs2 = new Observation(); obs1.getCode().setText("AAAAA"); obs1.setValue(new StringType("Diastolic Blood Pressure")); obs2.setStatus(ObservationStatus.FINAL); IIdType id2 = myObservationDao.create(obs2, mockSrd()).getId().toUnqualifiedVersionless(); - + SearchParameterMap map; - + map = new SearchParameterMap(); map.add(Observation.SP_VALUE_STRING, new StringParam("sure").setContains(true)); assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2))); } - @Test public void testSuggestIgnoresBase64Content() { Patient patient = new Patient(); @@ -153,11 +146,11 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { Media med = new Media(); med.getSubject().setReferenceElement(ptId); med.getContent().setContentType("LCws"); - med.getContent().setDataElement(new Base64BinaryType(new byte[] {44,44,44,44,44,44,44,44})); + med.getContent().setDataElement(new Base64BinaryType(new byte[]{44, 44, 44, 44, 44, 44, 44, 44})); med.getContent().setTitle("bbbb syst"); myMediaDao.create(med, mockSrd()); ourLog.info(myFhirCtx.newJsonParser().encodeResourceToString(med)); - + List output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "press", null); ourLog.info("Found: " + output); assertEquals(2, output.size()); @@ -177,12 +170,12 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { assertEquals("bbbb syst", output.get(1).getTerm()); assertEquals("Systolic", output.get(2).getTerm()); assertEquals("Systolic Blood Pressure", output.get(3).getTerm()); - + output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "LCws", null); ourLog.info("Found: " + output); assertEquals(0, output.size()); } - + @Test public void testSuggest() { Patient patient = new Patient(); @@ -238,7 +231,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { assertEquals(2, output.size()); assertEquals("HELLO", output.get(0).getTerm()); assertEquals("ZXC HELLO", output.get(1).getTerm()); - + output = mySearchDao.suggestKeywords("Patient/" + ptId.getIdPart() + "/$everything", "_content", "Z", null); ourLog.info("Found: " + output); assertEquals(0, output.size()); @@ -250,8 +243,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { assertEquals("ZXC HELLO", output.get(1).getTerm()); } - - + @Test public void testSearchAndReindex() { Patient patient; @@ -311,7 +303,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { Device dev1 = new Device(); dev1.setManufacturer("Some Manufacturer"); IIdType devId1 = myDeviceDao.create(dev1, mockSrd()).getId().toUnqualifiedVersionless(); - + Device dev2 = new Device(); dev2.setManufacturer("Some Manufacturer 2"); myDeviceDao.create(dev2, mockSrd()).getId().toUnqualifiedVersionless(); @@ -335,14 +327,14 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { obs3.getCode().addCoding().setCode("CODE3"); obs3.setValue(new StringType("obsvalue3")); IIdType obsId3 = myObservationDao.create(obs3, mockSrd()).getId().toUnqualifiedVersionless(); - + HttpServletRequest request; List actual; request = mock(HttpServletRequest.class); StringAndListParam param; - + ourLog.info("Pt1:{} Pt2:{} Obs1:{} Obs2:{} Obs3:{}", ptId1.getIdPart(), ptId2.getIdPart(), obsId1.getIdPart(), obsId2.getIdPart(), obsId3.getIdPart()); - + param = new StringAndListParam(); param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1"))); actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd())); @@ -360,7 +352,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { /* * Add another match */ - + Observation obs4 = new Observation(); obs4.getSubject().setReferenceElement(ptId1); obs4.getCode().addCoding().setCode("CODE1"); @@ -376,7 +368,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { /* * Make one previous match no longer match */ - + obs1 = new Observation(); obs1.setId(obsId1); obs1.getSubject().setReferenceElement(ptId1); @@ -390,7 +382,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4))); } - + @Test public void testEverythingTypeWithContentFilter() { Patient pt1 = new Patient(); @@ -404,7 +396,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { Device dev1 = new Device(); dev1.setManufacturer("Some Manufacturer"); IIdType devId1 = myDeviceDao.create(dev1, mockSrd()).getId().toUnqualifiedVersionless(); - + Device dev2 = new Device(); dev2.setManufacturer("Some Manufacturer 2"); myDeviceDao.create(dev2, mockSrd()).getId().toUnqualifiedVersionless(); @@ -427,14 +419,14 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { obs3.getCode().addCoding().setCode("CODE3"); obs3.setValue(new StringType("obsvalue3")); IIdType obsId3 = myObservationDao.create(obs3, mockSrd()).getId().toUnqualifiedVersionless(); - + HttpServletRequest request; List actual; request = mock(HttpServletRequest.class); StringAndListParam param; - + ourLog.info("Pt1:{} Pt2:{} Obs1:{} Obs2:{} Obs3:{}", ptId1.getIdPart(), ptId2.getIdPart(), obsId1.getIdPart(), obsId2.getIdPart(), obsId3.getIdPart()); - + param = new StringAndListParam(); param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1"))); actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd())); @@ -447,7 +439,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { /* * Add another match */ - + Observation obs4 = new Observation(); obs4.getSubject().setReferenceElement(ptId1); obs4.getCode().addCoding().setCode("CODE1"); @@ -463,7 +455,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { /* * Make one previous match no longer match */ - + obs1 = new Observation(); obs1.setId(obsId1); obs1.getSubject().setReferenceElement(ptId1); @@ -478,7 +470,6 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { } - /** * When processing transactions, we do two passes. Make sure we don't update the lucene index twice since that would * be inefficient @@ -576,4 +567,9 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test { } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java index b6e3fc8eaa9..1a9fb90065b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java @@ -527,7 +527,12 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { /* * 20 should be prefetched since that's the initial page size */ - + await().until(()->{ + return runInTransaction(()->{ + Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); + return search.getNumFound() == 20; + }); + }); runInTransaction(() -> { Search search = mySearchEntityDao.findByUuidAndFetchIncludes(uuid).orElseThrow(() -> new InternalErrorException("")); assertEquals(20, search.getNumFound()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java new file mode 100644 index 00000000000..67ebd6d3916 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchTest.java @@ -0,0 +1,230 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc; +import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticSearch; +import ca.uhn.fhir.jpa.dao.*; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; +import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; +import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; +import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4; +import ca.uhn.fhir.parser.IParser; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.util.TestUtil; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.ValidationResult; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.hapi.ctx.IValidationSupport; +import org.hl7.fhir.r4.model.*; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.PlatformTransactionManager; + +import javax.persistence.EntityManager; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {TestR4ConfigWithElasticSearch.class}) +public class FhirResourceDaoR4SearchWithElasticSearchTest extends BaseJpaTest { + public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; + public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchWithElasticSearchTest.class); + @Autowired + protected DaoConfig myDaoConfig; + @Autowired + protected PlatformTransactionManager myTxManager; + @Autowired + protected ISearchParamPresenceSvc mySearchParamPresenceSvc; + @Autowired + protected ISearchCoordinatorSvc mySearchCoordinatorSvc; + @Autowired + protected ISearchParamRegistry mySearchParamRegistry; + @Autowired + @Qualifier("myValueSetDaoR4") + protected IFhirResourceDaoValueSet myValueSetDao; + @Autowired + protected IHapiTerminologySvcR4 myTermSvc; + @Autowired + protected IResourceTableDao myResourceTableDao; + @Autowired + @Qualifier("myCodeSystemDaoR4") + private IFhirResourceDao myCodeSystemDao; + @Autowired + private FhirContext myFhirCtx; + @Autowired + @Qualifier("myObservationDaoR4") + private IFhirResourceDao myObservationDao; + @Autowired + @Qualifier("mySystemDaoR4") + private IFhirSystemDao mySystemDao; + @Autowired + private IResourceReindexingSvc myResourceReindexingSvc; + @Autowired + private IBulkDataExportSvc myBulkDataExportSvc; + + @Before + public void beforePurgeDatabase() { + purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataExportSvc); + } + + @Override + protected FhirContext getContext() { + return myFhirCtx; + } + + @Override + protected PlatformTransactionManager getTxManager() { + return myTxManager; + } + + @Test + public void testResourceTextSearch() throws InterruptedException { + Observation obs1 = new Observation(); + obs1.getCode().setText("Systolic Blood Pressure"); + obs1.setStatus(Observation.ObservationStatus.FINAL); + obs1.setValue(new Quantity(123)); + obs1.getNoteFirstRep().setText("obs1"); + IIdType id1 = myObservationDao.create(obs1, mySrd).getId().toUnqualifiedVersionless(); + + Observation obs2 = new Observation(); + obs2.getCode().setText("Diastolic Blood Pressure"); + obs2.setStatus(Observation.ObservationStatus.FINAL); + obs2.setValue(new Quantity(81)); + IIdType id2 = myObservationDao.create(obs2, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap map; + + map = new SearchParameterMap(); + map.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, new StringParam("systolic")); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1))); + + map = new SearchParameterMap(); + map.add(Constants.PARAM_CONTENT, new StringParam("blood")); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2))); + + } + + @Test + public void testExpandWithIsAInExternalValueSet() { + createExternalCsAndLocalVs(); + + ValueSet vs = new ValueSet(); + ValueSet.ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(URL_MY_CODE_SYSTEM); + include.addFilter().setOp(ValueSet.FilterOperator.ISA).setValue("childAA").setProperty("concept"); + + ValueSet result = myValueSetDao.expand(vs, null); + logAndValidateValueSet(result); + + ArrayList codes = toCodesContains(result.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("childAAA", "childAAB")); + + + } + + private CodeSystem createExternalCs() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(URL_MY_CODE_SYSTEM); + codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + + ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); + + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); + cs.getConcepts().add(parentA); + + TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA"); + parentA.addChild(childAA, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + + TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA"); + childAA.addChild(childAAA, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + + TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB"); + childAA.addChild(childAAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + + TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB"); + parentA.addChild(childAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + + TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); + cs.getConcepts().add(parentB); + + TermConcept childBA = new TermConcept(cs, "childBA").setDisplay("Child BA"); + childBA.addChild(childAAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + parentB.addChild(childBA, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + + TermConcept parentC = new TermConcept(cs, "ParentC").setDisplay("Parent C"); + cs.getConcepts().add(parentC); + + TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA"); + parentC.addChild(childCA, TermConceptParentChildLink.RelationshipTypeEnum.ISA); + + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table); + return codeSystem; + } + + private void createExternalCsAndLocalVs() { + CodeSystem codeSystem = createExternalCs(); + + createLocalVs(codeSystem); + } + + private void createLocalVs(CodeSystem codeSystem) { + ValueSet valueSet = new ValueSet(); + valueSet.setUrl(URL_MY_VALUE_SET); + valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl()); + myValueSetDao.create(valueSet, mySrd); + } + + private ArrayList toCodesContains(List theContains) { + ArrayList retVal = new ArrayList(); + for (ValueSet.ValueSetExpansionContainsComponent next : theContains) { + retVal.add(next.getCode()); + } + return retVal; + } + + + private void logAndValidateValueSet(ValueSet theResult) { + IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true); + String encoded = parser.encodeResourceToString(theResult); + ourLog.info(encoded); + + FhirValidator validator = myFhirCtx.newValidator(); + validator.setValidateAgainstStandardSchema(true); + validator.setValidateAgainstStandardSchematron(true); + ValidationResult result = validator.validateWithResult(theResult); + + assertEquals(0, result.getMessages().size()); + + } + + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java index 7057059ac32..5473e4f1eb6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java @@ -1,9 +1,8 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.config.TestR4Config; import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc; -import ca.uhn.fhir.jpa.config.TestR4WithoutLuceneConfig; +import ca.uhn.fhir.jpa.config.TestR4WithLuceneDisabledConfig; import ca.uhn.fhir.jpa.dao.*; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc; @@ -27,7 +26,6 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; @@ -40,7 +38,7 @@ import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = {TestR4WithoutLuceneConfig.class}) +@ContextConfiguration(classes = {TestR4WithLuceneDisabledConfig.class}) public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4SearchWithLuceneDisabledTest.class); @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml index b426d227835..6818eb0a0f3 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml @@ -1,7 +1,7 @@ - INFO + TRACE %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n @@ -40,6 +40,11 @@ + + + + + diff --git a/hapi-fhir-jpaserver-elasticsearch/pom.xml b/hapi-fhir-jpaserver-elasticsearch/pom.xml deleted file mode 100644 index eea93b06b5e..00000000000 --- a/hapi-fhir-jpaserver-elasticsearch/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - 4.0.0 - - - ca.uhn.hapi.fhir - hapi-deployable-pom - 4.1.0-SNAPSHOT - ../hapi-deployable-pom/pom.xml - - - hapi-fhir-jpaserver-elasticsearch - jar - - HAPI FHIR JPA Server - ElasticSearch Integration - - - - org.hibernate - hibernate-search-elasticsearch - - - - diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java index 363f8708e0e..902bd9ab74a 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java @@ -6,7 +6,7 @@ import javax.sql.DataSource; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.search.ElasticsearchMappingProvider; +import ca.uhn.fhir.jpa.search.elastic.ElasticsearchMappingProvider; import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java index 779fe92471b..9c07f46fe71 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationParameter.java @@ -14,6 +14,7 @@ import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.BaseAndListParam; import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.binder.CollectionBinder; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -21,14 +22,12 @@ import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.ReflectionUtil; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseDatatype; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.instance.model.api.*; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; +import java.util.function.Consumer; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -163,7 +162,10 @@ public class OperationParameter implements IParameter { */ isSearchParam &= typeIsConcrete && !IBase.class.isAssignableFrom(myParameterType); - myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType) || String.class.equals(myParameterType) || isSearchParam || ValidationModeEnum.class.equals(myParameterType); + myAllowGet = IPrimitiveType.class.isAssignableFrom(myParameterType) + || String.class.equals(myParameterType) + || isSearchParam + || ValidationModeEnum.class.equals(myParameterType); /* * The parameter can be of type string for validation methods - This is a bit weird. See ValidateDstu2Test. We @@ -172,6 +174,12 @@ public class OperationParameter implements IParameter { if (!myParameterType.equals(IBase.class) && !myParameterType.equals(String.class)) { if (IBaseResource.class.isAssignableFrom(myParameterType) && myParameterType.isInterface()) { myParamType = "Resource"; + } else if (IBaseReference.class.isAssignableFrom(myParameterType)) { + myParamType = "Reference"; + myAllowGet = true; + } else if (IBaseCoding.class.isAssignableFrom(myParameterType)) { + myParamType = "Coding"; + myAllowGet = true; } else if (DateRangeParam.class.isAssignableFrom(myParameterType)) { myParamType = "date"; myMax = 2; @@ -266,7 +274,7 @@ public class OperationParameter implements IParameter { if (myAllowGet) { if (DateRangeParam.class.isAssignableFrom(myParameterType)) { - List parameters = new ArrayList(); + List parameters = new ArrayList<>(); parameters.add(QualifiedParamList.singleton(paramValues[0])); if (paramValues.length > 1) { parameters.add(QualifiedParamList.singleton(paramValues[1])); @@ -275,11 +283,31 @@ public class OperationParameter implements IParameter { FhirContext ctx = theRequest.getServer().getFhirContext(); dateRangeParam.setValuesAsQueryTokens(ctx, myName, parameters); matchingParamValues.add(dateRangeParam); + + } else if (IBaseReference.class.isAssignableFrom(myParameterType)) { + + processAllCommaSeparatedValues(paramValues, t -> { + IBaseReference param = (IBaseReference) ReflectionUtil.newInstance(myParameterType); + param.setReference(t); + matchingParamValues.add(param); + }); + + } else if (IBaseCoding.class.isAssignableFrom(myParameterType)) { + + processAllCommaSeparatedValues(paramValues, t -> { + TokenParam tokenParam = new TokenParam(); + tokenParam.setValueAsQueryToken(myContext, myName, null, t); + + IBaseCoding param = (IBaseCoding) ReflectionUtil.newInstance(myParameterType); + param.setSystem(tokenParam.getSystem()); + param.setCode(tokenParam.getValue()); + matchingParamValues.add(param); + }); + } else if (String.class.isAssignableFrom(myParameterType)) { - for (String next : paramValues) { - matchingParamValues.add(next); - } + matchingParamValues.addAll(Arrays.asList(paramValues)); + } else if (ValidationModeEnum.class.equals(myParameterType)) { if (isNotBlank(paramValues[0])) { @@ -309,6 +337,22 @@ public class OperationParameter implements IParameter { } } + /** + * This method is here to mediate between the POST form of operation parameters (i.e. elements within a Parameters + * resource) and the GET form (i.e. URL parameters). + *

+ * Essentially we want to allow comma-separated values as is done with searches on URLs. + *

+ */ + private void processAllCommaSeparatedValues(String[] theParamValues, Consumer theHandler) { + for (String nextValue : theParamValues) { + QualifiedParamList qualifiedParamList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null, nextValue); + for (String nextSplitValue : qualifiedParamList) { + theHandler.accept(nextSplitValue); + } + } + } + private void translateQueryParametersIntoServerArgumentForPost(RequestDetails theRequest, List matchingParamValues) { IBaseResource requestContents = (IBaseResource) theRequest.getUserData().get(REQUEST_CONTENTS_USERDATA_KEY); if (requestContents != null) { @@ -394,10 +438,6 @@ public class OperationParameter implements IParameter { } } - public static void throwInvalidMode(String paramValues) { - throw new InvalidRequestException("Invalid mode value: \"" + paramValues + "\""); - } - interface IOperationParamConverter { Object incomingServer(Object theObject); @@ -429,5 +469,9 @@ public class OperationParameter implements IParameter { } + public static void throwInvalidMode(String paramValues) { + throw new InvalidRequestException("Invalid mode value: \"" + paramValues + "\""); + } + } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java new file mode 100644 index 00000000000..396b84ede9a --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java @@ -0,0 +1,43 @@ +package ca.uhn.fhir.rest.server.provider; + +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import java.util.List; + +/** + * This class implements the Observation + * $lastn operation. + *

+ * It is does not implement the actual storage logic for this operation, but can be + * subclassed to provide this functionality. + *

+ * + * @since 4.1.0 + */ +public abstract class BaseLastNProvider { + + @Operation(name = Constants.OPERATION_LASTN, typeName = "Observation", idempotent = true) + public IBaseBundle lastN( + ServletRequestDetails theRequestDetails, + @OperationParam(name = "subject", typeName = "reference", min = 0, max = 1) IBaseReference theSubject, + @OperationParam(name = "category", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List theCategories, + @OperationParam(name = "code", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List theCodes, + @OperationParam(name = "max", typeName = "integer", min = 0, max = 1) IPrimitiveType theMax + ) { + return processLastN(theSubject, theCategories, theCodes, theMax); + } + + /** + * Subclasses should implement this method + */ + protected abstract IBaseBundle processLastN(IBaseReference theSubject, List theCategories, List theCodes, IPrimitiveType theMax); + + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java new file mode 100644 index 00000000000..3b5b4c6376f --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java @@ -0,0 +1,44 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.test.utilities.JettyUtil; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.After; + +public class BaseR4ServerTest { + private FhirContext myCtx = FhirContext.forR4(); + private Server myServer; + protected IGenericClient myClient; + protected String myBaseUrl; + + @After + public void after() throws Exception { + JettyUtil.closeServer(myServer); + } + + protected void startServer(Object theProvider) throws Exception { + RestfulServer servlet = new RestfulServer(myCtx); + servlet.registerProvider(theProvider); + ServletHandler proxyHandler = new ServletHandler(); + servlet.setDefaultResponseEncoding(EncodingEnum.XML); + servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + + myServer = new Server(0); + myServer.setHandler(proxyHandler); + JettyUtil.startServer(myServer); + int port = JettyUtil.getPortForStartedServer(myServer); + + myBaseUrl = "http://localhost:" + port; + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + myClient = myCtx.newRestfulGenericClient(myBaseUrl); + } + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java new file mode 100644 index 00000000000..bba6c0d3b4e --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java @@ -0,0 +1,64 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.rest.server.provider.BaseLastNProvider; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.Bundle; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class LastNProviderTest extends BaseR4ServerTest { + + private IBaseReference myLastSubject; + private List myLastCategories; + private List myLastCodes; + private IPrimitiveType myLastMax; + + @Test + public void testAllParamsPopulated() throws Exception { + + class MyProvider extends BaseLastNProvider { + + @Override + protected IBaseBundle processLastN(IBaseReference theSubject, List theCategories, List theCodes, IPrimitiveType theMax) { + myLastSubject = theSubject; + myLastCategories = theCategories; + myLastCodes = theCodes; + myLastMax = theMax; + + Bundle retVal = new Bundle(); + retVal.setId("abc123"); + retVal.setType(Bundle.BundleType.SEARCHSET); + return retVal; + } + } + MyProvider provider = new MyProvider(); + startServer(provider); + + Bundle response = myClient + .search() + .byUrl(myBaseUrl + "/Observation/$lastn?subject=Patient/123&category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory,http://terminology.hl7.org/CodeSystem/observation-category|vital-signs&code=http://loinc.org|1111-1,http://loinc.org|2222-2&max=15") + .returnBundle(Bundle.class) + .execute(); + assertEquals("abc123", response.getIdElement().getIdPart()); + assertEquals("Patient/123", myLastSubject.getReferenceElement().getValue()); + assertEquals(2, myLastCategories.size()); + assertEquals("http://terminology.hl7.org/CodeSystem/observation-category", myLastCategories.get(0).getSystem()); + assertEquals("laboratory", myLastCategories.get(0).getCode()); + assertEquals("http://terminology.hl7.org/CodeSystem/observation-category", myLastCategories.get(1).getSystem()); + assertEquals("vital-signs", myLastCategories.get(1).getCode()); + assertEquals(2, myLastCodes.size()); + assertEquals("http://loinc.org", myLastCodes.get(0).getSystem()); + assertEquals("1111-1", myLastCodes.get(0).getCode()); + assertEquals("http://loinc.org", myLastCodes.get(1).getSystem()); + assertEquals("2222-2", myLastCodes.get(1).getCode()); + assertEquals(15, myLastMax.getValue().intValue()); + } + + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java index 14fa05826ef..3204c2c629f 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java @@ -1,24 +1,15 @@ package ca.uhn.fhir.rest.server; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; -import org.junit.After; import org.junit.Test; import java.util.List; @@ -27,18 +18,9 @@ import java.util.Set; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; -public class ServerMethodSelectionR4Test { +public class ServerMethodSelectionR4Test extends BaseR4ServerTest { - private FhirContext myCtx = FhirContext.forR4(); - private Server myServer; - private IGenericClient myClient; - - @After - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } - /** * Server method with no _include * Client request with _include @@ -161,22 +143,6 @@ public class ServerMethodSelectionR4Test { assertEquals(1, results.getEntry().size()); } - private void startServer(Object theProvider) throws Exception { - RestfulServer servlet = new RestfulServer(myCtx); - servlet.registerProvider(theProvider); - ServletHandler proxyHandler = new ServletHandler(); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - - myServer = new Server(0); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - int port = JettyUtil.getPortForStartedServer(myServer); - - myClient = myCtx.newRestfulGenericClient("http://localhost:" + port); - } public static class MyBaseProvider implements IResourceProvider { @@ -185,6 +151,7 @@ public class ServerMethodSelectionR4Test { public Class getResourceType() { return Patient.class; } + } } diff --git a/pom.xml b/pom.xml index 55b1dd4ae39..bc7792167a8 100755 --- a/pom.xml +++ b/pom.xml @@ -601,7 +601,7 @@ 5.4.4.Final - 5.11.1.Final + 5.11.3.Final 5.5.5 5.4.2.Final 4.4.11 @@ -857,18 +857,6 @@ 7.0.0.jre8 - javax.mail javax.mail-api @@ -1405,6 +1393,11 @@ xmlunit-core 2.4.0 + + pl.allegro.tech + embedded-elasticsearch + 2.10.0 + xpp3 xpp3 @@ -1669,6 +1662,11 @@ org.jacoco jacoco-maven-plugin 0.8.4 + + + ca/uhn/fhir/model/dstu2/**/*.class + + org.apache.maven.plugins @@ -2391,7 +2389,6 @@ hapi-fhir-jaxrsserver-base hapi-fhir-jaxrsserver-example hapi-fhir-jpaserver-base - hapi-fhir-jpaserver-elasticsearch hapi-fhir-jpaserver-migrate restful-server-example hapi-fhir-testpage-overlay diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d1ef194eab9..0beec66cdf9 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -12,7 +12,8 @@ latest versions (dependent HAPI modules listed in brackets): -
  • Hibernate Core (Core): 5.4.2.Final -> 5.4.4.Final
  • +
  • Hibernate Core (JPA): 5.4.2.Final -> 5.4.4.Final
  • +
  • Hibernate Search (JPA): 5.11.1.Final -> 5.11.3.Final
  • Jackson Databind (JPA): 2.9.9 -> 2.9.10 (CVE-2019-16335, CVE-2019-14540)
  • ]]> @@ -222,6 +223,11 @@ The JPA server failed to find codes defined in not-present codesystems in some cases, and reported that the CodeSystem did not exist. This has been corrected. + + Support for ElasticSearch has been added to the JPA server directly (i.e. without needing a separate + module) and a new class called "ElasticsearchHibernatePropertiesBuilder" has been added to facilitate + the creation of relevant properties. + From c8ce07c40ead2f689a4654947837ab359d4cc09f Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 30 Sep 2019 09:44:47 -0400 Subject: [PATCH 18/23] Deprecate redundant IVersionSpecificBundleProvider method --- .../main/java/example/IncludesExamples.java | 5 +- .../api/IVersionSpecificBundleFactory.java | 21 +- .../ca/uhn/fhir/cli/ExampleDataUploader.java | 3 +- .../BaseHttpClientInvocationWithContents.java | 12 +- .../uhn/hapi/fhir/docs/IncludesExamples.java | 5 +- .../rest/server/Dstu2_1BundleFactory.java | 61 +- .../provider/dstu2/Dstu2BundleFactory.java | 38 -- .../hapi/rest/server/Dstu3BundleFactory.java | 39 -- .../dstu2hl7org/Dstu2Hl7OrgBundleFactory.java | 56 +- .../r4/hapi/rest/server/R4BundleFactory.java | 39 -- .../r5/hapi/rest/server/R5BundleFactory.java | 560 ++++++++---------- .../provider/dstu2/Dstu2BundleFactory.java | 63 +- src/changes/changes.xml | 22 +- 13 files changed, 345 insertions(+), 579 deletions(-) diff --git a/examples/src/main/java/example/IncludesExamples.java b/examples/src/main/java/example/IncludesExamples.java index 2e8744468b4..899d095d62d 100644 --- a/examples/src/main/java/example/IncludesExamples.java +++ b/examples/src/main/java/example/IncludesExamples.java @@ -25,8 +25,9 @@ public class IncludesExamples { FhirContext ctx = FhirContext.forDstu2(); Dstu2BundleFactory bf = new Dstu2BundleFactory(ctx); - bf.initializeBundleFromResourceList(null, resources, "http://example.com/base", "http://example.com/base/Patient", 1, BundleTypeEnum.SEARCHSET); - IBaseResource b = bf.getResourceBundle(); + bf.addRootPropertiesToBundle(null, null, null, null, null, resources.size(), BundleTypeEnum.SEARCHSET, null); + bf.addResourcesToBundle(new ArrayList<>(resources), BundleTypeEnum.SEARCHSET, null, null, null); + IBaseResource b = bf.getResourceBundle(); // Encode the bundle String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java index 3b50d53eec4..2be14d372ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java @@ -19,14 +19,17 @@ package ca.uhn.fhir.rest.api; * limitations under the License. * #L% */ -import java.util.*; - -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.valueset.BundleTypeEnum; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; /** * This interface should be considered experimental and will likely change in future releases of HAPI. Use with caution! @@ -39,7 +42,15 @@ public interface IVersionSpecificBundleFactory { IBaseResource getResourceBundle(); - void initializeBundleFromResourceList(String theAuthor, List theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType); + /** + * @deprecated This was deprecated in HAPI FHIR 4.1.0 as it provides duplicate functionality to the {@link #addRootPropertiesToBundle(String, String, String, String, String, Integer, BundleTypeEnum, IPrimitiveType)} + * and {@link #addResourcesToBundle(List, BundleTypeEnum, String, BundleInclusionRule, Set)} methods + */ + @Deprecated + default void initializeBundleFromResourceList(String theAuthor, List theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { + addRootPropertiesToBundle(null, null, null, null, null, theResult.size(), theBundleType, null); + addResourcesToBundle(new ArrayList<>(theResult), theBundleType, null, null, null); + } void initializeWithBundleResource(IBaseResource theResource); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java index eb1bb118ad5..ab0bebfa720 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java @@ -715,7 +715,8 @@ public class ExampleDataUploader extends BaseCommand { ourLog.info("About to upload {} examples in a transaction, {} remaining", subResourceList.size(), resources.size()); IVersionSpecificBundleFactory bundleFactory = ctx.newBundleFactory(); - bundleFactory.initializeBundleFromResourceList(null, subResourceList, null, null, 0, BundleTypeEnum.TRANSACTION); + bundleFactory.addRootPropertiesToBundle(null, null, null, null, null, subResourceList.size(), BundleTypeEnum.TRANSACTION, null); + bundleFactory.addResourcesToBundle(new ArrayList<>(subResourceList), BundleTypeEnum.TRANSACTION, null, null, null); IBaseResource subBundle = bundleFactory.getResourceBundle(); String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(subBundle); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java index 56e7365bc38..802b35a9b04 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseHttpClientInvocationWithContents.java @@ -22,6 +22,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * #L% */ +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -52,7 +53,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca private boolean myOmitResourceId = false; private Map> myParams; private final IBaseResource myResource; - private final List myResources; + private final List myResources; private final String myUrlPath; private IIdType myForceResourceId; @@ -70,7 +71,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca super(theContext); myResource = null; myUrlPath = null; - myResources = theResources; + myResources = new ArrayList<>(theResources); myContents = null; myBundleType = theBundleType; } @@ -172,11 +173,8 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca parser.setOmitResourceId(myOmitResourceId); if (myResources != null) { IVersionSpecificBundleFactory bundleFactory = getContext().newBundleFactory(); - bundleFactory.initializeBundleFromResourceList("", myResources, "", "", myResources.size(), myBundleType); - IBaseResource bundle = bundleFactory.getResourceBundle(); - if (bundle != null) { - return parser.encodeResourceToString(bundle); - } + bundleFactory.addRootPropertiesToBundle(null, null, null, null, null, myResources.size(), myBundleType, null); + bundleFactory.addResourcesToBundle(myResources, myBundleType, null, null, null); IBaseResource bundleRes = bundleFactory.getResourceBundle(); return parser.encodeResourceToString(bundleRes); } else if (myContents != null) { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/IncludesExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/IncludesExamples.java index 15755661756..222cd3a57d1 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/IncludesExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/IncludesExamples.java @@ -44,8 +44,9 @@ public class IncludesExamples { FhirContext ctx = FhirContext.forDstu2(); R4BundleFactory bf = new R4BundleFactory(ctx); - bf.initializeBundleFromResourceList(null, resources, "http://example.com/base", "http://example.com/base/Patient", 1, BundleTypeEnum.SEARCHSET); - IBaseResource b = bf.getResourceBundle(); + bf.addRootPropertiesToBundle(null, null, null, null, null, resources.size(), BundleTypeEnum.SEARCHSET, null); + bf.addResourcesToBundle(new ArrayList<>(resources), BundleTypeEnum.SEARCHSET, null, null, null); + IBaseResource b = bf.getResourceBundle(); // Encode the bundle String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(b); diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java index 273b0c2cf8b..cf5e7800631 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java @@ -9,9 +9,9 @@ package org.hl7.fhir.dstu2016may.hapi.rest.server; * 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. @@ -19,13 +19,6 @@ package org.hl7.fhir.dstu2016may.hapi.rest.server; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.util.*; - -import org.hl7.fhir.dstu2016may.model.*; -import org.hl7.fhir.dstu2016may.model.Bundle.*; -import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.BundleInclusionRule; @@ -35,6 +28,18 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.util.ResourceReferenceInfo; +import org.hl7.fhir.dstu2016may.model.Bundle; +import org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.dstu2016may.model.Bundle.BundleLinkComponent; +import org.hl7.fhir.dstu2016may.model.Bundle.SearchEntryMode; +import org.hl7.fhir.dstu2016may.model.DomainResource; +import org.hl7.fhir.dstu2016may.model.IdType; +import org.hl7.fhir.dstu2016may.model.Resource; +import org.hl7.fhir.instance.model.api.*; + +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory { @@ -282,44 +287,6 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory { return false; } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, - BundleTypeEnum theBundleType) { - ensureBundle(); - - myBundle.setId(UUID.randomUUID().toString()); - - myBundle.getMeta().setLastUpdated(new Date()); - - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - Resource next = (Resource) nextBaseRes; - BundleEntryComponent nextEntry = myBundle.addEntry(); - - nextEntry.setResource(next); - if (next.getIdElement().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerb.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerb.PUT); - if (next.getIdElement().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getId()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - @Override public void initializeWithBundleResource(IBaseResource theBundle) { myBundle = (Bundle) theBundle; diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index 655093d45b6..a77c9f42d86 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -275,44 +275,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { return false; } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, - BundleTypeEnum theBundleType) { - ensureBundle(); - - myBundle.setId(UUID.randomUUID().toString()); - - ResourceMetadataKeyEnum.PUBLISHED.put(myBundle, InstantDt.withCurrentTime()); - - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - IResource next = (IResource) nextBaseRes; - Entry nextEntry = myBundle.addEntry(); - - nextEntry.setResource(next); - if (next.getId().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerbEnum.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerbEnum.PUT); - if (next.getId().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getId()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdDt(theServerBase, resourceType, next.getId().getIdPart(), next.getId().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - @Override public void initializeWithBundleResource(IBaseResource theBundle) { myBundle = (Bundle) theBundle; diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java index ef388b7bab3..1580395de52 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent; -import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb; import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode; import org.hl7.fhir.dstu3.model.DomainResource; import org.hl7.fhir.dstu3.model.IdType; @@ -314,44 +313,6 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { return false; } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, - BundleTypeEnum theBundleType) { - myBundle = new Bundle(); - - myBundle.setId(UUID.randomUUID().toString()); - - myBundle.getMeta().setLastUpdated(new Date()); - - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - Resource next = (Resource) nextBaseRes; - BundleEntryComponent nextEntry = myBundle.addEntry(); - - nextEntry.setResource(next); - if (next.getIdElement().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerb.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerb.PUT); - if (next.getIdElement().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getId()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - @Override public void initializeWithBundleResource(IBaseResource theBundle) { myBundle = (Bundle) theBundle; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java index 886def26cd1..9ce9fcbbb2f 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.provider.dstu2hl7org; * 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. @@ -30,7 +30,6 @@ import ca.uhn.fhir.util.ResourceReferenceInfo; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent; -import org.hl7.fhir.dstu2.model.Bundle.HTTPVerb; import org.hl7.fhir.dstu2.model.Bundle.SearchEntryMode; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.InstantType; @@ -116,9 +115,9 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { populateBundleEntryFullUrl(next, entry); } - /* - * Actually add the resources to the bundle - */ + /* + * Actually add the resources to the bundle + */ for (IBaseResource next : includedResources) { BundleEntryComponent entry = myBundle.addEntry(); entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); @@ -208,9 +207,9 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { // } } - /* - * Actually add the resources to the bundle - */ + /* + * Actually add the resources to the bundle + */ for (IBaseResource next : includedResources) { myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); } @@ -276,45 +275,6 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { return false; } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, - String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { - ensureBundle(); - - myBundle.setId(UUID.randomUUID().toString()); - - myBundle.getMeta().setLastUpdatedElement(InstantType.withCurrentTime()); - - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - IBaseResource next = (IBaseResource) nextBaseRes; - BundleEntryComponent nextEntry = myBundle.addEntry(); - - nextEntry.setResource((Resource) next); - if (next.getIdElement().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerb.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerb.PUT); - if (next.getIdElement().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getIdElement().getValue()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), - next.getIdElement().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - @Override public void initializeWithBundleResource(IBaseResource theBundle) { myBundle = (Bundle) theBundle; diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java index dba96fec27d..98a0f7575e9 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java @@ -33,7 +33,6 @@ import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent; -import org.hl7.fhir.r4.model.Bundle.HTTPVerb; import org.hl7.fhir.r4.model.Bundle.SearchEntryMode; import org.hl7.fhir.r4.model.DomainResource; import org.hl7.fhir.r4.model.IdType; @@ -316,44 +315,6 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory { return false; } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, - BundleTypeEnum theBundleType) { - myBundle = new Bundle(); - - myBundle.setId(UUID.randomUUID().toString()); - - myBundle.getMeta().setLastUpdated(new Date()); - - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - Resource next = (Resource) nextBaseRes; - BundleEntryComponent nextEntry = myBundle.addEntry(); - - nextEntry.setResource(next); - if (next.getIdElement().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerb.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerb.PUT); - if (next.getIdElement().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getId()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - @Override public void initializeWithBundleResource(IBaseResource theBundle) { myBundle = (Bundle) theBundle; diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java index 8684661d009..938eb5c8365 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java @@ -33,7 +33,6 @@ import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent; -import org.hl7.fhir.r5.model.Bundle.HTTPVerb; import org.hl7.fhir.r5.model.Bundle.SearchEntryMode; import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.IdType; @@ -45,352 +44,315 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; @SuppressWarnings("Duplicates") public class R5BundleFactory implements IVersionSpecificBundleFactory { - private String myBase; - private Bundle myBundle; - private FhirContext myContext; + private String myBase; + private Bundle myBundle; + private FhirContext myContext; - public R5BundleFactory(FhirContext theContext) { - myContext = theContext; - } + public R5BundleFactory(FhirContext theContext) { + myContext = theContext; + } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); + private void addResourcesForSearch(List theResult) { + List includedResources = new ArrayList(); + Set addedResourceIds = new HashSet(); - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } + for (IBaseResource next : theResult) { + if (next.getIdElement().isEmpty() == false) { + addedResourceIds.add(next.getIdElement()); + } + } - for (IBaseResource nextBaseRes : theResult) { - Resource next = (Resource) nextBaseRes; - Set containedIds = new HashSet(); - if (next instanceof DomainResource) { - for (Resource nextContained : ((DomainResource) next).getContained()) { - if (nextContained.getIdElement().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } - } + for (IBaseResource nextBaseRes : theResult) { + Resource next = (Resource) nextBaseRes; + Set containedIds = new HashSet(); + if (next instanceof DomainResource) { + for (Resource nextContained : ((DomainResource) next).getContained()) { + if (nextContained.getIdElement().isEmpty() == false) { + containedIds.add(nextContained.getIdElement().getValue()); + } + } + } - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); - do { - List addedResourcesThisPass = new ArrayList<>(); + List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); + do { + List addedResourcesThisPass = new ArrayList<>(); - for (IBaseReference nextRef : references) { - IAnyResource nextRes = (IAnyResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } + for (IBaseReference nextRef : references) { + IAnyResource nextRes = (IAnyResource) nextRef.getResource(); + if (nextRes != null) { + if (nextRes.getIdElement().hasIdPart()) { + if (containedIds.contains(nextRes.getIdElement().getValue())) { + // Don't add contained IDs as top level resources + continue; + } - IIdType id = nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } + IIdType id = nextRes.getIdElement(); + if (id.hasResourceType() == false) { + String resName = myContext.getResourceDefinition(nextRes).getName(); + id = id.withResourceType(resName); + } - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } + if (!addedResourceIds.contains(id)) { + addedResourceIds.add(id); + addedResourcesThisPass.add(nextRes); + } - } - } - } + } + } + } - // Linked resources may themselves have linked resources - references = new ArrayList<>(); - for (IAnyResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class); - references.addAll(newReferences); - } + // Linked resources may themselves have linked resources + references = new ArrayList<>(); + for (IAnyResource iResource : addedResourcesThisPass) { + List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class); + references.addAll(newReferences); + } - includedResources.addAll(addedResourcesThisPass); + includedResources.addAll(addedResourcesThisPass); - } while (references.isEmpty() == false); + } while (references.isEmpty() == false); - BundleEntryComponent entry = myBundle.addEntry().setResource(next); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getId()); - } + BundleEntryComponent entry = myBundle.addEntry().setResource(next); + if (next.getIdElement().hasBaseUrl()) { + entry.setFullUrl(next.getId()); + } - String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb); - entry.getRequest().getUrlElement().setValue(next.getId()); - } - if ("DELETE".equals(httpVerb)) { - entry.setResource(null); - } - } + String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); + if (httpVerb != null) { + entry.getRequest().getMethodElement().setValueAsString(httpVerb); + entry.getRequest().getUrlElement().setValue(next.getId()); + } + if ("DELETE".equals(httpVerb)) { + entry.setResource(null); + } + } - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } + /* + * Actually add the resources to the bundle + */ + for (IBaseResource next : includedResources) { + BundleEntryComponent entry = myBundle.addEntry(); + entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + if (next.getIdElement().hasBaseUrl()) { + entry.setFullUrl(next.getIdElement().getValue()); + } + } + } - @Override - public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { - ensureBundle(); + @Override + public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { + ensureBundle(); - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); + List includedResources = new ArrayList(); + Set addedResourceIds = new HashSet(); - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } + for (IBaseResource next : theResult) { + if (next.getIdElement().isEmpty() == false) { + addedResourceIds.add(next.getIdElement()); + } + } - for (IBaseResource next : theResult) { + for (IBaseResource next : theResult) { - Set containedIds = new HashSet(); + Set containedIds = new HashSet(); - if (next instanceof DomainResource) { - for (Resource nextContained : ((DomainResource) next).getContained()) { - if (isNotBlank(nextContained.getId())) { - containedIds.add(nextContained.getId()); - } - } - } + if (next instanceof DomainResource) { + for (Resource nextContained : ((DomainResource) next).getContained()) { + if (isNotBlank(nextContained.getId())) { + containedIds.add(nextContained.getId()); + } + } + } - List references = myContext.newTerser().getAllResourceReferences(next); - do { - List addedResourcesThisPass = new ArrayList(); + List references = myContext.newTerser().getAllResourceReferences(next); + do { + List addedResourcesThisPass = new ArrayList(); - for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { - continue; - } + for (ResourceReferenceInfo nextRefInfo : references) { + if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + continue; + } - IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } + IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource(); + if (nextRes != null) { + if (nextRes.getIdElement().hasIdPart()) { + if (containedIds.contains(nextRes.getIdElement().getValue())) { + // Don't add contained IDs as top level resources + continue; + } - IIdType id = nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } + IIdType id = nextRes.getIdElement(); + if (id.hasResourceType() == false) { + String resName = myContext.getResourceDefinition(nextRes).getName(); + id = id.withResourceType(resName); + } - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } + if (!addedResourceIds.contains(id)) { + addedResourceIds.add(id); + addedResourcesThisPass.add(nextRes); + } - } - } - } + } + } + } - includedResources.addAll(addedResourcesThisPass); + includedResources.addAll(addedResourcesThisPass); - // Linked resources may themselves have linked resources - references = new ArrayList<>(); - for (IAnyResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllResourceReferences(iResource); - references.addAll(newReferences); - } - } while (references.isEmpty() == false); + // Linked resources may themselves have linked resources + references = new ArrayList<>(); + for (IAnyResource iResource : addedResourcesThisPass) { + List newReferences = myContext.newTerser().getAllResourceReferences(iResource); + references.addAll(newReferences); + } + } while (references.isEmpty() == false); - BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); - Resource nextAsResource = (Resource) next; - IIdType id = populateBundleEntryFullUrl(next, entry); + BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); + Resource nextAsResource = (Resource) next; + IIdType id = populateBundleEntryFullUrl(next, entry); - // Populate Request - String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb); - if (id != null) { - entry.getRequest().setUrl(id.getValue()); - } - } - if ("DELETE".equals(httpVerb)) { - entry.setResource(null); - } + // Populate Request + String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource); + if (httpVerb != null) { + entry.getRequest().getMethodElement().setValueAsString(httpVerb); + if (id != null) { + entry.getRequest().setUrl(id.getValue()); + } + } + if ("DELETE".equals(httpVerb)) { + entry.setResource(null); + } - // Populate Bundle.entry.response - if (theBundleType != null) { - switch (theBundleType) { - case BATCH_RESPONSE: - case TRANSACTION_RESPONSE: - case HISTORY: - if ("1".equals(id.getVersionIdPart())) { - entry.getResponse().setStatus("201 Created"); - } else if (isNotBlank(id.getVersionIdPart())) { - entry.getResponse().setStatus("200 OK"); - } - if (isNotBlank(id.getVersionIdPart())) { - entry.getResponse().setEtag(RestfulServerUtils.createEtag(id.getVersionIdPart())); - } - break; - } - } + // Populate Bundle.entry.response + if (theBundleType != null) { + switch (theBundleType) { + case BATCH_RESPONSE: + case TRANSACTION_RESPONSE: + case HISTORY: + if ("1".equals(id.getVersionIdPart())) { + entry.getResponse().setStatus("201 Created"); + } else if (isNotBlank(id.getVersionIdPart())) { + entry.getResponse().setStatus("200 OK"); + } + if (isNotBlank(id.getVersionIdPart())) { + entry.getResponse().setEtag(RestfulServerUtils.createEtag(id.getVersionIdPart())); + } + break; + } + } - // Populate Bundle.entry.search - String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource); - if (searchMode != null) { - entry.getSearch().getModeElement().setValueAsString(searchMode); - } - } + // Populate Bundle.entry.search + String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource); + if (searchMode != null) { + entry.getSearch().getModeElement().setValueAsString(searchMode); + } + } - /* - * Actually add the resources to the bundle - */ - for (IAnyResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - populateBundleEntryFullUrl(next, entry); - } + /* + * Actually add the resources to the bundle + */ + for (IAnyResource next : includedResources) { + BundleEntryComponent entry = myBundle.addEntry(); + entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + populateBundleEntryFullUrl(next, entry); + } - } + } - @Override - public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, - IPrimitiveType theLastUpdated) { - ensureBundle(); + @Override + public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, + IPrimitiveType theLastUpdated) { + ensureBundle(); - myBase = theServerBase; + myBase = theServerBase; - if (myBundle.getIdElement().isEmpty()) { - myBundle.setId(theId); - } - if (myBundle.getIdElement().isEmpty()) { - myBundle.setId(UUID.randomUUID().toString()); - } + if (myBundle.getIdElement().isEmpty()) { + myBundle.setId(theId); + } + if (myBundle.getIdElement().isEmpty()) { + myBundle.setId(UUID.randomUUID().toString()); + } - if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { - myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); - } + if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { + myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString()); + } - if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); - } - if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { - myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); - } - if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { - myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); - } + if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) { + myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf); + } + if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) { + myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext); + } + if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) { + myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev); + } - if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - } + if (myBundle.getTypeElement().isEmpty() && theBundleType != null) { + myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); + } - if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) { - myBundle.getTotalElement().setValue(theTotalResults); - } - } + if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) { + myBundle.getTotalElement().setValue(theTotalResults); + } + } - private void ensureBundle() { - if (myBundle == null) { - myBundle = new Bundle(); - } - } + private void ensureBundle() { + if (myBundle == null) { + myBundle = new Bundle(); + } + } - @Override - public IBaseResource getResourceBundle() { - return myBundle; - } + @Override + public IBaseResource getResourceBundle() { + return myBundle; + } - private boolean hasLink(String theLinkType, Bundle theBundle) { - for (BundleLinkComponent next : theBundle.getLink()) { - if (theLinkType.equals(next.getRelation())) { - return true; - } - } - return false; - } + private boolean hasLink(String theLinkType, Bundle theBundle) { + for (BundleLinkComponent next : theBundle.getLink()) { + if (theLinkType.equals(next.getRelation())) { + return true; + } + } + return false; + } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, - BundleTypeEnum theBundleType) { - myBundle = new Bundle(); - myBundle.setId(UUID.randomUUID().toString()); + @Override + public void initializeWithBundleResource(IBaseResource theBundle) { + myBundle = (Bundle) theBundle; + } - myBundle.getMeta().setLastUpdated(new Date()); + private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + IIdType idElement = null; + if (next.getIdElement().hasBaseUrl()) { + idElement = next.getIdElement(); + entry.setFullUrl(idElement.toVersionless().getValue()); + } else { + if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) { + idElement = next.getIdElement(); + idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); + entry.setFullUrl(idElement.toVersionless().getValue()); + } + } + return idElement; + } - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - Resource next = (Resource) nextBaseRes; - BundleEntryComponent nextEntry = myBundle.addEntry(); - - nextEntry.setResource(next); - if (next.getIdElement().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerb.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerb.PUT); - if (next.getIdElement().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getId()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - - @Override - public void initializeWithBundleResource(IBaseResource theBundle) { - myBundle = (Bundle) theBundle; - } - - private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { - IIdType idElement = null; - if (next.getIdElement().hasBaseUrl()) { - idElement = next.getIdElement(); - entry.setFullUrl(idElement.toVersionless().getValue()); - } else { - if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) { - idElement = next.getIdElement(); - idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); - entry.setFullUrl(idElement.toVersionless().getValue()); - } - } - return idElement; - } - - @Override - public List toListOfResources() { - ArrayList retVal = new ArrayList(); - for (BundleEntryComponent next : myBundle.getEntry()) { - if (next.getResource() != null) { - retVal.add(next.getResource()); - } else if (next.getResponse().getLocationElement().isEmpty() == false) { - IdType id = new IdType(next.getResponse().getLocation()); - String resourceType = id.getResourceType(); - if (isNotBlank(resourceType)) { - IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance(); - res.setId(id); - retVal.add(res); - } - } - } - return retVal; - } + @Override + public List toListOfResources() { + ArrayList retVal = new ArrayList(); + for (BundleEntryComponent next : myBundle.getEntry()) { + if (next.getResource() != null) { + retVal.add(next.getResource()); + } else if (next.getResponse().getLocationElement().isEmpty() == false) { + IdType id = new IdType(next.getResponse().getLocation()); + String resourceType = id.getResourceType(); + if (isNotBlank(resourceType)) { + IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance(); + res.setId(id); + retVal.add(res); + } + } + } + return retVal; + } } diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index efeeb8c4b38..0be0fe9925a 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.provider.dstu2; * 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. @@ -19,27 +19,32 @@ package ca.uhn.fhir.rest.server.provider.dstu2; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.util.*; - -import org.hl7.fhir.instance.model.api.*; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.BundleInclusionRule; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Link; -import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.model.valueset.*; +import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; +import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; +import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.util.ResourceReferenceInfo; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { private Bundle myBundle; @@ -282,44 +287,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { return false; } - @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, - BundleTypeEnum theBundleType) { - myBundle = new Bundle(); - - myBundle.setId(UUID.randomUUID().toString()); - - ResourceMetadataKeyEnum.PUBLISHED.put(myBundle, InstantDt.withCurrentTime()); - - myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase); - myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl); - myBundle.getTypeElement().setValueAsString(theBundleType.getCode()); - - if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) { - for (IBaseResource nextBaseRes : theResources) { - IResource next = (IResource) nextBaseRes; - Entry nextEntry = myBundle.addEntry(); - - nextEntry.setResource(next); - if (next.getId().isEmpty()) { - nextEntry.getRequest().setMethod(HTTPVerbEnum.POST); - } else { - nextEntry.getRequest().setMethod(HTTPVerbEnum.PUT); - if (next.getId().isAbsolute()) { - nextEntry.getRequest().setUrl(next.getId()); - } else { - String resourceType = myContext.getResourceDefinition(next).getName(); - nextEntry.getRequest().setUrl(new IdDt(theServerBase, resourceType, next.getId().getIdPart(), next.getId().getVersionIdPart()).getValue()); - } - } - } - } else { - addResourcesForSearch(theResources); - } - - myBundle.getTotalElement().setValue(theTotalResults); - } - @Override public void initializeWithBundleResource(IBaseResource theBundle) { myBundle = (Bundle) theBundle; diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 816f7f06dea..b33989faa47 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -41,6 +41,16 @@ only system level export is currently supported but others will follow. ]]> + + New Feature: + Support for ElasticSearch has been added to the JPA server directly (i.e. without needing a separate + module) and a new class called "ElasticsearchHibernatePropertiesBuilder" has been added to facilitate + the creation of relevant properties. Instructions have been added to the hapi-fhir-jpaserver-starter + project to get started with Elasticsearch. It is likely we will switch our default recommendation + to Elastic in the future. + ]]> + Improvement: @@ -253,10 +263,14 @@ The JPA server failed to find codes defined in not-present codesystems in some cases, and reported that the CodeSystem did not exist. This has been corrected. - - Support for ElasticSearch has been added to the JPA server directly (i.e. without needing a separate - module) and a new class called "ElasticsearchHibernatePropertiesBuilder" has been added to facilitate - the creation of relevant properties. + + The method + IVersionSpecificBundleFactory#initializeBundleFromResourceList + ]]> + has been deprecated, as it provided duplicate functionality to other methods and had an + outdated argument list based on the Bundle needs in DSTU1. We are not aware of any + public use of this API, please let us know if this deprecation causes any issues. From 0bee7a7b5399b5f90a69086b577450f6f307df18 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 30 Sep 2019 10:12:49 -0400 Subject: [PATCH 19/23] Test fix --- ...tionInterceptorResourceProviderR4Test.java | 64 ++++++++++++++++++- .../rest/server/Dstu2_1BundleFactory.java | 2 +- .../hapi/rest/server/Dstu3BundleFactory.java | 2 +- .../r4/hapi/rest/server/R4BundleFactory.java | 2 +- .../r5/hapi/rest/server/R5BundleFactory.java | 2 +- 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java index 962409f2309..0e87ed5be21 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java @@ -102,7 +102,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource } @Test - public void testCreateConditional() { + public void testUpdateConditional() { Patient patient = new Patient(); patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); @@ -150,6 +150,68 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource } + @Test + public void testCreateConditionalViaTransaction() { + ourRestServer.getInterceptorService().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().createConditional().resourcesOfType("Patient").andThen() + .build(); + } + }); + + // Create a patient (allowed) + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); + patient.addName().setFamily("Tester").addGiven("Raghad"); + + Bundle request = new Bundle(); + request.setType(Bundle.BundleType.TRANSACTION); + request.addEntry() + .setResource(patient) + .getRequest() + .setMethod(Bundle.HTTPVerb.POST) + .setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|100"); + Bundle response = ourClient.transaction().withBundle(request).execute(); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response)); + + try { + ourClient.update().resource(patient).execute(); + fail(); + } catch (ForbiddenOperationException e) { + assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); + } + } + + // Create a patient (blocked) + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); + patient.addName().setFamily("Tester").addGiven("Raghad"); + + Bundle request = new Bundle(); + request.setType(Bundle.BundleType.TRANSACTION); + request.addEntry() + .setResource(patient) + .getRequest() + .setMethod(Bundle.HTTPVerb.POST) + .setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|100"); + Bundle response = ourClient.transaction().withBundle(request).execute(); + ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response)); + + try { + ourClient.update().resource(patient).execute(); + fail(); + } catch (ForbiddenOperationException e) { + assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); + } + } + + } + + @Test public void testReadInTransaction() { diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java index cf5e7800631..4e9294e2924 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java @@ -165,7 +165,7 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory { List addedResourcesThisPass = new ArrayList(); for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { continue; } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java index 1580395de52..d4f4fb106b0 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java @@ -169,7 +169,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { List addedResourcesThisPass = new ArrayList(); for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { continue; } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java index 98a0f7575e9..63aafe2e47e 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java @@ -169,7 +169,7 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory { List addedResourcesThisPass = new ArrayList(); for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { continue; } diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java index 938eb5c8365..7e72bcb6bd6 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java @@ -169,7 +169,7 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory { List addedResourcesThisPass = new ArrayList(); for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { continue; } From 5509478566f5146a9f57141c679ff80b26eb5e78 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Mon, 30 Sep 2019 10:13:09 -0400 Subject: [PATCH 20/23] License header update --- .../server/provider/BaseLastNProvider.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java index 396b84ede9a..fca6f754613 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/BaseLastNProvider.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.rest.server.provider; +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2019 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.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.Constants; From dbd5331f5a7a9230416a3aa102752a0696174fd4 Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Mon, 30 Sep 2019 10:25:29 -0400 Subject: [PATCH 21/23] Support for ancestor filter with IN operator for LOINC has been added. --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 35 ++++++++++++------- .../jpa/term/TerminologySvcImplDstu3Test.java | 2 -- src/changes/changes.xml | 4 +++ 3 files changed, 27 insertions(+), 14 deletions(-) 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 f1a8d5b49f3..daef9c67328 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 @@ -990,22 +990,33 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - TermConcept code = findCode(theSystem, theFilter.getValue()) - .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theFilter.getValue())); + addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter.getProperty(), theFilter.getValue()); + } - logFilteringValueOnProperty(theFilter.getValue(), theFilter.getProperty()); - theBool.must(theQb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); + private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getAncestorTerms(theSystem, theProperty, theValue); + theBool.must(new TermsQuery(terms)); } private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - throw new UnsupportedOperationException(); - // FIXME: DM 2019-09-30 - Working on this presently. - // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor; see #1512 in GitHub. - // FIXME: DM 2019-09-26 - Once implemented, fix changelog entry for #1454 in changes.xml -// String[] values = theFilter.getValue().split(","); -// for (String value : values) { -// logFilteringValueOnProperty(value, theFilter.getProperty()); -// } + String[] values = theFilter.getValue().split(","); + List terms = new ArrayList<>(); + for (String value : values) { + terms.addAll(getAncestorTerms(theSystem, theFilter.getProperty(), value)); + } + theBool.must(new TermsQuery(terms)); + } + + private List getAncestorTerms(String theSystem, String theProperty, String theValue) { + List retVal = new ArrayList<>(); + + TermConcept code = findCode(theSystem, theValue) + .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theValue)); + + retVal.add(new Term("myParentPids", "" + code.getId())); + logFilteringValueOnProperty(theValue, theProperty); + + return retVal; } private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { 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 e2153aa4e63..9d2d785b5ee 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 @@ -619,7 +619,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { assertThat(codes, containsInAnyOrder("50015-7", "43343-3", "43343-4", "47239-9")); } - @Ignore("Not yet implemented; see #1512 in GitHub.") @Test public void testExpandValueSetPropertyFilterLoincAncestorWithExcludeAndIn() { createLoincSystemWithSomeCodes(); @@ -707,7 +706,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { assertEquals(0, outcome.getExpansion().getContains().size()); } - @Ignore("Not yet implemented; see #1512 in GitHub.") @Test public void testExpandValueSetPropertyFilterLoincAncestorWithIncludeAndIn() { createLoincSystemWithSomeCodes(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 816f7f06dea..b72f400977b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -249,6 +249,10 @@ =]]> or in]]> operators. At present, the ancestor]]> filter can only be used with the =]]> operator. + + Support for the LOINC ancestor]]> filter with the in]]> + operator has been added. + The JPA server failed to find codes defined in not-present codesystems in some cases, and reported that the CodeSystem did not exist. This has been corrected. From 396f358f2d62518ae6f3d0a0c13a6ec8899bfa6e Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Mon, 30 Sep 2019 14:40:31 -0400 Subject: [PATCH 22/23] Support for long concept property values has been added. --- .../fhir/jpa/entity/TermConceptProperty.java | 40 ++++++++++++++++++- .../term/TerminologyLoaderSvcLoincTest.java | 4 +- .../tasks/HapiFhirJpaMigrationTasks.java | 4 +- src/changes/changes.xml | 5 +++ 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index 671505733c1..d83bf36c1f5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -29,6 +29,7 @@ import org.hibernate.validator.constraints.NotBlank; import javax.annotation.Nonnull; import javax.persistence.*; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; @@ -62,9 +63,11 @@ public class TermConceptProperty implements Serializable { @Column(name = "PROP_KEY", nullable = false, length = MAX_LENGTH) @NotBlank private String myKey; - // FIXME: DM 2019-09-13 - We presently truncate down to 500. The longest value for EXTERNAL_COPYRIGHT_NOTICE is 2,597 so we should use a LOB instead of a String. @Column(name = "PROP_VAL", nullable = true, length = MAX_LENGTH) private String myValue; + @Column(name = "PROP_VAL_LOB") + @Lob() + private byte[] myValueLob; @Column(name = "PROP_TYPE", nullable = false, length = MAX_PROPTYPE_ENUM_LENGTH) private TermConceptPropertyTypeEnum myType; @@ -145,6 +148,9 @@ public class TermConceptProperty implements Serializable { * property, and the code for a {@link TermConceptPropertyTypeEnum#CODING coding} property. */ public String getValue() { + if (hasValueLob()) { + return getValueLobAsString(); + } return myValue; } @@ -153,10 +159,40 @@ public class TermConceptProperty implements Serializable { * property, and the code for a {@link TermConceptPropertyTypeEnum#CODING coding} property. */ public TermConceptProperty setValue(String theValue) { + if (theValue.length() > MAX_LENGTH) { + setValueLob(theValue); + } else { + myValueLob = null; + } myValue = left(theValue, MAX_LENGTH); return this; } + public boolean hasValueLob() { + if (myValueLob != null && myValueLob.length > 0) { + return true; + } + return false; + } + + public byte[] getValueLob() { + return myValueLob; + } + + public String getValueLobAsString() { + return new String(myValueLob, StandardCharsets.UTF_8); + } + + public TermConceptProperty setValueLob(byte[] theValueLob) { + myValueLob = theValueLob; + return this; + } + + public TermConceptProperty setValueLob(String theValueLob) { + myValueLob = theValueLob.getBytes(StandardCharsets.UTF_8); + return this; + } + public TermConceptProperty setCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { myCodeSystemVersion = theCodeSystemVersion; return this; @@ -171,7 +207,7 @@ public class TermConceptProperty implements Serializable { public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("key", myKey) - .append("value", myValue) + .append("value", getValue()) .toString(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java index b45ab57fbde..523c770757d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java @@ -90,9 +90,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest { // LOINC code with 3rd party copyright code = concepts.get("47239-9"); - // FIXME: DM 2019-09-13 - We presently truncate down to 500. The longest value for EXTERNAL_COPYRIGHT_NOTICE is 2,597 so we should use a LOB instead of a String. -// String expectedExternalCopyrightNotice = "Copyright © 2006 World Health Organization. Used with permission. Publications of the World Health Organization can be obtained from WHO Press, World Health Organization, 20 Avenue Appia, 1211 Geneva 27, Switzerland (tel: +41 22 791 2476; fax: +41 22 791 4857; email: bookorders@who.int). Requests for permission to reproduce or translate WHO publications – whether for sale or for noncommercial distribution – should be addressed to WHO Press, at the above address (fax: +41 22 791 4806; email: permissions@who.int). The designations employed and the presentation of the material in this publication do not imply the expression of any opinion whatsoever on the part of the World Health Organization concerning the legal status of any country, territory, city or area or of its authorities, or concerning the delimitation of its frontiers or boundaries. Dotted lines on maps represent approximate border lines for which there may not yet be full agreement. The mention of specific companies or of certain manufacturers’ products does not imply that they are endorsed or recommended by the World Health Organization in preference to others of a similar nature that are not mentioned. Errors and omissions excepted, the names of proprietary products are distinguished by initial capital letters. All reasonable precautions have been taken by WHO to verify the information contained in this publication. However, the published material is being distributed without warranty of any kind, either express or implied. The responsibility for the interpretation and use of the material lies with the reader. In no event shall the World Health Organization be liable for damages arising from its use."; - String expectedExternalCopyrightNotice = "Copyright © 2006 World Health Organization. Used with permission. Publications of the World Health Organization can be obtained from WHO Press, World Health Organization, 20 Avenue Appia, 1211 Geneva 27, Switzerland (tel: +41 22 791 2476; fax: +41 22 791 4857; email: bookorders@who.int). Requests for permission to reproduce or translate WHO publications – whether for sale or for noncommercial distribution – should be addressed to WHO Press, at the above address (fax: +41 22 791 4806; email: perm"; + String expectedExternalCopyrightNotice = "Copyright © 2006 World Health Organization. Used with permission. Publications of the World Health Organization can be obtained from WHO Press, World Health Organization, 20 Avenue Appia, 1211 Geneva 27, Switzerland (tel: +41 22 791 2476; fax: +41 22 791 4857; email: bookorders@who.int). Requests for permission to reproduce or translate WHO publications – whether for sale or for noncommercial distribution – should be addressed to WHO Press, at the above address (fax: +41 22 791 4806; email: permissions@who.int). The designations employed and the presentation of the material in this publication do not imply the expression of any opinion whatsoever on the part of the World Health Organization concerning the legal status of any country, territory, city or area or of its authorities, or concerning the delimitation of its frontiers or boundaries. Dotted lines on maps represent approximate border lines for which there may not yet be full agreement. The mention of specific companies or of certain manufacturers’ products does not imply that they are endorsed or recommended by the World Health Organization in preference to others of a similar nature that are not mentioned. Errors and omissions excepted, the names of proprietary products are distinguished by initial capital letters. All reasonable precautions have been taken by WHO to verify the information contained in this publication. However, the published material is being distributed without warranty of any kind, either express or implied. The responsibility for the interpretation and use of the material lies with the reader. In no event shall the World Health Organization be liable for damages arising from its use."; assertEquals(expectedExternalCopyrightNotice, code.getStringProperty("EXTERNAL_COPYRIGHT_NOTICE")); // Answer list diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 3817e196b70..876ab67b838 100644 --- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -29,7 +29,6 @@ import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks; import ca.uhn.fhir.jpa.model.entity.*; import ca.uhn.fhir.util.VersionEnum; -import javax.persistence.Index; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -165,6 +164,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // TermValueSetConceptDesignation version.startSectionWithMessage("Processing table: TRM_VALUESET_C_DESIGNATION"); version.onTable("TRM_VALUESET_C_DESIGNATION").modifyColumn("VAL").nonNullable().withType(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, 2000); + + version.startSectionWithMessage("Processing table: TRM_CONCEPT_PROPERTY"); + version.onTable("TRM_CONCEPT_PROPERTY").addColumn("PROP_VAL_LOB").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.CLOB); } protected void init400() { diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 9d419660505..d28d3ec3a18 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -276,6 +276,11 @@ outdated argument list based on the Bundle needs in DSTU1. We are not aware of any public use of this API, please let us know if this deprecation causes any issues. + + Support for concept property values with a length exceeding 500 characters has been added in the terminology + tables. In particular, this was added to facilitate the LOINC EXTERNAL_COPYRIGHT_NOTICE property, for which + values can be quite long. + From 066c9a7fb76c9d700024d698dcfcd9f4b84718d7 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 30 Sep 2019 15:07:23 -0400 Subject: [PATCH 23/23] Improvements to AutohrizationInterceptor create handling --- .../model/api/ResourceMetadataKeyEnum.java | 3 +- .../java/ca/uhn/fhir/util/BundleUtil.java | 32 ++- .../okhttp/GenericOkHttpClientDstu2Test.java | 47 ----- .../fhir/rest/client/impl/GenericClient.java | 33 ++- .../client/GenericJaxRsClientDstu2Test.java | 4 +- .../client/GenericJaxRsClientDstu3Test.java | 4 +- .../dao/MetadataKeyCurrentlyReindexing.java | 8 - ...tionInterceptorResourceProviderR4Test.java | 64 ++++-- .../fhir/rest/api/server/RequestDetails.java | 12 ++ .../auth/IAuthRuleBuilderRule.java | 9 + .../server/interceptor/auth/RuleBuilder.java | 8 + .../server/interceptor/auth/RuleImplOp.java | 19 ++ .../server/interceptor/auth/RuleOpEnum.java | 1 + .../rest/server/Dstu2_1BundleFactory.java | 84 -------- .../provider/dstu2/Dstu2BundleFactory.java | 80 ------- .../rest/client/GenericClientDstu2Test.java | 8 +- .../hapi/rest/server/Dstu3BundleFactory.java | 87 -------- .../dstu2hl7org/Dstu2Hl7OrgBundleFactory.java | 195 +++++++----------- .../client/GenericClientDstu2Hl7OrgTest.java | 4 +- .../r4/hapi/rest/server/R4BundleFactory.java | 87 -------- .../r5/hapi/rest/server/R5BundleFactory.java | 87 -------- .../provider/dstu2/Dstu2BundleFactory.java | 80 ------- src/changes/changes.xml | 6 + 23 files changed, 250 insertions(+), 712 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index bdb2447ec9a..e84549679ad 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.io.Serializable; @@ -629,5 +630,5 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } } - + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java index ba28c567d34..d51b55dd931 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java @@ -46,12 +46,14 @@ public class BundleUtil { private final RequestTypeEnum myRequestType; private final IBaseResource myResource; private final String myUrl; + private final String myConditionalUrl; - BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource) { + BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource, String theConditionalUrl) { super(); myRequestType = theRequestType; myUrl = theUrl; myResource = theResource; + myConditionalUrl = theConditionalUrl; } public RequestTypeEnum getRequestType() { @@ -62,6 +64,10 @@ public class BundleUtil { return myResource; } + public String getConditionalUrl() { + return myConditionalUrl; + } + public String getUrl() { return myUrl; } @@ -190,19 +196,21 @@ public class BundleUtil { BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource"); BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request"); BaseRuntimeElementCompositeDefinition requestElem = (BaseRuntimeElementCompositeDefinition) requestChild.getChildByName("request"); - BaseRuntimeChildDefinition urlChild = requestElem.getChildByName("url"); + BaseRuntimeChildDefinition requestUrlChild = requestElem.getChildByName("url"); + BaseRuntimeChildDefinition requestIfNoneExistChild = requestElem.getChildByName("ifNoneExist"); BaseRuntimeChildDefinition methodChild = requestElem.getChildByName("method"); for (IBase nextEntry : entries) { IBaseResource resource = null; String url = null; RequestTypeEnum requestType = null; + String conditionalUrl = null; for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) { resource = (IBaseResource) next; } for (IBase nextRequest : requestChild.getAccessor().getValues(nextEntry)) { - for (IBase nextUrl : urlChild.getAccessor().getValues(nextRequest)) { + for (IBase nextUrl : requestUrlChild.getAccessor().getValues(nextRequest)) { url = ((IPrimitiveType) nextUrl).getValueAsString(); } for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) { @@ -211,13 +219,29 @@ public class BundleUtil { requestType = RequestTypeEnum.valueOf(methodString); } } + + if (requestType != null) { + //noinspection EnumSwitchStatementWhichMissesCases + switch (requestType) { + case PUT: + conditionalUrl = url != null && url.contains("?") ? url : null; + break; + case POST: + List ifNoneExistReps = requestIfNoneExistChild.getAccessor().getValues(nextRequest); + if (ifNoneExistReps.size() > 0) { + IPrimitiveType ifNoneExist = (IPrimitiveType) ifNoneExistReps.get(0); + conditionalUrl = ifNoneExist.getValueAsString(); + } + break; + } + } } /* * All 3 might be null - That's ok because we still want to know the * order in the original bundle. */ - retVal.add(new BundleEntryParts(requestType, url, resource)); + retVal.add(new BundleEntryParts(requestType, url, resource, conditionalUrl)); } diff --git a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java index 20ca82fdc29..34a549c8226 100644 --- a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java +++ b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java @@ -1540,53 +1540,6 @@ public class GenericOkHttpClientDstu2Test { assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } - @Test - public void testTransactionWithListOfResources() throws Exception { - ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); - resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; - - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); - - List input = new ArrayList(); - - Patient p1 = new Patient(); // No ID - p1.addName().addFamily("PATIENT1"); - input.add(p1); - - Patient p2 = new Patient(); // Yes ID - p2.addName().addFamily("PATIENT2"); - p2.setId("Patient/2"); - input.add(p2); - - List response = client.transaction() - .withResources(input) - .encodedJson() - .execute(); - - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); - assertEquals(2, response.size()); - - String requestString = ourRequestBodyString; - ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString); - assertEquals(2, requestBundle.getEntry().size()); - assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); - assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod()); - assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); - - p1 = (Patient) response.get(0); - assertEquals(new IdDt("Patient/1/_history/1"), p1.getId().toUnqualified()); - // assertEquals("PATIENT1", p1.getName().get(0).getFamily().get(0).getValue()); - - p2 = (Patient) response.get(1); - assertEquals(new IdDt("Patient/2/_history/2"), p2.getId().toUnqualified()); - // assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue()); - } - @Test public void testTransactionWithString() throws Exception { ca.uhn.fhir.model.dstu2.resource.Bundle req = new ca.uhn.fhir.model.dstu2.resource.Bundle(); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java index 6aebc0982f0..f357a627ec3 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java @@ -22,11 +22,14 @@ package ca.uhn.fhir.rest.client.impl; import ca.uhn.fhir.context.*; import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; +import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.*; @@ -2046,7 +2049,35 @@ public class GenericClient extends BaseClient implements IGenericClient { @Override public ITransactionTyped> withResources(List theResources) { Validate.notNull(theResources, "theResources must not be null"); - return new TransactionExecutable>(theResources); + + for (IBaseResource next : theResources) { + String entryMethod = null; + if (next instanceof IResource) { + BundleEntryTransactionMethodEnum entryMethodEnum = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) next); + if (entryMethodEnum != null) { + entryMethod = entryMethodEnum.getCode(); + } + } else { + entryMethod = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IAnyResource) next); + } + + if (isBlank(entryMethod)) { + if (isBlank(next.getIdElement().getValue())) { + entryMethod = "POST"; + } else { + entryMethod = "PUT"; + } + if (next instanceof IResource) { + ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put((IResource) next, BundleEntryTransactionMethodEnum.valueOf(entryMethod)); + } else { + ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put((IAnyResource) next, entryMethod); + } + + } + + } + + return new TransactionExecutable<>(theResources); } } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java index c45919cc76d..62d057015b6 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java @@ -1722,7 +1722,7 @@ public class GenericJaxRsClientDstu2Test { Patient p2 = new Patient(); // Yes ID p2.addName().addFamily("PATIENT2"); - p2.setId("Patient/2"); + p2.setId("http://example.com/Patient/2"); input.add(p2); @@ -1740,7 +1740,7 @@ public class GenericJaxRsClientDstu2Test { assertEquals(2, requestBundle.getEntry().size()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod()); - assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); + assertEquals("http://example.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl()); p1 = (Patient) response.get(0); assertEquals(new IdDt("Patient/1/_history/1"), p1.getId().toUnqualified()); diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java index b7d1ffb7dbe..48a03513567 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java @@ -1758,7 +1758,7 @@ public class GenericJaxRsClientDstu3Test { Patient p2 = new Patient(); // Yes ID p2.addName().setFamily("PATIENT2"); - p2.setId("Patient/2"); + p2.setId("http://example.com/Patient/2"); input.add(p2); //@formatter:off @@ -1776,7 +1776,7 @@ public class GenericJaxRsClientDstu3Test { assertEquals(2, requestBundle.getEntry().size()); assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod()); assertEquals(HTTPVerb.PUT, requestBundle.getEntry().get(1).getRequest().getMethod()); - assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); + assertEquals("http://example.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl()); p1 = (Patient) response.get(0); assertEquals(new IdType("Patient/1/_history/1"), p1.getIdElement()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java index 9400c527b95..0f4881d97e3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java @@ -42,14 +42,6 @@ public final class MetadataKeyCurrentlyReindexing extends ResourceMetadataKeySup return (Boolean) theResource.getResourceMetadata().get(IDao.CURRENTLY_REINDEXING); } - public Boolean get(IBaseResource theResource) { - if (theResource instanceof IAnyResource) { - return get((IAnyResource) theResource); - } else { - return get((IResource) theResource); - } - } - @Override public void put(IAnyResource theResource, Boolean theObject) { theResource.setUserData(IDao.CURRENTLY_REINDEXING.name(), theObject); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java index 0e87ed5be21..f8c80672d95 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java @@ -4,21 +4,20 @@ import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; -import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; -import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; -import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; +import ca.uhn.fhir.rest.server.interceptor.auth.*; import ca.uhn.fhir.util.TestUtil; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Observation.ObservationStatus; @@ -156,7 +155,22 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() + .allow().create().resourcesOfType("Patient").withAnyId().withTester(new IAuthRuleTester() { + @Override + public boolean matches(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IIdType theInputResourceId, IBaseResource theInputResource) { + if (theInputResource instanceof Patient) { + Patient patient = (Patient) theInputResource; + return patient + .getIdentifier() + .stream() + .filter(t-> "http://uhn.ca/mrns".equals(t.getSystem())) + .anyMatch(t-> "100".equals(t.getValue())); + } + return false; + } + }).andThen() .allow().createConditional().resourcesOfType("Patient").andThen() + .allow().transaction().withAnyOperation().andApplyNormalRules().andThen() .build(); } }); @@ -177,19 +191,16 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource Bundle response = ourClient.transaction().withBundle(request).execute(); ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response)); - try { - ourClient.update().resource(patient).execute(); - fail(); - } catch (ForbiddenOperationException e) { - assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); - } + // Subsequent calls also shouldn't fail + ourClient.transaction().withBundle(request).execute(); + ourClient.transaction().withBundle(request).execute(); } - // Create a patient (blocked) + // Create a patient with wrong identifier (blocked) { Patient patient = new Patient(); - patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100"); - patient.addName().setFamily("Tester").addGiven("Raghad"); + patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("101"); + patient.addName().setFamily("Tester").addGiven("Fozzie"); Bundle request = new Bundle(); request.setType(Bundle.BundleType.TRANSACTION); @@ -197,12 +208,31 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource .setResource(patient) .getRequest() .setMethod(Bundle.HTTPVerb.POST) - .setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|100"); - Bundle response = ourClient.transaction().withBundle(request).execute(); - ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response)); + .setIfNoneExist("Patient?identifier=http://uhn.ca/mrns|101"); try { - ourClient.update().resource(patient).execute(); + ourClient.transaction().withBundle(request).execute(); + fail(); + } catch (ForbiddenOperationException e) { + assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); + } + } + + // Create an organization (blocked) + { + Organization patient = new Organization(); + patient.setName("FOO"); + + Bundle request = new Bundle(); + request.setType(Bundle.BundleType.TRANSACTION); + request.addEntry() + .setResource(patient) + .getRequest() + .setMethod(Bundle.HTTPVerb.POST) + .setIfNoneExist("Organization?name=FOO"); + + try { + ourClient.transaction().withBundle(request).execute(); fail(); } catch (ForbiddenOperationException e) { assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage()); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java index 42205b1d554..f974423e357 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java @@ -73,6 +73,7 @@ public abstract class RequestDetails { private Map myUserData; private IBaseResource myResource; private String myRequestId; + private String myFixedConditionalUrl; /** * Constructor @@ -81,6 +82,14 @@ public abstract class RequestDetails { myInterceptorBroadcaster = theInterceptorBroadcaster; } + public String getFixedConditionalUrl() { + return myFixedConditionalUrl; + } + + public void setFixedConditionalUrl(String theFixedConditionalUrl) { + myFixedConditionalUrl = theFixedConditionalUrl; + } + public String getRequestId() { return myRequestId; } @@ -152,6 +161,9 @@ public abstract class RequestDetails { * @return Returns the conditional URL if this request has one, or null otherwise */ public String getConditionalUrl(RestOperationTypeEnum theOperationType) { + if (myFixedConditionalUrl != null) { + return myFixedConditionalUrl; + } switch (theOperationType) { case CREATE: String retVal = this.getHeader(Constants.HEADER_IF_NONE_EXIST); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java index 56bf76e3f4f..a7a28fb26c6 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java @@ -108,8 +108,17 @@ public interface IAuthRuleBuilderRule { */ IAuthRuleBuilderRuleOp write(); + /** + * This rule specifically allows a user to perform a FHIR create, but not an update or other write operations + * + * @see #write() + * @since 4.1.0 + */ + IAuthRuleBuilderRuleOp create(); + /** * Allow a GraphQL query */ IAuthRuleBuilderGraphQL graphQL(); + } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java index c99b6105312..163839b6106 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java @@ -253,6 +253,14 @@ public class RuleBuilder implements IAuthRuleBuilder { return myWriteRuleBuilder; } + @Override + public IAuthRuleBuilderRuleOp create() { + if (myWriteRuleBuilder == null) { + myWriteRuleBuilder = new RuleBuilderRuleOp(RuleOpEnum.CREATE); + } + return myWriteRuleBuilder; + } + @Override public IAuthRuleBuilderGraphQL graphQL() { return new RuleBuilderGraphQL(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java index 396250e0d24..6962fc6403c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java @@ -207,6 +207,19 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { return null; } break; + case CREATE: + if (theInputResource == null && theInputResourceId == null) { + return null; + } + if (theOperation == RestOperationTypeEnum.CREATE) { + appliesToResource = theInputResource; + if (theInputResourceId != null) { + appliesToResourceId = Collections.singletonList(theInputResourceId); + } + } else { + return null; + } + break; case DELETE: if (theOperation == RestOperationTypeEnum.DELETE) { if (myAppliesToDeleteCascade != (thePointcut == Pointcut.STORAGE_CASCADE_DELETE)) { @@ -288,7 +301,13 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { } } + String previousFixedConditionalUrl = theRequestDetails.getFixedConditionalUrl(); + theRequestDetails.setFixedConditionalUrl(nextPart.getConditionalUrl()); + Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, inputResourceId, null, thePointcut); + + theRequestDetails.setFixedConditionalUrl(previousFixedConditionalUrl); + if (newVerdict == null) { continue; } else if (verdict == null) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java index 561012eddb6..9ef29f0cc5c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java @@ -33,5 +33,6 @@ enum RuleOpEnum { DELETE, OPERATION, GRAPHQL, + CREATE, PATCH } diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java index 4e9294e2924..c45f1c0a274 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java @@ -51,90 +51,6 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource nextBaseRes : theResult) { - Resource next = (Resource) nextBaseRes; - Set containedIds = new HashSet(); - if (next instanceof DomainResource) { - for (Resource nextContained : ((DomainResource) next).getContained()) { - if (nextContained.getIdElement().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } - } - - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); - do { - List addedResourcesThisPass = new ArrayList(); - - for (IBaseReference nextRef : references) { - IAnyResource nextRes = (IAnyResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IIdType id = nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - - // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IAnyResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - BundleEntryComponent entry = myBundle.addEntry().setResource(next); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getId()); - } - - String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb); - entry.getRequest().getUrlElement().setValue(next.getId()); - } - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } - @Override public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { ensureBundle(); diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index a77c9f42d86..eecd8140b5d 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -50,86 +50,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource nextBaseRes : theResult) { - IResource next = (IResource) nextBaseRes; - Set containedIds = new HashSet(); - for (IResource nextContained : next.getContained().getContainedResources()) { - if (nextContained.getId().isEmpty() == false) { - containedIds.add(nextContained.getId().getValue()); - } - } - - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class); - do { - List addedResourcesThisPass = new ArrayList(); - - for (BaseResourceReferenceDt nextRef : references) { - IResource nextRes = (IResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getId().hasIdPart()) { - if (containedIds.contains(nextRes.getId().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IdDt id = nextRes.getId(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - - // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - Entry entry = myBundle.addEntry().setResource(next); - if (next.getId().hasBaseUrl()) { - entry.setFullUrl(next.getId().getValue()); - } - BundleEntryTransactionMethodEnum httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb.getCode()); - } - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - Entry entry = myBundle.addEntry(); - entry.setResource((IResource) next).getSearch().setMode(SearchEntryModeEnum.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } - @Override public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { ensureBundle(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index 794567b09f0..fede33ce24a 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -2231,25 +2231,27 @@ public class GenericClientDstu2Test { Patient p2 = new Patient(); // Yes ID p2.addName().addFamily("PATIENT2"); - p2.setId("Patient/2"); + p2.setId("http://foo.com/Patient/2"); input.add(p2); //@formatter:off List response = client.transaction() .withResources(input) .encodedJson() + .prettyPrint() .execute(); //@formatter:on - assertEquals("http://example.com/fhir", capt.getValue().getURI().toString()); + assertEquals("http://example.com/fhir?_pretty=true", capt.getValue().getURI().toString()); assertEquals(2, response.size()); String requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent()); + ourLog.info(requestString); ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString); assertEquals(2, requestBundle.getEntry().size()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod()); - assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); + assertEquals("http://foo.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl()); assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", "")); p1 = (Patient) response.get(0); diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java index d4f4fb106b0..8858d27fa7e 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java @@ -51,93 +51,6 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource nextBaseRes : theResult) { - Resource next = (Resource) nextBaseRes; - Set containedIds = new HashSet(); - if (next instanceof DomainResource) { - for (Resource nextContained : ((DomainResource) next).getContained()) { - if (nextContained.getIdElement().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } - } - - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); - do { - List addedResourcesThisPass = new ArrayList(); - - for (IBaseReference nextRef : references) { - IAnyResource nextRes = (IAnyResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IIdType id = nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - - // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IAnyResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - BundleEntryComponent entry = myBundle.addEntry().setResource(next); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getId()); - } - - String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb); - entry.getRequest().getUrlElement().setValue(next.getId()); - } - if ("DELETE".equals(httpVerb)) { - entry.setResource(null); - } - - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } @Override public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java index 9ce9fcbbb2f..ace6e1ae9e4 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java @@ -23,17 +23,16 @@ package ca.uhn.fhir.rest.server.provider.dstu2hl7org; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; +import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.util.ResourceReferenceInfo; -import org.hl7.fhir.dstu2.model.Bundle; +import org.hl7.fhir.dstu2.model.*; import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent; import org.hl7.fhir.dstu2.model.Bundle.SearchEntryMode; -import org.hl7.fhir.dstu2.model.IdType; -import org.hl7.fhir.dstu2.model.InstantType; -import org.hl7.fhir.dstu2.model.Resource; import org.hl7.fhir.instance.model.api.*; import java.util.*; @@ -51,8 +50,12 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); + @Override + public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, + BundleInclusionRule theBundleInclusionRule, Set theIncludes) { + ensureBundle(); + + List includedResources = new ArrayList(); Set addedResourceIds = new HashSet(); for (IBaseResource next : theResult) { @@ -61,22 +64,28 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { } } - for (IBaseResource nextBaseRes : theResult) { - IDomainResource next = (IDomainResource) nextBaseRes; + for (IBaseResource next : theResult) { + Set containedIds = new HashSet(); - for (IBaseResource nextContained : next.getContained()) { - if (nextContained.getIdElement().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); + + if (next instanceof DomainResource) { + for (Resource nextContained : ((DomainResource) next).getContained()) { + if (isNotBlank(nextContained.getId())) { + containedIds.add(nextContained.getId()); + } } } - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, - IBaseReference.class); + List references = myContext.newTerser().getAllResourceReferences(next); do { - List addedResourcesThisPass = new ArrayList(); + List addedResourcesThisPass = new ArrayList(); - for (IBaseReference nextRef : references) { - IBaseResource nextRes = (IBaseResource) nextRef.getResource(); + for (ResourceReferenceInfo nextRefInfo : references) { + if (theBundleInclusionRule != null && !theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) { + continue; + } + + IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource(); if (nextRes != null) { if (nextRes.getIdElement().hasIdPart()) { if (containedIds.contains(nextRes.getIdElement().getValue())) { @@ -99,119 +108,62 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { } } - // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IBaseResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, - IBaseReference.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); - populateBundleEntryFullUrl(next, entry); - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - populateBundleEntryFullUrl(next, entry); - } - } - - @Override - public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, - BundleInclusionRule theBundleInclusionRule, Set theIncludes) { - ensureBundle(); - - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource next : theResult) { - - List contained; - if (next instanceof IDomainResource) { - IDomainResource nextDomain = (IDomainResource) next; - contained = nextDomain.getContained(); - } else { - contained = Collections.emptyList(); - } - - Set containedIds = new HashSet(); - for (IAnyResource nextContained : contained) { - if (nextContained.getId().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } - - List references = myContext.newTerser().getAllResourceReferences(next); - do { - List addedResourcesThisPass = new ArrayList(); - - for (ResourceReferenceInfo nextRefInfo : references) { - if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) - continue; - - IBaseResource nextRes = (IBaseResource) nextRefInfo.getResourceReference().getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IdType id = (IdType) nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - includedResources.addAll(addedResourcesThisPass); // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IBaseResource iResource : addedResourcesThisPass) { + references = new ArrayList<>(); + for (IAnyResource iResource : addedResourcesThisPass) { List newReferences = myContext.newTerser().getAllResourceReferences(iResource); references.addAll(newReferences); } } while (references.isEmpty() == false); BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next); - populateBundleEntryFullUrl(next, entry); + Resource nextAsResource = (Resource) next; + IIdType id = populateBundleEntryFullUrl(next, entry); + String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource); + if (httpVerb != null) { + entry.getRequest().getMethodElement().setValueAsString(httpVerb); + if (id != null) { + entry.getRequest().setUrl(id.getValue()); + } + } + if ("DELETE".equals(httpVerb)) { + entry.setResource(null); + } + + // Populate Bundle.entry.response + if (theBundleType != null) { + switch (theBundleType) { + case BATCH_RESPONSE: + case TRANSACTION_RESPONSE: + if ("1".equals(id.getVersionIdPart())) { + entry.getResponse().setStatus("201 Created"); + } else if (isNotBlank(id.getVersionIdPart())) { + entry.getResponse().setStatus("200 OK"); + } + if (isNotBlank(id.getVersionIdPart())) { + entry.getResponse().setEtag(RestfulServerUtils.createEtag(id.getVersionIdPart())); + } + break; + } + } + + // Populate Bundle.entry.search + String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource); + if (searchMode != null) { + entry.getSearch().getModeElement().setValueAsString(searchMode); + } - // BundleEntrySearchModeEnum searchMode = - // ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next); - // if (searchMode != null) { - // entry.getSearch().getModeElement().setValue(searchMode.getCode()); - // } } /* * Actually add the resources to the bundle */ - for (IBaseResource next : includedResources) { - myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + for (IAnyResource next : includedResources) { + BundleEntryComponent entry = myBundle.addEntry(); + entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); + populateBundleEntryFullUrl(next, entry); } } @@ -228,7 +180,7 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { myBundle.setId(UUID.randomUUID().toString()); } - if (myBundle.getMeta().getLastUpdated() == null) { + if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) { InstantType instantType = new InstantType(); instantType.setValueAsString(theLastUpdated.getValueAsString()); myBundle.getMeta().setLastUpdatedElement(instantType); @@ -280,16 +232,19 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { myBundle = (Bundle) theBundle; } - private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) { + IIdType idElement = null; if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().toVersionless().getValue()); + idElement = next.getIdElement(); + entry.setFullUrl(idElement.toVersionless().getValue()); } else { if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) { - IIdType id = next.getIdElement().toVersionless(); - id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); - entry.setFullUrl(id.getValue()); + idElement = next.getIdElement(); + idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName()); + entry.setFullUrl(idElement.toVersionless().getValue()); } } + return idElement; } @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Hl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Hl7OrgTest.java index 92a4a7ef3eb..23ff543aadf 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Hl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Hl7OrgTest.java @@ -629,7 +629,7 @@ public class GenericClientDstu2Hl7OrgTest { Patient p2 = new Patient(); // Yes ID p2.addName().addFamily("PATIENT2"); - p2.setId("Patient/2"); + p2.setId("http://example.com/Patient/2"); input.add(p2); //@formatter:off @@ -647,7 +647,7 @@ public class GenericClientDstu2Hl7OrgTest { assertEquals(2, requestBundle.getEntry().size()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod().name()); assertEquals("PUT", requestBundle.getEntry().get(1).getRequest().getMethod().name()); - assertEquals("Patient/2", requestBundle.getEntry().get(1).getRequest().getUrl()); + assertEquals("http://example.com/Patient/2", requestBundle.getEntry().get(1).getFullUrl()); p1 = (Patient) response.get(0); assertEquals(new IdType("Patient/1/_history/1"), p1.getIdElement().toUnqualified()); diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java index 63aafe2e47e..120352acb97 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java @@ -52,93 +52,6 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource nextBaseRes : theResult) { - Resource next = (Resource) nextBaseRes; - Set containedIds = new HashSet(); - if (next instanceof DomainResource) { - for (Resource nextContained : ((DomainResource) next).getContained()) { - if (nextContained.getIdElement().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } - } - - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); - do { - List addedResourcesThisPass = new ArrayList<>(); - - for (IBaseReference nextRef : references) { - IAnyResource nextRes = (IAnyResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IIdType id = nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - - // Linked resources may themselves have linked resources - references = new ArrayList<>(); - for (IAnyResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - BundleEntryComponent entry = myBundle.addEntry().setResource(next); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getId()); - } - - String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb); - entry.getRequest().getUrlElement().setValue(next.getId()); - } - if ("DELETE".equals(httpVerb)) { - entry.setResource(null); - } - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } - @Override public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { ensureBundle(); diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java index 7e72bcb6bd6..a06d4f98cd3 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java @@ -52,93 +52,6 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource nextBaseRes : theResult) { - Resource next = (Resource) nextBaseRes; - Set containedIds = new HashSet(); - if (next instanceof DomainResource) { - for (Resource nextContained : ((DomainResource) next).getContained()) { - if (nextContained.getIdElement().isEmpty() == false) { - containedIds.add(nextContained.getIdElement().getValue()); - } - } - } - - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class); - do { - List addedResourcesThisPass = new ArrayList<>(); - - for (IBaseReference nextRef : references) { - IAnyResource nextRes = (IAnyResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getIdElement().hasIdPart()) { - if (containedIds.contains(nextRes.getIdElement().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IIdType id = nextRes.getIdElement(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - - // Linked resources may themselves have linked resources - references = new ArrayList<>(); - for (IAnyResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - BundleEntryComponent entry = myBundle.addEntry().setResource(next); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getId()); - } - - String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb); - entry.getRequest().getUrlElement().setValue(next.getId()); - } - if ("DELETE".equals(httpVerb)) { - entry.setResource(null); - } - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - BundleEntryComponent entry = myBundle.addEntry(); - entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } - @Override public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { ensureBundle(); diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index 0be0fe9925a..5c1431a5666 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -55,86 +55,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { myContext = theContext; } - private void addResourcesForSearch(List theResult) { - List includedResources = new ArrayList(); - Set addedResourceIds = new HashSet(); - - for (IBaseResource next : theResult) { - if (next.getIdElement().isEmpty() == false) { - addedResourceIds.add(next.getIdElement()); - } - } - - for (IBaseResource nextBaseRes : theResult) { - IResource next = (IResource) nextBaseRes; - Set containedIds = new HashSet(); - for (IResource nextContained : next.getContained().getContainedResources()) { - if (nextContained.getId().isEmpty() == false) { - containedIds.add(nextContained.getId().getValue()); - } - } - - List references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class); - do { - List addedResourcesThisPass = new ArrayList(); - - for (BaseResourceReferenceDt nextRef : references) { - IResource nextRes = (IResource) nextRef.getResource(); - if (nextRes != null) { - if (nextRes.getId().hasIdPart()) { - if (containedIds.contains(nextRes.getId().getValue())) { - // Don't add contained IDs as top level resources - continue; - } - - IdDt id = nextRes.getId(); - if (id.hasResourceType() == false) { - String resName = myContext.getResourceDefinition(nextRes).getName(); - id = id.withResourceType(resName); - } - - if (!addedResourceIds.contains(id)) { - addedResourceIds.add(id); - addedResourcesThisPass.add(nextRes); - } - - } - } - } - - // Linked resources may themselves have linked resources - references = new ArrayList(); - for (IResource iResource : addedResourcesThisPass) { - List newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class); - references.addAll(newReferences); - } - - includedResources.addAll(addedResourcesThisPass); - - } while (references.isEmpty() == false); - - Entry entry = myBundle.addEntry().setResource(next); - if (next.getId().hasBaseUrl()) { - entry.setFullUrl(next.getId().getValue()); - } - BundleEntryTransactionMethodEnum httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next); - if (httpVerb != null) { - entry.getRequest().getMethodElement().setValueAsString(httpVerb.getCode()); - } - } - - /* - * Actually add the resources to the bundle - */ - for (IBaseResource next : includedResources) { - Entry entry = myBundle.addEntry(); - entry.setResource((IResource) next).getSearch().setMode(SearchEntryModeEnum.INCLUDE); - if (next.getIdElement().hasBaseUrl()) { - entry.setFullUrl(next.getIdElement().getValue()); - } - } - } - @Override public void addResourcesToBundle(List theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set theIncludes) { if (myBundle == null) { diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d28d3ec3a18..1d86c3b8817 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -281,6 +281,12 @@ tables. In particular, this was added to facilitate the LOINC EXTERNAL_COPYRIGHT_NOTICE property, for which values can be quite long. + + The AuthorizationInterceptor has been enhanced so that a user can be authorized to + perform create operations specifically, without authorizing all write operations. Also, + conditional creates can now be authorized even if they are happening inside a FHIR + transaction. +