From 4aea94ccb607bf3100f0f9f0f9db8ade5e1f9066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Primo=C5=BE=20Delopst?= Date: Tue, 24 Sep 2024 16:43:17 +0200 Subject: [PATCH 1/6] Fix forced ID migration for MSSQL (#6290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Primož Delopst --- .../taskdef/ForceIdMigrationFixTask.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java index 07607a90845..543fd942892 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java @@ -89,7 +89,7 @@ public class ForceIdMigrationFixTask extends BaseTask { " set fhir_id = coalesce( " + // case 5. - " trim(fhir_id), " + trimFhirId() + // case 3 " (select f.forced_id from hfj_forced_id f where f.resource_pid = res_id), " @@ -109,6 +109,22 @@ public class ForceIdMigrationFixTask extends BaseTask { } } + private String trimFhirId() { + switch (getDriverType()) { + case MSSQL_2012: + return " LTRIM(RTRIM(fhir_id)), "; + case H2_EMBEDDED: + case DERBY_EMBEDDED: + case MARIADB_10_1: + case MYSQL_5_7: + case POSTGRES_9_4: + case ORACLE_12C: + case COCKROACHDB_21_1: + default: + return " trim(fhir_id), "; + } + } + private String getWhereClauseByDBType() { switch (getDriverType()) { case MSSQL_2012: From 20d3e6bb252eff52def255669ca6f590c2d8db59 Mon Sep 17 00:00:00 2001 From: Martha Mitran Date: Tue, 24 Sep 2024 16:46:38 -0700 Subject: [PATCH 2/6] Fix missing qualifier search for reference search parameters (#6306) * Fix missing qualifier search for reference search parameter. Reuse tests and run them for both indexing enabled and disabled. * Fix edge case where the search parameter has multiple paths. Add a test. Fix some of the compile warnings in a test class. * Fix test setup. --- ...er-does-not-work-reference-parameters.yaml | 6 + .../ResourceLinkPredicateBuilder.java | 13 +- .../FhirResourceDaoR4SearchMissingTest.java | 963 +++++++++--------- .../provider/r4/ResourceProviderR4Test.java | 292 +++--- 4 files changed, 631 insertions(+), 643 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6305-search-missing-qualifier-does-not-work-reference-parameters.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6305-search-missing-qualifier-does-not-work-reference-parameters.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6305-search-missing-qualifier-does-not-work-reference-parameters.yaml new file mode 100644 index 00000000000..da98735c073 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6305-search-missing-qualifier-does-not-work-reference-parameters.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 6305 +title: "Previously, when having StorageSettings#getIndexMissingFields() == IndexEnabledEnum.DISABLED (default value) +and attempting to search with the missing qualifier against a resource type with multiple search parameters of type reference, +the returned results would be incorrect. This has been fixed." \ No newline at end of file diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java index ab4b1afdefc..79981c2ca7f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java @@ -800,14 +800,19 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im subquery.addCustomColumns(1); subquery.addFromTable(getTable()); + String resourceType = theParams.getResourceTablePredicateBuilder().getResourceType(); + RuntimeSearchParam paramDefinition = + mySearchParamRegistry.getRuntimeSearchParam(resourceType, theParams.getParamName()); + List pathList = paramDefinition.getPathsSplitForResourceType(resourceType); + Condition subQueryCondition = ComboCondition.and( BinaryCondition.equalTo( getResourceIdColumn(), theParams.getResourceTablePredicateBuilder().getResourceIdColumn()), - BinaryCondition.equalTo( - getResourceTypeColumn(), - generatePlaceholder( - theParams.getResourceTablePredicateBuilder().getResourceType()))); + BinaryCondition.equalTo(getResourceTypeColumn(), generatePlaceholder(resourceType)), + ComboCondition.or(pathList.stream() + .map(path -> BinaryCondition.equalTo(getColumnSourcePath(), generatePlaceholder(path))) + .toArray(BinaryCondition[]::new))); subquery.addCondition(subQueryCondition); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchMissingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchMissingTest.java index 742fb1ed48d..811ea570e2f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchMissingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchMissingTest.java @@ -1,9 +1,10 @@ package ca.uhn.fhir.jpa.dao.r4; -import static org.junit.jupiter.api.Assertions.assertEquals; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity; +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized; +import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -12,7 +13,6 @@ import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DecimalType; @@ -26,513 +26,528 @@ import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Task; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.List; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; -public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test { - private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchMissingTest.class); +public class FhirResourceDaoR4SearchMissingTest { - @BeforeEach - public void beforeResetMissing() { - myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.ENABLED); - } - - @AfterEach - public void afterResetSearch() { - myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED); - } - - @Test - public void testIndexMissingFieldsDisabledDontAllowInSearch_NonReference() { - myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); - - SearchParameterMap params = new SearchParameterMap(); - params.add(Patient.SP_ACTIVE, new StringParam().setMissing(true)); - try { - myPatientDao.search(params); - } catch (MethodNotAllowedException e) { - assertEquals(Msg.code(985) + ":missing modifier is disabled on this server", e.getMessage()); + @Nested + class IndexMissingDisabledTests extends MissingTests { + @BeforeEach + public void before() { + myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.DISABLED); } - } - @Test - public void testIndexMissingFieldsDisabledDontAllowInSearch_Reference() { - myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); - - SearchParameterMap params = new SearchParameterMap(); - params.add(Patient.SP_ORGANIZATION, new StringParam().setMissing(true)); - try { - myPatientDao.search(params); - } catch (MethodNotAllowedException e) { - assertEquals(Msg.code(985) + ":missing modifier is disabled on this server", e.getMessage()); - } - } - - @Test - public void testIndexMissingFieldsDisabledDontCreateIndexes() { - myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); - Organization org = new Organization(); - org.setActive(true); - myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - - runInTransaction(() -> { - assertThat(mySearchParamPresentDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamStringDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamDateDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamTokenDao.findAll()).hasSize(1); - assertThat(myResourceIndexedSearchParamQuantityDao.findAll()).isEmpty(); - }); - - } - - @Test - public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() { - - myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); - myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED); - Organization org = new Organization(); - org.setActive(true); - myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - - runInTransaction(() -> { - assertThat(mySearchParamPresentDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamStringDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamDateDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamTokenDao.findAll()).hasSize(1); - assertThat(myResourceIndexedSearchParamQuantityDao.findAll()).isEmpty(); - }); - - } - - @Test - public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() { - - myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); - myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED); - Organization org = new Organization(); - org.setActive(true); - myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - - runInTransaction(() -> { - assertThat(mySearchParamPresentDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamStringDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamDateDao.findAll()).isEmpty(); - assertThat(myResourceIndexedSearchParamTokenDao.findAll()).hasSize(1); - assertThat(myResourceIndexedSearchParamQuantityDao.findAll()).isEmpty(); - }); - - } - - @SuppressWarnings("unused") - @Test - public void testSearchResourceReferenceMissingChain() { - IIdType oid1; - { + @Test + public void testIndexMissingFieldsDisabledDontCreateIndexes() { Organization org = new Organization(); org.setActive(true); - oid1 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - } - IIdType tid1; - { - Task task = new Task(); - task.setRequester(new Reference(oid1)); - tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); - } - IIdType tid2; - { - Task task = new Task(); - task.setOwner(new Reference(oid1)); - tid2 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + + runInTransaction(() -> { + assertThat(mySearchParamPresentDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamStringDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamDateDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamTokenDao.findAll()).hasSize(1); + assertThat(myResourceIndexedSearchParamQuantityDao.findAll()).isEmpty(); + }); + } - IIdType oid2; - { + @Test + public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() { + myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED); Organization org = new Organization(); org.setActive(true); - org.setName("NAME"); - oid2 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - } - IIdType tid3; - { - Task task = new Task(); - task.setRequester(new Reference(oid2)); - tid3 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + + runInTransaction(() -> { + assertThat(mySearchParamPresentDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamStringDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamDateDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamTokenDao.findAll()).hasSize(1); + assertThat(myResourceIndexedSearchParamQuantityDao.findAll()).isEmpty(); + }); + } - SearchParameterMap map; - List ids; + @Test + public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() { + myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED); + Organization org = new Organization(); + org.setActive(true); + myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); - map = new SearchParameterMap(); - map.add(Organization.SP_NAME, new StringParam().setMissing(true)); - ids = toUnqualifiedVersionlessIds(myOrganizationDao.search(map)); - assertThat(ids).containsExactly(oid1); + runInTransaction(() -> { + assertThat(mySearchParamPresentDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamStringDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamDateDao.findAll()).isEmpty(); + assertThat(myResourceIndexedSearchParamTokenDao.findAll()).hasSize(1); + assertThat(myResourceIndexedSearchParamQuantityDao.findAll()).isEmpty(); + }); - ourLog.info("Starting Search 2"); - - map = new SearchParameterMap(); - map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "true")); - ids = toUnqualifiedVersionlessIds(myTaskDao.search(map)); - assertThat(ids).containsExactly(tid1); // NOT tid2 - - map = new SearchParameterMap(); - map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "false")); - ids = toUnqualifiedVersionlessIds(myTaskDao.search(map)); - assertThat(ids).containsExactly(tid3); - - map = new SearchParameterMap(); - map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization", "name:missing", "true")); - ids = toUnqualifiedVersionlessIds(myPatientDao.search(map)); - assertThat(ids).isEmpty(); - - } - - @Test - public void testSearchWithMissingDate() { - IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId(); - IIdType notMissing; - IIdType missing; - { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - } - { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("002"); - patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); - patient.setBirthDateElement(new DateType("2011-01-01")); - patient.getManagingOrganization().setReferenceElement(orgId); - notMissing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - } - // Date Param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - DateParam param = new DateParam(); - param.setMissing(false); - params.add(Patient.SP_BIRTHDATE, param); - List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); - assertThat(patients).containsSubsequence(notMissing); - assertThat(patients).doesNotContainSubsequence(missing); - } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - DateParam param = new DateParam(); - param.setMissing(true); - params.add(Patient.SP_BIRTHDATE, param); - List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); } } - @Test - public void testSearchWithMissingCoords() { - String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue(); - String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue(); - - runInTransaction(() -> { - ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * "))); - }); - - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - TokenParam param = new TokenParam(); - param.setMissing(true); - params.add(Location.SP_NEAR, param); - myCaptureQueriesListener.clear(); - List patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params)); - myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); - assertThat(patients).containsSubsequence(locId); - assertThat(patients).doesNotContainSubsequence(locId2); + @Nested + class IndexMissingEnabledTests extends MissingTests { + @BeforeEach + public void before() { + myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.ENABLED); } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - TokenParam param = new TokenParam(); - param.setMissing(false); - params.add(Location.SP_NEAR, param); - List patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params)); - assertThat(patients).containsSubsequence(locId2); - assertThat(patients).doesNotContainSubsequence(locId); + + + @SuppressWarnings("unused") + @Test + public void testSearchResourceReferenceMissingChain() { + IIdType oid1; + { + Organization org = new Organization(); + org.setActive(true); + oid1 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType tid1; + { + Task task = new Task(); + task.setRequester(new Reference(oid1)); + tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + } + { + Task task = new Task(); + task.setOwner(new Reference(oid1)); + myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + } + + IIdType oid2; + { + Organization org = new Organization(); + org.setActive(true); + org.setName("NAME"); + oid2 = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + IIdType tid3; + { + Task task = new Task(); + task.setRequester(new Reference(oid2)); + tid3 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); + } + + SearchParameterMap map; + List ids; + + map = new SearchParameterMap(); + map.add(Organization.SP_NAME, new StringParam().setMissing(true)); + ids = toUnqualifiedVersionlessIds(myOrganizationDao.search(map, mySrd)); + assertThat(ids).containsExactly(oid1); + + ourLog.info("Starting Search 2"); + + map = new SearchParameterMap(); + map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "true")); + ids = toUnqualifiedVersionlessIds(myTaskDao.search(map, mySrd)); + assertThat(ids).containsExactly(tid1); // NOT tid2 + + map = new SearchParameterMap(); + map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "false")); + ids = toUnqualifiedVersionlessIds(myTaskDao.search(map, mySrd)); + assertThat(ids).containsExactly(tid3); + + map = new SearchParameterMap(); + map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization", "name:missing", "true")); + ids = toUnqualifiedVersionlessIds(myPatientDao.search(map, mySrd)); + assertThat(ids).isEmpty(); + } } - @Test - public void testSearchWithMissingDate2() { - MedicationRequest mr1 = new MedicationRequest(); - mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); - mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01"); - IIdType id1 = myMedicationRequestDao.create(mr1).getId().toUnqualifiedVersionless(); + static class MissingTests extends BaseJpaR4Test { + @AfterEach + public void after() { + myStorageSettings.setIndexMissingFields(new StorageSettings().getIndexMissingFields()); + myStorageSettings.setNormalizedQuantitySearchLevel(new StorageSettings().getNormalizedQuantitySearchLevel()); - MedicationRequest mr2 = new MedicationRequest(); - mr2.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); - IIdType id2 = myMedicationRequestDao.create(mr2).getId().toUnqualifiedVersionless(); - - SearchParameterMap map = new SearchParameterMap(); - map.add(MedicationRequest.SP_DATE, new DateParam().setMissing(true)); - IBundleProvider results = myMedicationRequestDao.search(map); - List ids = toUnqualifiedVersionlessIdValues(results); - - assertThat(ids).containsExactly(id2.getValue()); - - } - - @Test - public void testSearchWithMissingQuantity() { - IIdType notMissing; - IIdType missing; - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); } - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("002"); - obs.setValue(new Quantity(123)); - notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + + @Test + public void testSearchWithMissingDate() { + IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId(); + IIdType notMissing; + IIdType missing; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); + patient.setBirthDateElement(new DateType("2011-01-01")); + patient.getManagingOrganization().setReferenceElement(orgId); + notMissing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + // Date Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + DateParam param = new DateParam(); + param.setMissing(false); + params.add(Patient.SP_BIRTHDATE, param); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(notMissing); + assertThat(patients).doesNotContainSubsequence(missing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + DateParam param = new DateParam(); + param.setMissing(true); + params.add(Patient.SP_BIRTHDATE, param); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + } } - // Quantity Param - { + + @Test + public void testSearchWithMissingCoords() { + String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue(); + String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue(); + + runInTransaction(() -> ourLog.info("Coords:\n * {}", + myResourceIndexedSearchParamCoordsDao.findAll().stream() + .map(ResourceIndexedSearchParamCoords::toString).collect(Collectors.joining("\n * ")) + ) + ); + + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + TokenParam param = new TokenParam(); + param.setMissing(true); + params.add(Location.SP_NEAR, param); + myCaptureQueriesListener.clear(); + List patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params, mySrd)); + myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); + assertThat(patients).containsSubsequence(locId); + assertThat(patients).doesNotContainSubsequence(locId2); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + TokenParam param = new TokenParam(); + param.setMissing(false); + params.add(Location.SP_NEAR, param); + List patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(locId2); + assertThat(patients).doesNotContainSubsequence(locId); + } + } + + @Test + public void testSearchWithMissingDate2() { + MedicationRequest mr1 = new MedicationRequest(); + mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); + mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01"); + myMedicationRequestDao.create(mr1, mySrd).getId().toUnqualifiedVersionless(); + + MedicationRequest mr2 = new MedicationRequest(); + mr2.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); + IIdType id2 = myMedicationRequestDao.create(mr2, mySrd).getId().toUnqualifiedVersionless(); + + SearchParameterMap map = new SearchParameterMap(); + map.add(MedicationRequest.SP_DATE, new DateParam().setMissing(true)); + IBundleProvider results = myMedicationRequestDao.search(map, mySrd); + List ids = toUnqualifiedVersionlessIdValues(results); + + assertThat(ids).containsExactly(id2.getValue()); + + } + + @Test + public void testSearchWithMissingQuantity() { + IIdType notMissing; + IIdType missing; + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("002"); + obs.setValue(new Quantity(123)); + notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + // Quantity Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + QuantityParam param = new QuantityParam(); + param.setMissing(false); + params.add(Observation.SP_VALUE_QUANTITY, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).doesNotContainSubsequence(missing); + assertThat(patients).containsSubsequence(notMissing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + QuantityParam param = new QuantityParam(); + param.setMissing(true); + params.add(Observation.SP_VALUE_QUANTITY, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + } + } + + @Test + public void testSearchWithMissingQuantityWithNormalizedQuantitySearchSupported() { + + myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED); + IIdType notMissing; + IIdType missing; + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("002"); + obs.setValue(new Quantity(123)); + notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + + runInTransaction(() -> { + ourLog.info("Quantity Indexes:\n * {}", + myResourceIndexedSearchParamQuantityDao.findAll().stream() + .filter(t -> t.getParamName().equals("value-quantity")).map(ResourceIndexedSearchParamQuantity::toString).collect(Collectors.joining("\n * ") + ) + ); + ourLog.info("Normalized Quantity Indexes:\n * {}", + myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream(). + filter(t -> t.getParamName().equals("value-quantity")).map(ResourceIndexedSearchParamQuantityNormalized::toString).collect(Collectors.joining("\n * ") + ) + ); + }); + + // Quantity Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + QuantityParam param = new QuantityParam(); + param.setMissing(false); + params.add(Observation.SP_VALUE_QUANTITY, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).doesNotContainSubsequence(missing); + assertThat(patients).containsSubsequence(notMissing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + QuantityParam param = new QuantityParam(); + param.setMissing(true); + params.add(Observation.SP_VALUE_QUANTITY, param); + myCaptureQueriesListener.clear(); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + myCaptureQueriesListener.logSelectQueries(); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + } + + } + + @Test + public void testSearchWithMissingQuantityWithNormalizedQuantityStorageSupported() { + + myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED); + IIdType notMissing; + IIdType missing; + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("002"); + obs.setValue(new Quantity(123)); + notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + // Quantity Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + QuantityParam param = new QuantityParam(); + param.setMissing(false); + params.add(Observation.SP_VALUE_QUANTITY, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).doesNotContainSubsequence(missing); + assertThat(patients).containsSubsequence(notMissing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + QuantityParam param = new QuantityParam(); + param.setMissing(true); + params.add(Observation.SP_VALUE_QUANTITY, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + } + + } + + + @Test + public void testSearchWithMissingReference() { + IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId().toUnqualifiedVersionless(); + IIdType notMissing; + IIdType missing; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); + patient.setBirthDateElement(new DateType("2011-01-01")); + patient.getManagingOrganization().setReferenceElement(orgId); + notMissing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + // Reference Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + ReferenceParam param = new ReferenceParam(); + param.setMissing(false); + params.add(Patient.SP_ORGANIZATION, param); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd)); + assertThat(patients).doesNotContainSubsequence(missing); + assertThat(patients).containsSubsequence(notMissing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + ReferenceParam param = new ReferenceParam(); + param.setMissing(true); + params.add(Patient.SP_ORGANIZATION, param); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + assertThat(patients).doesNotContainSubsequence(orgId); + } + } + + + @Test + public void testSearchWithMissingReference_resourceTypeWithMultipleReferences() { + IIdType patientId = createPatient(); + IIdType observationId = createObservation(withSubject(patientId)); + SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - QuantityParam param = new QuantityParam(); - param.setMissing(false); - params.add(Observation.SP_VALUE_QUANTITY, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).doesNotContainSubsequence(missing); - assertThat(patients).containsSubsequence(notMissing); + params.add(Observation.SP_PERFORMER, new ReferenceParam().setMissing(true)); + IBundleProvider bundleProvider = myObservationDao.search(params, mySrd); + assertThat(bundleProvider.getAllResourceIds()).containsExactly(observationId.getIdPart()); } - { + + @Test + public void testSearchWithMissingReference_searchParamMultiplePaths() { + IIdType encounterId = createEncounter(); + createObservation(withEncounter(encounterId.getValue())); + SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - QuantityParam param = new QuantityParam(); - param.setMissing(true); - params.add(Observation.SP_VALUE_QUANTITY, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); - } - } - - @Test - public void testSearchWithMissingQuantityWithNormalizedQuantitySearchSupported() { - - myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED); - IIdType notMissing; - IIdType missing; - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - } - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("002"); - obs.setValue(new Quantity(123)); - notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + params.add(Observation.SP_ENCOUNTER, new ReferenceParam().setMissing(true)); + IBundleProvider bundleProvider = myObservationDao.search(params, mySrd); + assertThat(bundleProvider.getAllResourceIds()).isEmpty(); } - runInTransaction(() -> { - ourLog.info("Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * "))); - ourLog.info("Normalized Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * "))); - }); - - // Quantity Param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - QuantityParam param = new QuantityParam(); - param.setMissing(false); - params.add(Observation.SP_VALUE_QUANTITY, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).doesNotContainSubsequence(missing); - assertThat(patients).containsSubsequence(notMissing); - } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - QuantityParam param = new QuantityParam(); - param.setMissing(true); - params.add(Observation.SP_VALUE_QUANTITY, param); - myCaptureQueriesListener.clear(); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - myCaptureQueriesListener.logSelectQueries(); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); + @Test + public void testSearchWithMissingString() { + IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId(); + IIdType notMissing; + IIdType missing; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("002"); + patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); + patient.setBirthDateElement(new DateType("2011-01-01")); + patient.getManagingOrganization().setReferenceElement(orgId); + notMissing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); + } + // String Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + StringParam param = new StringParam(); + param.setMissing(false); + params.add(Patient.SP_FAMILY, param); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd)); + assertThat(patients).doesNotContainSubsequence(missing); + assertThat(patients).containsSubsequence(notMissing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + StringParam param = new StringParam(); + param.setMissing(true); + params.add(Patient.SP_FAMILY, param); + List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + } } - } - - @Test - public void testSearchWithMissingQuantityWithNormalizedQuantityStorageSupported() { - - myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED); - IIdType notMissing; - IIdType missing; - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - } - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("002"); - obs.setValue(new Quantity(123)); - notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - } - // Quantity Param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - QuantityParam param = new QuantityParam(); - param.setMissing(false); - params.add(Observation.SP_VALUE_QUANTITY, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).doesNotContainSubsequence(missing); - assertThat(patients).containsSubsequence(notMissing); - } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - QuantityParam param = new QuantityParam(); - param.setMissing(true); - params.add(Observation.SP_VALUE_QUANTITY, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); - } - - } - - - @Test - public void testSearchWithMissingReference() { - IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId().toUnqualifiedVersionless(); - IIdType notMissing; - IIdType missing; - { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - } - { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("002"); - patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); - patient.setBirthDateElement(new DateType("2011-01-01")); - patient.getManagingOrganization().setReferenceElement(orgId); - notMissing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - } - // Reference Param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - ReferenceParam param = new ReferenceParam(); - param.setMissing(false); - params.add(Patient.SP_ORGANIZATION, param); - List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); - assertThat(patients).doesNotContainSubsequence(missing); - assertThat(patients).containsSubsequence(notMissing); - } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - ReferenceParam param = new ReferenceParam(); - param.setMissing(true); - params.add(Patient.SP_ORGANIZATION, param); - List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); - assertThat(patients).doesNotContainSubsequence(orgId); - } - } - - @Test - public void testSearchWithMissingString() { - IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId(); - IIdType notMissing; - IIdType missing; - { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - } - { - Patient patient = new Patient(); - patient.addIdentifier().setSystem("urn:system").setValue("002"); - patient.addName().setFamily("Tester_testSearchStringParam").addGiven("John"); - patient.setBirthDateElement(new DateType("2011-01-01")); - patient.getManagingOrganization().setReferenceElement(orgId); - notMissing = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - } - // String Param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - StringParam param = new StringParam(); - param.setMissing(false); - params.add(Patient.SP_FAMILY, param); - List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); - assertThat(patients).doesNotContainSubsequence(missing); - assertThat(patients).containsSubsequence(notMissing); - } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - StringParam param = new StringParam(); - param.setMissing(true); - params.add(Patient.SP_FAMILY, param); - List patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); - } - } - - @Test - public void testSearchWithToken() { - IIdType notMissing; - IIdType missing; - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("001"); - missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - } - { - Observation obs = new Observation(); - obs.addIdentifier().setSystem("urn:system").setValue("002"); - obs.getCode().addCoding().setSystem("urn:system").setCode("002"); - notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - } - // Token Param - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - TokenParam param = new TokenParam(); - param.setMissing(false); - params.add(Observation.SP_CODE, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).doesNotContainSubsequence(missing); - assertThat(patients).containsSubsequence(notMissing); - } - { - SearchParameterMap params = new SearchParameterMap(); - params.setLoadSynchronous(true); - TokenParam param = new TokenParam(); - param.setMissing(true); - params.add(Observation.SP_CODE, param); - List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); - assertThat(patients).containsSubsequence(missing); - assertThat(patients).doesNotContainSubsequence(notMissing); + @Test + public void testSearchWithToken() { + IIdType notMissing; + IIdType missing; + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("001"); + missing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + { + Observation obs = new Observation(); + obs.addIdentifier().setSystem("urn:system").setValue("002"); + obs.getCode().addCoding().setSystem("urn:system").setCode("002"); + notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + } + // Token Param + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + TokenParam param = new TokenParam(); + param.setMissing(false); + params.add(Observation.SP_CODE, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).doesNotContainSubsequence(missing); + assertThat(patients).containsSubsequence(notMissing); + } + { + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronous(true); + TokenParam param = new TokenParam(); + param.setMissing(true); + params.add(Observation.SP_CODE, param); + List patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd)); + assertThat(patients).containsSubsequence(missing); + assertThat(patients).doesNotContainSubsequence(notMissing); + } } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index cb098dc7012..427970449c6 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -217,7 +217,7 @@ import static org.mockito.Mockito.when; public class ResourceProviderR4Test extends BaseResourceProviderR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class); private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw; - private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); + private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); @Autowired private ISearchDao mySearchEntityDao; @@ -413,15 +413,15 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient pt1 = new Patient(); pt1.addName().setFamily("Elizabeth"); - String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); + String pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); Patient pt2 = new Patient(); pt2.addName().setFamily("fghijk"); - String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); + String pt2id = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless().getValue(); Patient pt3 = new Patient(); pt3.addName().setFamily("zzzzz"); - myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue(); + myPatientDao.create(pt3, mySrd).getId().toUnqualifiedVersionless().getValue(); Bundle output = myClient @@ -450,7 +450,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient pt1 = new Patient(); pt1.addName().setFamily("Smith%"); - String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); + String pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); Bundle output = myClient .search() @@ -463,7 +463,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient pt2 = new Patient(); pt2.addName().setFamily("Sm%ith"); - String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); + String pt2id = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless().getValue(); output = myClient .search() @@ -740,7 +740,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient p = new Patient(); p.addName().setFamily("FAM").addGiven("GIV"); - IIdType id = myPatientDao.create(p).getId(); + IIdType id = myPatientDao.create(p, mySrd).getId(); myClient.read().resource("Patient").withId(id.toUnqualifiedVersionless()).execute(); @@ -763,7 +763,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient p = new Patient(); p.addName().setFamily("FAM").addGiven("GIV"); - IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); + IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); myClient .delete() @@ -1025,57 +1025,58 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { public void testCreateAndReadBackResourceWithContainedReferenceToContainer() { myFhirContext.setParserErrorHandler(new StrictErrorHandler()); - String input = "{\n" + - " \"resourceType\": \"Organization\",\n" + - " \"id\": \"1\",\n" + - " \"meta\": {\n" + - " \"tag\": [\n" + - " {\n" + - " \"system\": \"https://blah.org/deployment\",\n" + - " \"code\": \"e69414dd-b5c2-462d-bcfd-9d04d6b16596\",\n" + - " \"display\": \"DEPLOYMENT\"\n" + - " },\n" + - " {\n" + - " \"system\": \"https://blah.org/region\",\n" + - " \"code\": \"b47d7a5b-b159-4bed-a8f8-3258e6603adb\",\n" + - " \"display\": \"REGION\"\n" + - " },\n" + - " {\n" + - " \"system\": \"https://blah.org/provider\",\n" + - " \"code\": \"28c30004-0333-40cf-9e7f-3f9e080930bd\",\n" + - " \"display\": \"PROVIDER\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " \"contained\": [\n" + - " {\n" + - " \"resourceType\": \"Location\",\n" + - " \"id\": \"2\",\n" + - " \"position\": {\n" + - " \"longitude\": 51.443238301454289,\n" + - " \"latitude\": 7.34196905697293\n" + - " },\n" + - " \"managingOrganization\": {\n" + - " \"reference\": \"#\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"type\": [\n" + - " {\n" + - " \"coding\": [\n" + - " {\n" + - " \"system\": \"https://blah.org/fmc/OrganizationType\",\n" + - " \"code\": \"CLINIC\",\n" + - " \"display\": \"Clinic\"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"name\": \"testOrg\"\n" + - "}"; + String input = """ +{ + "resourceType": "Organization", + "id": "1", + "meta": { + "tag": [ + { + "system": "https://blah.org/deployment", + "code": "e69414dd-b5c2-462d-bcfd-9d04d6b16596", + "display": "DEPLOYMENT" + }, + { + "system": "https://blah.org/region", + "code": "b47d7a5b-b159-4bed-a8f8-3258e6603adb", + "display": "REGION" + }, + { + "system": "https://blah.org/provider", + "code": "28c30004-0333-40cf-9e7f-3f9e080930bd", + "display": "PROVIDER" + } + ] + }, + "contained": [ + { + "resourceType": "Location", + "id": "2", + "position": { + "longitude": 51.443238301454289, + "latitude": 7.34196905697293 + }, + "managingOrganization": { + "reference": "#" + } + } + ], + "type": [ + { + "coding": [ + { + "system": "https://blah.org/fmc/OrganizationType", + "code": "CLINIC", + "display": "Clinic" + } + ] + } + ], + "name": "testOrg" +}"""; Organization org = myFhirContext.newJsonParser().parseResource(Organization.class, input); - IIdType id = myOrganizationDao.create(org).getId(); + IIdType id = myOrganizationDao.create(org, mySrd).getId(); org = myOrganizationDao.read(id); String output = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(org); @@ -1095,9 +1096,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } List outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); - runInTransaction(() -> { - assertEquals(100, myResourceTableDao.count()); - }); + runInTransaction(() -> assertEquals(100, myResourceTableDao.count())); Bundle found = myClient .search() @@ -1306,7 +1305,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } @Test - public void testCreateQuestionnaireResponseWithValidation() throws IOException { + public void testCreateQuestionnaireResponseWithValidation() { CodeSystem cs = new CodeSystem(); cs.setUrl("http://cs"); cs.setStatus(Enumerations.PublicationStatus.ACTIVE); @@ -1906,8 +1905,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too.. */ Socket sock = new Socket(); - sock.setSoTimeout(3000); - try { + try (sock) { + sock.setSoTimeout(3000); sock.connect(new InetSocketAddress("localhost", myPort)); sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes(StandardCharsets.UTF_8)); sock.getOutputStream().write("Host: localhost\n".getBytes(StandardCharsets.UTF_8)); @@ -1915,7 +1914,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream())); - // String response = ""; StringBuilder b = new StringBuilder(); char[] buf = new char[1000]; while (socketInput.read(buf) != -1) { @@ -1925,9 +1923,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { ourLog.debug("Resp: {}", resp); } catch (SocketTimeoutException e) { - e.printStackTrace(); - } finally { - sock.close(); + ourLog.debug(e.getMessage(), e); } Thread.sleep(1000); @@ -2398,7 +2394,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { assertThat(idValues).as(idValues.toString()).hasSize(10); idValues = searchAndReturnUnqualifiedIdValues(myServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear()); - assertThat(idValues).hasSize(0); + assertThat(idValues).isEmpty(); } @@ -2427,7 +2423,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID); } - Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).execute(); + Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).execute(); assertEquals(1, history.getEntry().size()); BundleEntryComponent historyEntry0 = history.getEntry().get(0); // validate entry.fullUrl @@ -2476,7 +2472,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID); } - Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).execute(); + Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).execute(); assertEquals(1, history.getEntry().size()); BundleEntryComponent historyEntry0 = history.getEntry().get(0); // validate entry.fullUrl @@ -2508,7 +2504,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { ourLog.info("Res ID: {}", id); - Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA).execute(); + Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA).execute(); assertThat(history.getEntry()).hasSize(3); assertEquals(id.withVersion("3").getValue(), history.getEntry().get(0).getResource().getId()); assertThat(((Patient) history.getEntry().get(0).getResource()).getName()).hasSize(1); @@ -2746,7 +2742,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { int total = 20; Organization org = new Organization(); org.setName("ORG"); - IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); Coding tagCode = new Coding(); tagCode.setCode("test"); @@ -2757,7 +2753,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .addTag(tagCode); t.setStatus(Task.TaskStatus.REQUESTED); t.getOwner().setReference(orgId.getValue()); - myTaskDao.create(t); + myTaskDao.create(t, mySrd); } HashSet ids = new HashSet<>(); @@ -2835,12 +2831,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { if (orgCount > 0) { Organization org = new Organization(); org.setName("ORG"); - IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); orgCount--; t.getOwner().setReference(orgId.getValue()); } - myTaskDao.create(t); + myTaskDao.create(t, mySrd); } } @@ -2909,12 +2905,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { if (orgCount > 0) { Organization org = new Organization(); org.setName("ORG"); - IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); orgCount--; t.getOwner().setReference(orgId.getValue()); } - myTaskDao.create(t); + myTaskDao.create(t, mySrd); } } @@ -2961,13 +2957,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { public void testIncludeCountDoesntIncludeIncludes() { Organization org = new Organization(); org.setName("ORG"); - IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); for (int i = 0; i < 10; i++) { Patient pt = new Patient(); pt.getManagingOrganization().setReference(orgId.getValue()); pt.addName().setFamily("FAM" + i); - myPatientDao.create(pt); + myPatientDao.create(pt, mySrd); } Bundle bundle = myClient @@ -3168,7 +3164,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); assertEquals("2", newPt.getIdElement().getVersionIdPart()); - assertEquals(false, newPt.getActive()); + assertFalse(newPt.getActive()); } @Test @@ -3196,7 +3192,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); assertEquals("1", newPt.getIdElement().getVersionIdPart()); - assertEquals(true, newPt.getActive()); + assertTrue(newPt.getActive()); } @Test @@ -3226,7 +3222,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); assertEquals("2", newPt.getIdElement().getVersionIdPart()); - assertEquals(false, newPt.getActive()); + assertFalse(newPt.getActive()); } @Test @@ -3255,7 +3251,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); assertEquals("2", newPt.getIdElement().getVersionIdPart()); - assertEquals(false, newPt.getActive()); + assertFalse(newPt.getActive()); } @Test @@ -3323,12 +3319,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { { Bundle returned = myClient.search().forResource(Patient.class).encodedXml().returnBundle(Bundle.class).execute(); - assertThat(returned.getEntry().size()).isGreaterThan(1); + assertThat(returned.getEntry()).hasSizeGreaterThan(1); assertEquals(BundleType.SEARCHSET, returned.getType()); } { Bundle returned = myClient.search().forResource(Patient.class).encodedJson().returnBundle(Bundle.class).execute(); - assertThat(returned.getEntry().size()).isGreaterThan(1); + assertThat(returned.getEntry()).hasSizeGreaterThan(1); } } @@ -3350,7 +3346,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } }); - Bundle bundle = myClient.history().onServer().andReturnBundle(Bundle.class).execute(); + Bundle bundle = myClient.history().onServer().returnBundle(Bundle.class).execute(); assertEquals(1, bundle.getTotal()); assertThat(bundle.getEntry()).hasSize(1); assertEquals(id2.getIdPart(), bundle.getEntry().get(0).getResource().getIdElement().getIdPart()); @@ -3507,15 +3503,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { assertThat(text).doesNotContain("\"B\""); assertThat(text).doesNotContain("\"B1\""); } - - -// HttpGet read = new HttpGet(ourServerBase + "/Observation?patient=P5000000302&_sort:desc=code&code:in=http://fkcfhir.org/fhir/vs/ccdacapddialysisorder"); -// try (CloseableHttpResponse response = ourHttpClient.execute(read)) { -// String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); -// ourLog.info(text); -// assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode()); -// assertThat(text).doesNotContain("\"text\",\"type\""); -// } } @Test @@ -3873,9 +3860,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { p.addName().setFamily(methodName + "1"); IIdType pid1 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); - Thread.sleep(10); long time1 = System.currentTimeMillis(); - Thread.sleep(10); Patient p2 = new Patient(); p2.addName().setFamily(methodName + "2"); @@ -4064,9 +4049,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { public void testSearchLastUpdatedParamRp() throws InterruptedException { String methodName = "testSearchLastUpdatedParamRp"; - int sleep = 100; - Thread.sleep(sleep); - DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI); IIdType id1a; { @@ -4083,9 +4065,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { id1b = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless(); } - Thread.sleep(1100); DateTimeType beforeR2 = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI); - Thread.sleep(1100); IIdType id2; { @@ -4249,13 +4229,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); matches = bundle.getEntry().size(); - assertThat(matches).isGreaterThan(0); + assertThat(matches).isPositive(); } @Test public void testSearchReturnsSearchDate() throws Exception { Date before = new Date(); - Thread.sleep(1); //@formatter:off Bundle found = myClient @@ -4266,7 +4245,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { .execute(); //@formatter:on - Thread.sleep(1); Date after = new Date(); InstantType updated = found.getMeta().getLastUpdatedElement(); @@ -4300,7 +4278,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4313,7 +4291,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4326,7 +4304,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4339,24 +4317,24 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } // > 1m String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m"); - ourLog.info("uri = " + uri); + ourLog.info("uri = {}", uri); List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids).hasSize(3); //>= 100cm uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm"); - ourLog.info("uri = " + uri); + ourLog.info("uri = {}", uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids).hasSize(3); //>= 10dm uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm"); - ourLog.info("uri = " + uri); + ourLog.info("uri = {}", uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids).hasSize(3); } @@ -4381,7 +4359,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4392,7 +4370,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4403,7 +4381,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4414,7 +4392,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } String uri; @@ -4451,7 +4429,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4462,7 +4440,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -4474,7 +4452,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } myCaptureQueriesListener.clear(); @@ -4490,8 +4468,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { //-- check use normalized quantity table to search String searchSql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true); - assertThat(searchSql).doesNotContain("HFJ_SPIDX_QUANTITY t0"); - assertThat(searchSql).contains("HFJ_SPIDX_QUANTITY_NRML"); + assertThat(searchSql).doesNotContain("HFJ_SPIDX_QUANTITY t0").contains("HFJ_SPIDX_QUANTITY_NRML"); } @Test @@ -5044,7 +5021,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -5056,7 +5033,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -5068,7 +5045,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -5080,7 +5057,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } String uri = myServerBase + "/Observation?_sort=code-value-quantity"; @@ -5092,7 +5069,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { found = myFhirContext.newXmlParser().parseResource(Bundle.class, output); } - ourLog.debug("Bundle: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); + ourLog.debug("Bundle: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); List list = toUnqualifiedVersionlessIds(found); assertThat(found.getEntry()).hasSize(4); @@ -5129,7 +5106,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -5145,7 +5122,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -5161,7 +5138,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -5176,7 +5153,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { comp.setValue(new Quantity().setValue(250)); oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } String uri = myServerBase + "/Observation?_sort=combo-code-value-quantity"; @@ -5188,7 +5165,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { found = myFhirContext.newXmlParser().parseResource(Bundle.class, output); } - ourLog.debug("Bundle: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); + ourLog.debug("Bundle: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); List list = toUnqualifiedVersionlessIds(found); assertThat(found.getEntry()).hasSize(4); @@ -5264,9 +5241,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { List list = toUnqualifiedVersionlessIds(found); ourLog.info(methodName + " found: " + list.toString() + " - Wanted " + orgMissing + " but not " + orgNotMissing); - assertThat(list).doesNotContain(orgNotMissing); - assertThat(list).doesNotContain(deletedIdMissingTrue); - assertThat(list).contains(orgMissing); + assertThat(list).doesNotContain(orgNotMissing).doesNotContain(deletedIdMissingTrue).contains(orgMissing); } @Test @@ -5927,7 +5902,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { } Date before = new Date(); - Thread.sleep(100); pt = new Patient(); pt.setId(id.getIdPart()); @@ -6450,7 +6424,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); obs.setValue(new Quantity().setValueElement(new DecimalType(125.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); IIdType opid1 = myObservationDao.create(obs, mySrd).getId(); @@ -6463,7 +6437,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); obs.setValue(new Quantity().setValueElement(new DecimalType(24.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); myObservationDao.update(obs, mySrd); } @@ -6479,7 +6453,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -6492,7 +6466,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } { @@ -6505,25 +6479,25 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myObservationDao.create(obs, mySrd); - ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); + ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); } // > 1m String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m"); - ourLog.info("uri = " + uri); + ourLog.info("uri = {}", uri); List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids).hasSize(2); //>= 100cm uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm"); - ourLog.info("uri = " + uri); + ourLog.info("uri = {}", uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids).hasSize(2); //>= 10dm uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm"); - ourLog.info("uri = " + uri); + ourLog.info("uri = {}", uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); assertThat(ids).hasSize(2); } @@ -6540,7 +6514,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { patient.setBirthDateElement(new DateType("2073")); pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); - ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient)); + ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient)); ourLog.info("pid0 " + pid0); } @@ -6553,7 +6527,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); - ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); } uri = myServerBase + "/Patient?_total=accurate&birthdate=gt2072-01-01"; @@ -6564,7 +6538,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); - ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); + ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); } } @@ -6995,9 +6969,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { && theInput.IsEnforceRefOnType && theInput.IsEnforceRefOnWrite).isFalse(); } catch (InvalidRequestException ex) { - assertThat(ex.getMessage().contains( - "Invalid resource reference" - )).as(ex.getMessage()).isTrue(); + assertThat(ex.getMessage()).as(ex.getMessage()).contains("Invalid resource reference"); } finally { myStorageSettings.setEnforceReferentialIntegrityOnWrite(isEnforceRefOnWrite); myStorageSettings.setEnforceReferenceTargetTypes(isEnforceRefTargetTypes); @@ -7331,9 +7303,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { patient.setBirthDate(cal.getTime()); } return patient; - }, (isMissing) -> { - return doSearch(Patient.class, Patient.BIRTHDATE.isMissing(isMissing)); - }); + }, (isMissing) -> doSearch(Patient.class, Patient.BIRTHDATE.isMissing(isMissing))); } @ParameterizedTest @@ -7346,9 +7316,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { patient.setGender(AdministrativeGender.FEMALE); } return patient; - }, isMissing -> { - return doSearch(Patient.class, Patient.GENDER.isMissing(isMissing)); - }); + }, isMissing -> doSearch(Patient.class, Patient.GENDER.isMissing(isMissing))); } @ParameterizedTest @@ -7364,9 +7332,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { patient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerId))); } return patient; - }, isMissing -> { - return doSearch(Patient.class, Patient.GENERAL_PRACTITIONER.isMissing(isMissing)); - }); + }, isMissing -> doSearch(Patient.class, Patient.GENERAL_PRACTITIONER.isMissing(isMissing))); } @ParameterizedTest @@ -7409,9 +7375,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { sp.setUrl("http://example.com"); } return sp; - }, isMissing -> { - return doSearch(SearchParameter.class, SearchParameter.URL.isMissing(isMissing)); - }); + }, isMissing -> doSearch(SearchParameter.class, SearchParameter.URL.isMissing(isMissing))); } @ParameterizedTest @@ -7424,9 +7388,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { obs.setValue(new Quantity(3)); } return obs; - }, isMissing -> { - return doSearch(Observation.class, Observation.VALUE_QUANTITY.isMissing(isMissing)); - }); + }, isMissing -> doSearch(Observation.class, Observation.VALUE_QUANTITY.isMissing(isMissing))); } @ParameterizedTest @@ -7457,7 +7419,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { Y doTask(X theInput); } - private static class MissingSearchTestParameters { + public static class MissingSearchTestParameters { /** * The setting for IndexMissingFields */ From 377e44b6cacae415639c7890dd901d2d63db093a Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 25 Sep 2024 20:38:22 +0000 Subject: [PATCH 3/6] attribution and pom change (#6309) --- .../fhir/changelog/7_6_0/6290-mssql-migration-trim-fix.yaml | 5 +++++ pom.xml | 1 + 2 files changed, 6 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6290-mssql-migration-trim-fix.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6290-mssql-migration-trim-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6290-mssql-migration-trim-fix.yaml new file mode 100644 index 00000000000..435207a3454 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6290-mssql-migration-trim-fix.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 6290 +title: "Previously, a specific migration task was using the `TRIM()` function, which does not exist in MSSQL 2012. This was causing migrations targeting MSSQL 2012 to fail. +This has been corrected and replaced with usage of a combination of LTRIM() and RTRIM(). Thanks to Primož Delopst at Better for the contribution!" diff --git a/pom.xml b/pom.xml index e6f098ceece..481c3a77047 100644 --- a/pom.xml +++ b/pom.xml @@ -869,6 +869,7 @@ delopst Primož Delopst + Better Zach Smith From 3f6d1eb29beb44232e56d5974e78d29ec1c83506 Mon Sep 17 00:00:00 2001 From: Thomas Papke Date: Thu, 26 Sep 2024 04:07:27 +0200 Subject: [PATCH 4/6] #5768 Upgrade to latest simple-java-mail (#6261) --- hapi-fhir-jpaserver-subscription/pom.xml | 5 +++ .../email/EmailSubscriptionDstu2Test.java | 4 +-- .../email/EmailSenderImplTest.java | 4 +-- .../email/EmailSubscriptionDstu3Test.java | 4 +-- hapi-fhir-server-cds-hooks/pom.xml | 14 -------- .../uhn/fhir/rest/server/mail/IMailSvc.java | 6 ++-- .../ca/uhn/fhir/rest/server/mail/MailSvc.java | 35 ++++++++++--------- .../uhn/fhir/rest/server/mail/MailSvcIT.java | 7 ++-- pom.xml | 33 +++++++++++------ 9 files changed, 57 insertions(+), 55 deletions(-) diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index ba09eed0ab5..45504f94b2f 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -70,6 +70,11 @@ jakarta.servlet-api provided + + jakarta.mail + jakarta.mail-api + true + diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java index 90726afb0c8..ae1e985ed22 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu2Test.java @@ -28,8 +28,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSenderImplTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSenderImplTest.java index fa871fa2595..9c01f3aeb90 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSenderImplTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSenderImplTest.java @@ -17,8 +17,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java index 28d8ae61b35..825f6ebb9ae 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/email/EmailSubscriptionDstu3Test.java @@ -26,8 +26,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 1f396dc4fdc..281604dba5a 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -79,25 +79,11 @@ org.simplejavamail simple-java-mail - - - - com.sun.activation - jakarta.activation - - com.icegreen greenmail compile - - - - com.sun.activation - jakarta.activation - - diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java index 2a4d3ea6d56..62e67076204 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java @@ -21,9 +21,9 @@ package ca.uhn.fhir.rest.server.mail; import jakarta.annotation.Nonnull; import org.simplejavamail.api.email.Email; -import org.simplejavamail.api.mailer.AsyncResponse; import java.util.List; +import java.util.function.Consumer; public interface IMailSvc { void sendMail(@Nonnull List theEmails); @@ -31,7 +31,5 @@ public interface IMailSvc { void sendMail(@Nonnull Email theEmail); void sendMail( - @Nonnull Email theEmail, - @Nonnull Runnable theOnSuccess, - @Nonnull AsyncResponse.ExceptionConsumer theErrorHandler); + @Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer theErrorHandler); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java index 45299edda15..adc7111d4b9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java @@ -20,12 +20,9 @@ package ca.uhn.fhir.rest.server.mail; import jakarta.annotation.Nonnull; -import org.apache.commons.lang3.Validate; import org.simplejavamail.MailException; import org.simplejavamail.api.email.Email; import org.simplejavamail.api.email.Recipient; -import org.simplejavamail.api.mailer.AsyncResponse; -import org.simplejavamail.api.mailer.AsyncResponse.ExceptionConsumer; import org.simplejavamail.api.mailer.Mailer; import org.simplejavamail.api.mailer.config.TransportStrategy; import org.simplejavamail.mailer.MailerBuilder; @@ -33,6 +30,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; import java.util.stream.Collectors; public class MailSvc implements IMailSvc { @@ -42,14 +41,14 @@ public class MailSvc implements IMailSvc { private final Mailer myMailer; public MailSvc(@Nonnull MailConfig theMailConfig) { - Validate.notNull(theMailConfig); + Objects.requireNonNull(theMailConfig); myMailConfig = theMailConfig; myMailer = makeMailer(myMailConfig); } @Override public void sendMail(@Nonnull List theEmails) { - Validate.notNull(theEmails); + Objects.requireNonNull(theEmails); theEmails.forEach(theEmail -> send(theEmail, new OnSuccess(theEmail), new ErrorHandler(theEmail))); } @@ -60,21 +59,23 @@ public class MailSvc implements IMailSvc { @Override public void sendMail( - @Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull ExceptionConsumer theErrorHandler) { + @Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer theErrorHandler) { send(theEmail, theOnSuccess, theErrorHandler); } private void send( - @Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull ExceptionConsumer theErrorHandler) { - Validate.notNull(theEmail); - Validate.notNull(theOnSuccess); - Validate.notNull(theErrorHandler); + @Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer theErrorHandler) { + Objects.requireNonNull(theEmail); + Objects.requireNonNull(theOnSuccess); + Objects.requireNonNull(theErrorHandler); try { - final AsyncResponse asyncResponse = myMailer.sendMail(theEmail, true); - if (asyncResponse != null) { - asyncResponse.onSuccess(theOnSuccess); - asyncResponse.onException(theErrorHandler); - } + myMailer.sendMail(theEmail, true).whenComplete((result, ex) -> { + if (ex != null) { + theErrorHandler.accept(ex); + } else { + theOnSuccess.run(); + } + }); } catch (MailException e) { theErrorHandler.accept(e); } @@ -117,7 +118,7 @@ public class MailSvc implements IMailSvc { } } - private class ErrorHandler implements ExceptionConsumer { + private class ErrorHandler implements Consumer { private final Email myEmail; private ErrorHandler(@Nonnull Email theEmail) { @@ -125,7 +126,7 @@ public class MailSvc implements IMailSvc { } @Override - public void accept(Exception t) { + public void accept(Throwable t) { ourLog.error("Email not sent" + makeMessage(myEmail), t); } } diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java index 2c2fd6c2d52..a78b4f8c26c 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java @@ -4,6 +4,7 @@ import com.icegreen.greenmail.junit5.GreenMailExtension; import com.icegreen.greenmail.util.GreenMailUtil; import com.icegreen.greenmail.util.ServerSetupTest; import jakarta.annotation.Nonnull; +import jakarta.mail.internet.MimeMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -11,7 +12,6 @@ import org.simplejavamail.MailException; import org.simplejavamail.api.email.Email; import org.simplejavamail.email.EmailBuilder; -import javax.mail.internet.MimeMessage; import java.util.Arrays; import java.util.List; @@ -86,13 +86,14 @@ public class MailSvcIT { @Test public void testSendMailWithInvalidToAddressExpectErrorHandler() { // setup - final Email email = withEmail("xyz"); + String invalidEmailAdress = "xyz"; + final Email email = withEmail(invalidEmailAdress); // execute fixture.sendMail(email, () -> fail("Should not execute on Success"), (e) -> { assertTrue(e instanceof MailException); - assertEquals("Invalid TO address: " + email, e.getMessage()); + assertEquals("Invalid TO address: " + invalidEmailAdress, e.getMessage()); }); // validate assertTrue(ourGreenMail.waitForIncomingEmail(1000, 0)); diff --git a/pom.xml b/pom.xml index 481c3a77047..0f562d546bd 100644 --- a/pom.xml +++ b/pom.xml @@ -1161,27 +1161,38 @@ org.simplejavamail simple-java-mail - 6.6.1 + 8.11.2 - com.sun.activation - jakarta.activation-api + com.github.bbottema + jetbrains-runtime-annotations - com.sun.activation - jakarta.activation + jakarta.mail + jakarta.mail-api + + + + + jakarta.mail + jakarta.mail-api + 2.1.3 + + + com.icegreen + greenmail + 2.1.0-rc-1 + + + jakarta.mail + jakarta.mail-api - - com.icegreen - greenmail - 1.6.4 - com.icegreen greenmail-junit5 - 1.6.4 + 2.1.0-rc-1 compile From 919e2d24050e6cfff0ef584e28903723e22a67ff Mon Sep 17 00:00:00 2001 From: Tadgh Date: Thu, 26 Sep 2024 14:56:49 +0000 Subject: [PATCH 5/6] Attribution for javamail and API bump (#6319) --- .../fhir/changelog/7_6_0/6261-upgrade-to-jakarta-mail.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6261-upgrade-to-jakarta-mail.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6261-upgrade-to-jakarta-mail.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6261-upgrade-to-jakarta-mail.yaml new file mode 100644 index 00000000000..96433fc01cf --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6261-upgrade-to-jakarta-mail.yaml @@ -0,0 +1,5 @@ +--- +type: change +issue: 6261 +title: "Upgrading to Jakarta had caused a problem with the version of java-simple-mail that was in use. This has been updated to be conformant +with the Jakarta Mail APIs. Thanks to Thomas Papke(@thopap) for the contribution!" From fb7571185a693865d4818af536188b9fb89cb3bf Mon Sep 17 00:00:00 2001 From: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:18:35 -0600 Subject: [PATCH 6/6] Composite unique search parameter with dateTime component does not reject resource duplication (#6318) * Composite Unique Search Parameter with dateTime component does not reject resource duplication - failing tests * Composite Unique Search Parameter with dateTime component does not reject resource duplication - implementation * Composite Unique Search Parameter with dateTime component does not reject resource duplication - changelog and test fixes --- ...te-unique-sp-with-date-time-component.yaml | 7 + .../jpa/search/builder/SearchBuilder.java | 20 +- .../extractor/BaseSearchParamExtractor.java | 3 +- .../jpa/dao/r4/BasePartitioningR4Test.java | 12 +- ...FhirResourceDaoR4ComboUniqueParamTest.java | 228 +++++++++++++++--- .../jpa/dao/r4/PartitioningSqlR4Test.java | 28 +-- 6 files changed, 239 insertions(+), 59 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6317-fix-resource-duplication-for-composite-unique-sp-with-date-time-component.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6317-fix-resource-duplication-for-composite-unique-sp-with-date-time-component.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6317-fix-resource-duplication-for-composite-unique-sp-with-date-time-component.yaml new file mode 100644 index 00000000000..f1e379d654c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_6_0/6317-fix-resource-duplication-for-composite-unique-sp-with-date-time-component.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 6317 +title: "Previously, defining a unique combo Search Parameter with the DateTime component and submitting multiple +resources with the same dateTime element (e.g. Observation.effectiveDateTime) resulted in duplicate resource creation. +This has been fixed." + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index bb50e0dd613..59ecfa233ae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -1972,7 +1972,7 @@ public class SearchBuilder implements ISearchBuilder { * The loop allows us to create multiple combo index joins if there * are multiple AND expressions for the related parameters. */ - while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames)) { + while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames, comboParam)) { applyComboSearchParam(theQueryStack, theParams, theRequest, comboParamNames, comboParam); } } @@ -2068,7 +2068,10 @@ public class SearchBuilder implements ISearchBuilder { * (e.g. ?date=gt2024-02-01), etc. */ private boolean validateParamValuesAreValidForComboParam( - RequestDetails theRequest, @Nonnull SearchParameterMap theParams, List theComboParamNames) { + RequestDetails theRequest, + @Nonnull SearchParameterMap theParams, + List theComboParamNames, + RuntimeSearchParam theComboParam) { boolean paramValuesAreValidForCombo = true; List> paramOrValues = new ArrayList<>(theComboParamNames.size()); @@ -2129,6 +2132,19 @@ public class SearchBuilder implements ISearchBuilder { break; } } + + // Date params are not eligible for using composite unique index + // as index could contain date with different precision (e.g. DAY, SECOND) + if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.DATE + && theComboParam.getComboSearchParamType() == ComboSearchParamType.UNIQUE) { + ourLog.debug( + "Search with params {} is not a candidate for combo searching - " + + "Unique combo search parameter '{}' has DATE type", + theComboParamNames, + nextParamName); + paramValuesAreValidForCombo = false; + break; + } } if (CartesianProductUtil.calculateCartesianProductSize(paramOrValues) > 500) { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index a35dc11d554..290c166a38c 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -566,7 +566,8 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) { IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType(); - if (nextParamAsClientParam instanceof DateParam) { + if (theParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE + && nextParamAsClientParam instanceof DateParam) { DateParam date = (DateParam) nextParamAsClientParam; if (date.getPrecision() != TemporalPrecisionEnum.DAY) { continue; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java index 0773223b9f0..de665e28ec0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java @@ -135,10 +135,10 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { addCreateDefaultPartition(); addReadDefaultPartition(); // one for search param validation SearchParameter sp = new SearchParameter(); - sp.setId("SearchParameter/patient-birthdate"); - sp.setType(Enumerations.SearchParamType.DATE); - sp.setCode("birthdate"); - sp.setExpression("Patient.birthDate"); + sp.setId("SearchParameter/patient-gender"); + sp.setType(Enumerations.SearchParamType.TOKEN); + sp.setCode("gender"); + sp.setExpression("Patient.gender"); sp.setStatus(Enumerations.PublicationStatus.ACTIVE); sp.addBase("Patient"); mySearchParameterDao.update(sp, mySrd); @@ -156,13 +156,13 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { addCreateDefaultPartition(); sp = new SearchParameter(); - sp.setId("SearchParameter/patient-birthdate-unique"); + sp.setId("SearchParameter/patient-gender-family-unique"); sp.setType(Enumerations.SearchParamType.COMPOSITE); sp.setStatus(Enumerations.PublicationStatus.ACTIVE); sp.addBase("Patient"); sp.addComponent() .setExpression("Patient") - .setDefinition("SearchParameter/patient-birthdate"); + .setDefinition("SearchParameter/patient-gender"); sp.addComponent() .setExpression("Patient") .setDefinition("SearchParameter/patient-family"); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java index baf3e756028..05f4c5a5e24 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java @@ -13,10 +13,10 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil; import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.DateAndListParam; -import ca.uhn.fhir.rest.param.DateOrListParam; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; @@ -33,6 +33,7 @@ import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.Encounter; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; +import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Organization; @@ -115,6 +116,46 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test myMessages.clear(); } + private void createUniqueGenderFamilyComboSp() { + SearchParameter sp = new SearchParameter(); + sp.setId("SearchParameter/patient-gender"); + sp.setType(Enumerations.SearchParamType.TOKEN); + sp.setCode("gender"); + sp.setExpression("Patient.gender"); + sp.setStatus(PublicationStatus.ACTIVE); + sp.addBase("Patient"); + mySearchParameterDao.update(sp, mySrd); + + sp = new SearchParameter(); + sp.setId("SearchParameter/patient-family"); + sp.setType(Enumerations.SearchParamType.STRING); + sp.setCode("family"); + sp.setExpression("Patient.name.family"); + sp.setStatus(Enumerations.PublicationStatus.ACTIVE); + sp.addBase("Patient"); + mySearchParameterDao.update(sp, mySrd); + + sp = new SearchParameter(); + sp.setId("SearchParameter/patient-gender-family"); + sp.setType(Enumerations.SearchParamType.COMPOSITE); + sp.setStatus(PublicationStatus.ACTIVE); + sp.addBase("Patient"); + sp.addComponent() + .setExpression("Patient") + .setDefinition("SearchParameter/patient-gender"); + sp.addComponent() + .setExpression("Patient") + .setDefinition("SearchParameter/patient-family"); + sp.addExtension() + .setUrl(HapiExtensions.EXT_SP_UNIQUE) + .setValue(new BooleanType(true)); + mySearchParameterDao.update(sp, mySrd); + + mySearchParamRegistry.forceRefresh(); + + myMessages.clear(); + } + private void createUniqueIndexCoverageBeneficiary() { SearchParameter sp = new SearchParameter(); sp.setId("SearchParameter/coverage-beneficiary"); @@ -276,6 +317,45 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test mySearchParamRegistry.forceRefresh(); } + private void createUniqueObservationDateCode() { + SearchParameter sp = new SearchParameter(); + sp.setId("SearchParameter/obs-effective"); + sp.setType(Enumerations.SearchParamType.DATE); + sp.setCode("date"); + sp.setExpression("Observation.effective"); + sp.setStatus(PublicationStatus.ACTIVE); + sp.addBase("Observation"); + mySearchParameterDao.update(sp, mySrd); + + sp = new SearchParameter(); + sp.setId("SearchParameter/obs-code"); + sp.setType(Enumerations.SearchParamType.TOKEN); + sp.setCode("code"); + sp.setExpression("Observation.code"); + sp.setStatus(PublicationStatus.ACTIVE); + sp.addBase("Observation"); + mySearchParameterDao.update(sp, mySrd); + + sp = new SearchParameter(); + sp.setId("SearchParameter/observation-date-code"); + sp.setType(Enumerations.SearchParamType.COMPOSITE); + sp.setStatus(PublicationStatus.ACTIVE); + sp.addBase("Observation"); + sp.setExpression("Observation.code"); + sp.addComponent() + .setExpression("Observation") + .setDefinition("SearchParameter/obs-effective"); + sp.addComponent() + .setExpression("Observation") + .setDefinition("SearchParameter/obs-code"); + sp.addExtension() + .setUrl(HapiExtensions.EXT_SP_UNIQUE) + .setValue(new BooleanType(true)); + mySearchParameterDao.update(sp, mySrd); + + mySearchParamRegistry.forceRefresh(); + } + private void createUniqueObservationSubjectDateCode() { SearchParameter sp = new SearchParameter(); sp.setId("SearchParameter/obs-subject"); @@ -471,11 +551,11 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test public void testDoubleMatchingOnAnd_Search_TwoAndOrValues() { myStorageSettings.setUniqueIndexesCheckedBeforeSave(false); - createUniqueBirthdateAndGenderSps(); + createUniqueGenderFamilyComboSp(); Patient pt1 = new Patient(); pt1.setGender(Enumerations.AdministrativeGender.MALE); - pt1.setBirthDateElement(new DateType("2011-01-01")); + pt1.getName().add(new HumanName().setFamily("Family1")); String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); // Two OR values on the same resource - Currently composite SPs don't work for this @@ -484,17 +564,22 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test sp.setLoadSynchronous(true); sp.add(Patient.SP_GENDER, new TokenAndListParam() - .addAnd(new TokenParam("http://hl7.org/fhir/administrative-gender","male"), new TokenParam( "http://hl7.org/fhir/administrative-gender","female")) + .addAnd(new TokenParam("http://hl7.org/fhir/administrative-gender","male"), + new TokenParam( "http://hl7.org/fhir/administrative-gender","female")) ); - sp.add(Patient.SP_BIRTHDATE, - new DateAndListParam() - .addAnd(new DateParam("2011-01-01"), new DateParam( "2011-02-02")) + sp.add(Patient.SP_FAMILY, + new StringOrListParam() + .addOr(new StringParam("Family1")).addOr(new StringParam("Family2")) ); IBundleProvider outcome = myPatientDao.search(sp, mySrd); myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); assertThat(toUnqualifiedVersionlessIdValues(outcome)).containsExactlyInAnyOrder(id1); String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN ('Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale','Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale','Patient?birthdate=2011-02-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale','Patient?birthdate=2011-02-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )", unformattedSql); + assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN (" + + "'Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale'," + + "'Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale'," + + "'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cfemale'," + + "'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )", unformattedSql); } @@ -1167,16 +1252,16 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test @Test public void testOrQuery() { myStorageSettings.setAdvancedHSearchIndexing(false); - createUniqueBirthdateAndGenderSps(); + createUniqueGenderFamilyComboSp(); Patient pt1 = new Patient(); pt1.setGender(Enumerations.AdministrativeGender.MALE); - pt1.setBirthDateElement(new DateType("2011-01-01")); + pt1.getName().add(new HumanName().setFamily("Family1")); IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless(); Patient pt2 = new Patient(); pt2.setGender(Enumerations.AdministrativeGender.MALE); - pt2.setBirthDateElement(new DateType("2011-01-02")); + pt2.getName().add(new HumanName().setFamily("Family2")); IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); myCaptureQueriesListener.clear(); @@ -1184,16 +1269,21 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test SearchParameterMap params = new SearchParameterMap(); params.setLoadSynchronousUpTo(100); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); - params.add("birthdate", new DateOrListParam().addOr(new DateParam("2011-01-01")).addOr(new DateParam("2011-01-02"))); + params.add("family", new StringOrListParam() + .addOr(new StringParam("Family1")).addOr(new StringParam("Family2"))); myCaptureQueriesListener.clear(); IBundleProvider results = myPatientDao.search(params, mySrd); myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue(), id2.getValue()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false)) - .contains("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN ('Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale','Patient?birthdate=2011-01-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )"); + .contains("SELECT t0.RES_ID FROM HFJ_IDX_CMP_STRING_UNIQ t0 WHERE (t0.IDX_STRING IN " + + "('Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale'," + + "'Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale') )"); logCapturedMessages(); - assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: [Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale, Patient?birthdate=2011-01-02&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale]"); + assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " + + "[Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale, " + + "Patient?family=Family2&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale]"); myMessages.clear(); } @@ -1201,16 +1291,16 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test @Test public void testSearchSynchronousUsingUniqueComposite() { myStorageSettings.setAdvancedHSearchIndexing(false); - createUniqueBirthdateAndGenderSps(); + createUniqueGenderFamilyComboSp(); Patient pt1 = new Patient(); pt1.setGender(Enumerations.AdministrativeGender.MALE); - pt1.setBirthDateElement(new DateType("2011-01-01")); + pt1.getName().add(new HumanName().setFamily("Family1")); IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless(); Patient pt2 = new Patient(); pt2.setGender(Enumerations.AdministrativeGender.MALE); - pt2.setBirthDateElement(new DateType("2011-01-02")); + pt2.getName().add(new HumanName().setFamily("Family2")); myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); myCaptureQueriesListener.clear(); @@ -1218,13 +1308,14 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test SearchParameterMap params = new SearchParameterMap(); params.setLoadSynchronousUpTo(100); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); - params.add("birthdate", new DateParam("2011-01-01")); + params.add("family", new StringParam("Family1")); IBundleProvider results = myPatientDao.search(params, mySrd); myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1.getValue()); logCapturedMessages(); - assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); + assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " + + "Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); myMessages.clear(); } @@ -1232,33 +1323,34 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test @Test public void testSearchUsingUniqueComposite() { - createUniqueBirthdateAndGenderSps(); + createUniqueGenderFamilyComboSp(); Patient pt1 = new Patient(); pt1.setGender(Enumerations.AdministrativeGender.MALE); - pt1.setBirthDateElement(new DateType("2011-01-01")); + pt1.getName().add(new HumanName().setFamily("Family1")); String id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); Patient pt2 = new Patient(); pt2.setGender(Enumerations.AdministrativeGender.MALE); - pt2.setBirthDateElement(new DateType("2011-01-02")); + pt2.getName().add(new HumanName().setFamily("Family2")); myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); myMessages.clear(); SearchParameterMap params = new SearchParameterMap(); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); - params.add("birthdate", new DateParam("2011-01-01")); + params.add("family", new StringParam("Family1")); IBundleProvider results = myPatientDao.search(params, mySrd); String searchId = results.getUuid(); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id1); logCapturedMessages(); - assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); + assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " + + "Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); myMessages.clear(); // Other order myMessages.clear(); params = new SearchParameterMap(); - params.add("birthdate", new DateParam("2011-01-01")); + params.add("family", new StringParam("Family1")); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); results = myPatientDao.search(params, mySrd); assertEquals(searchId, results.getUuid()); @@ -1272,16 +1364,17 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test myMessages.clear(); params = new SearchParameterMap(); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); - params.add("birthdate", new DateParam("2011-01-03")); + params.add("family", new StringParam("Family3")); results = myPatientDao.search(params, mySrd); assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty(); logCapturedMessages(); - assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-03&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); + assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " + + "Patient?family=Family3&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); myMessages.clear(); myMessages.clear(); params = new SearchParameterMap(); - params.add("birthdate", new DateParam("2011-01-03")); + params.add("family", new StringParam("Family3")); results = myPatientDao.search(params, mySrd); assertThat(toUnqualifiedVersionlessIdValues(results)).isEmpty(); // STANDARD QUERY @@ -1666,7 +1759,7 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test } @Test - public void testDuplicateUniqueValuesAreRejected() { + public void testDuplicateUniqueValuesWithDateAreRejected() { createUniqueBirthdateAndGenderSps(); Patient pt1 = new Patient(); @@ -1699,13 +1792,75 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test } @Test - public void testReplaceOneWithAnother() { - myStorageSettings.setAdvancedHSearchIndexing(false); + public void testDuplicateUniqueValuesWithDateTimeAreRejected() { + createUniqueObservationDateCode(); + + Observation obs = new Observation(); + obs.getCode().addCoding().setSystem("foo").setCode("bar"); + obs.setEffective(new DateTimeType("2017-10-10T00:00:00")); + myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + + try { + myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); + fail(); + } catch (ResourceVersionConflictException e) { + assertThat(e.getMessage()) + .contains("new unique index created by SearchParameter/observation-date-code"); + } + } + + @Test + public void testUniqueComboSearchWithDateNotUsingUniqueIndex() { createUniqueBirthdateAndGenderSps(); Patient pt1 = new Patient(); pt1.setGender(Enumerations.AdministrativeGender.MALE); pt1.setBirthDateElement(new DateType("2011-01-01")); + String pId = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue(); + + myCaptureQueriesListener.clear(); + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronousUpTo(100); + params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); + params.add("birthdate", new DateParam("2011-01-01")); + + IBundleProvider results = myPatientDao.search(params, mySrd); + assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(pId); + myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); + String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertThat(unformattedSql).doesNotContain("HFJ_IDX_CMP_STRING_UNIQ"); + } + + @Test + public void testUniqueComboSearchWithDateTimeNotUsingUniqueIndex() { + createUniqueObservationDateCode(); + + Observation obs = new Observation(); + obs.getCode().addCoding().setSystem("foo").setCode("bar"); + obs.setEffective(new DateTimeType("2017-10-10T00:00:00")); + String obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless().getValue(); + + myCaptureQueriesListener.clear(); + SearchParameterMap params = new SearchParameterMap(); + params.setLoadSynchronousUpTo(100); + params.add("code", new TokenParam("foo", "bar")); + params.add("date", new DateParam("2017-10-10T00:00:00")); + + IBundleProvider results = myObservationDao.search(params, mySrd); + assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(obsId); + myCaptureQueriesListener.logFirstSelectQueryForCurrentThread(); + String unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertThat(unformattedSql).doesNotContain("HFJ_IDX_CMP_STRING_UNIQ"); + } + + @Test + public void testReplaceOneWithAnother() { + myStorageSettings.setAdvancedHSearchIndexing(false); + createUniqueGenderFamilyComboSp(); + + Patient pt1 = new Patient(); + pt1.setGender(Enumerations.AdministrativeGender.MALE); + pt1.getName().add(new HumanName().setFamily("Family1")); IIdType id1 = myPatientDao.create(pt1, mySrd).getId().toUnqualified(); assertNotNull(id1); @@ -1714,27 +1869,28 @@ public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test pt1 = new Patient(); pt1.setId(id1); pt1.setGender(Enumerations.AdministrativeGender.FEMALE); - pt1.setBirthDateElement(new DateType("2011-01-01")); + pt1.getName().add(new HumanName().setFamily("Family1")); id1 = myPatientDao.update(pt1, mySrd).getId().toUnqualified(); assertNotNull(id1); assertEquals("2", id1.getVersionIdPart()); Patient pt2 = new Patient(); pt2.setGender(Enumerations.AdministrativeGender.MALE); - pt2.setBirthDateElement(new DateType("2011-01-01")); + pt2.getName().add(new HumanName().setFamily("Family1")); IIdType id2 = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless(); myMessages.clear(); SearchParameterMap params = new SearchParameterMap(); params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male")); - params.add("birthdate", new DateParam("2011-01-01")); + params.add("family", new StringParam("Family1")); IBundleProvider results = myPatientDao.search(params, mySrd); String searchId = results.getUuid(); assertThat(searchId).isNotBlank(); assertThat(toUnqualifiedVersionlessIdValues(results)).containsExactlyInAnyOrder(id2.getValue()); logCapturedMessages(); - assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); + assertThat(myMessages.toString()).contains("Using UNIQUE index(es) for query for search: " + + "Patient?family=Family1&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"); myMessages.clear(); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java index 9eddb6767e8..ee0cce41228 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java @@ -413,7 +413,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { p.getMeta().addTag("http://system", "code", "diisplay"); p.addName().setFamily("FAM"); p.addIdentifier().setSystem("system").setValue("value"); - p.setBirthDateElement(new DateType("2020-01-01")); + p.setGender(Enumerations.AdministrativeGender.MALE); p.getManagingOrganization().setReferenceElement(orgId); Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -502,7 +502,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { p.getMeta().addTag("http://system", "code", "diisplay"); p.addName().setFamily("FAM"); p.addIdentifier().setSystem("system").setValue("value"); - p.setBirthDate(new Date()); + p.setGender(Enumerations.AdministrativeGender.MALE); p.getManagingOrganization().setReferenceElement(orgId); Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong(); @@ -679,7 +679,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { p.getMeta().addTag("http://system", "code", "display"); p.addName().setFamily("FAM"); p.addIdentifier().setSystem("system").setValue("value"); - p.setBirthDate(new Date()); + p.setGender(Enumerations.AdministrativeGender.MALE); p.getManagingOrganization().setReference(org.getId()); input.addEntry() .setFullUrl(p.getId()) @@ -2541,14 +2541,14 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { public void testSearch_UniqueParam_SearchAllPartitions() { createUniqueComboSp(); - IIdType id = createPatient(withPartition(1), withBirthdate("2020-01-01"), withFamily("FAM")); + IIdType id = createPatient(withPartition(1), withGender("male"), withFamily("FAM")); addReadAllPartitions(); myCaptureQueriesListener.clear(); SearchParameterMap map = new SearchParameterMap(); map.add(Patient.SP_FAMILY, new StringParam("FAM")); - map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01")); + map.add(Patient.SP_GENDER, new TokenParam(null, "male")); map.setLoadSynchronous(true); IBundleProvider results = myPatientDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIds(results); @@ -2558,7 +2558,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); ourLog.info("Search SQL:\n{}", searchSql); assertThat(searchSql).doesNotContain("PARTITION_ID"); - assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?birthdate=2020-01-01&family=FAM'"); + assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?family=FAM&gender=male'"); } @@ -2566,13 +2566,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { public void testSearch_UniqueParam_SearchOnePartition() { createUniqueComboSp(); - IIdType id = createPatient(withPartition(1), withBirthdate("2020-01-01"), withFamily("FAM")); + IIdType id = createPatient(withPartition(1), withGender("male"), withFamily("FAM")); addReadPartition(1); myCaptureQueriesListener.clear(); SearchParameterMap map = new SearchParameterMap(); map.add(Patient.SP_FAMILY, new StringParam("FAM")); - map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01")); + map.add(Patient.SP_GENDER, new TokenParam(null, "male")); map.setLoadSynchronous(true); IBundleProvider results = myPatientDao.search(map, mySrd); List ids = toUnqualifiedVersionlessIds(results); @@ -2582,13 +2582,13 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); ourLog.info("Search SQL:\n{}", searchSql); assertThat(searchSql).containsOnlyOnce( "PARTITION_ID = '1'"); - assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?birthdate=2020-01-01&family=FAM'"); + assertThat(searchSql).containsOnlyOnce("IDX_STRING = 'Patient?family=FAM&gender=male'"); // Same query, different partition addReadPartition(2); myCaptureQueriesListener.clear(); map = new SearchParameterMap(); - map.add(Patient.SP_BIRTHDATE, new DateParam("2020-01-01")); + map.add(Patient.SP_GENDER, new TokenParam(null, "male")); map.setLoadSynchronous(true); results = myPatientDao.search(map, mySrd); ids = toUnqualifiedVersionlessIds(results); @@ -2661,7 +2661,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { public void testSearch_RefParam_TargetPid_SearchOnePartition() { createUniqueComboSp(); - IIdType patientId = createPatient(withPartition(myPartitionId), withBirthdate("2020-01-01")); + IIdType patientId = createPatient(withPartition(myPartitionId), withGender("male")); IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId)); addReadPartition(myPartitionId); @@ -2698,7 +2698,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { public void testSearch_RefParam_TargetPid_SearchDefaultPartition() { createUniqueComboSp(); - IIdType patientId = createPatient(withPartition(null), withBirthdate("2020-01-01")); + IIdType patientId = createPatient(withPartition(null), withGender("male")); IIdType observationId = createObservation(withPartition(null), withSubject(patientId)); addReadDefaultPartition(); @@ -2735,7 +2735,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { public void testSearch_RefParam_TargetForcedId_SearchOnePartition() { createUniqueComboSp(); - IIdType patientId = createPatient(withPartition(myPartitionId), withId("ONE"), withBirthdate("2020-01-01")); + IIdType patientId = createPatient(withPartition(myPartitionId), withId("ONE"), withGender("male")); IIdType observationId = createObservation(withPartition(myPartitionId), withSubject(patientId)); addReadPartition(myPartitionId); @@ -2805,7 +2805,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { public void testSearch_RefParam_TargetForcedId_SearchDefaultPartition() { createUniqueComboSp(); - IIdType patientId = createPatient(withPartition(null), withId("ONE"), withBirthdate("2020-01-01")); + IIdType patientId = createPatient(withPartition(null), withId("ONE"), withGender("male")); IIdType observationId = createObservation(withPartition(null), withSubject(patientId)); addReadDefaultPartition();