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

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

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

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

+ * + * @since 4.1.0 + */ +public abstract class BaseLastNProvider { + + @Operation(name = Constants.OPERATION_LASTN, typeName = "Observation", idempotent = true) + public IBaseBundle lastN( + ServletRequestDetails theRequestDetails, + @OperationParam(name = "subject", typeName = "reference", min = 0, max = 1) IBaseReference theSubject, + @OperationParam(name = "category", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List theCategories, + @OperationParam(name = "code", typeName = "coding", min = 0, max = OperationParam.MAX_UNLIMITED) List theCodes, + @OperationParam(name = "max", typeName = "integer", min = 0, max = 1) IPrimitiveType theMax + ) { + return processLastN(theSubject, theCategories, theCodes, theMax); + } + + /** + * Subclasses should implement this method + */ + protected abstract IBaseBundle processLastN(IBaseReference theSubject, List theCategories, List theCodes, IPrimitiveType theMax); + + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java new file mode 100644 index 00000000000..3b5b4c6376f --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java @@ -0,0 +1,44 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.test.utilities.JettyUtil; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.After; + +public class BaseR4ServerTest { + private FhirContext myCtx = FhirContext.forR4(); + private Server myServer; + protected IGenericClient myClient; + protected String myBaseUrl; + + @After + public void after() throws Exception { + JettyUtil.closeServer(myServer); + } + + protected void startServer(Object theProvider) throws Exception { + RestfulServer servlet = new RestfulServer(myCtx); + servlet.registerProvider(theProvider); + ServletHandler proxyHandler = new ServletHandler(); + servlet.setDefaultResponseEncoding(EncodingEnum.XML); + servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + + myServer = new Server(0); + myServer.setHandler(proxyHandler); + JettyUtil.startServer(myServer); + int port = JettyUtil.getPortForStartedServer(myServer); + + myBaseUrl = "http://localhost:" + port; + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + myClient = myCtx.newRestfulGenericClient(myBaseUrl); + } + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java new file mode 100644 index 00000000000..bba6c0d3b4e --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java @@ -0,0 +1,64 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.rest.server.provider.BaseLastNProvider; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.Bundle; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class LastNProviderTest extends BaseR4ServerTest { + + private IBaseReference myLastSubject; + private List myLastCategories; + private List myLastCodes; + private IPrimitiveType myLastMax; + + @Test + public void testAllParamsPopulated() throws Exception { + + class MyProvider extends BaseLastNProvider { + + @Override + protected IBaseBundle processLastN(IBaseReference theSubject, List theCategories, List theCodes, IPrimitiveType theMax) { + myLastSubject = theSubject; + myLastCategories = theCategories; + myLastCodes = theCodes; + myLastMax = theMax; + + Bundle retVal = new Bundle(); + retVal.setId("abc123"); + retVal.setType(Bundle.BundleType.SEARCHSET); + return retVal; + } + } + MyProvider provider = new MyProvider(); + startServer(provider); + + Bundle response = myClient + .search() + .byUrl(myBaseUrl + "/Observation/$lastn?subject=Patient/123&category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory,http://terminology.hl7.org/CodeSystem/observation-category|vital-signs&code=http://loinc.org|1111-1,http://loinc.org|2222-2&max=15") + .returnBundle(Bundle.class) + .execute(); + assertEquals("abc123", response.getIdElement().getIdPart()); + assertEquals("Patient/123", myLastSubject.getReferenceElement().getValue()); + assertEquals(2, myLastCategories.size()); + assertEquals("http://terminology.hl7.org/CodeSystem/observation-category", myLastCategories.get(0).getSystem()); + assertEquals("laboratory", myLastCategories.get(0).getCode()); + assertEquals("http://terminology.hl7.org/CodeSystem/observation-category", myLastCategories.get(1).getSystem()); + assertEquals("vital-signs", myLastCategories.get(1).getCode()); + assertEquals(2, myLastCodes.size()); + assertEquals("http://loinc.org", myLastCodes.get(0).getSystem()); + assertEquals("1111-1", myLastCodes.get(0).getCode()); + assertEquals("http://loinc.org", myLastCodes.get(1).getSystem()); + assertEquals("2222-2", myLastCodes.get(1).getCode()); + assertEquals(15, myLastMax.getValue().intValue()); + } + + +} diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java index 14fa05826ef..3204c2c629f 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java @@ -1,24 +1,15 @@ package ca.uhn.fhir.rest.server; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; -import org.junit.After; import org.junit.Test; import java.util.List; @@ -27,18 +18,9 @@ import java.util.Set; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; -public class ServerMethodSelectionR4Test { +public class ServerMethodSelectionR4Test extends BaseR4ServerTest { - private FhirContext myCtx = FhirContext.forR4(); - private Server myServer; - private IGenericClient myClient; - - @After - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } - /** * Server method with no _include * Client request with _include @@ -161,22 +143,6 @@ public class ServerMethodSelectionR4Test { assertEquals(1, results.getEntry().size()); } - private void startServer(Object theProvider) throws Exception { - RestfulServer servlet = new RestfulServer(myCtx); - servlet.registerProvider(theProvider); - ServletHandler proxyHandler = new ServletHandler(); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - - myServer = new Server(0); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - int port = JettyUtil.getPortForStartedServer(myServer); - - myClient = myCtx.newRestfulGenericClient("http://localhost:" + port); - } public static class MyBaseProvider implements IResourceProvider { @@ -185,6 +151,7 @@ public class ServerMethodSelectionR4Test { public Class getResourceType() { return Patient.class; } + } } diff --git a/pom.xml b/pom.xml index 55b1dd4ae39..bc7792167a8 100755 --- a/pom.xml +++ b/pom.xml @@ -601,7 +601,7 @@ 5.4.4.Final - 5.11.1.Final + 5.11.3.Final 5.5.5 5.4.2.Final 4.4.11 @@ -857,18 +857,6 @@ 7.0.0.jre8 - javax.mail javax.mail-api @@ -1405,6 +1393,11 @@ xmlunit-core 2.4.0 + + pl.allegro.tech + embedded-elasticsearch + 2.10.0 + xpp3 xpp3 @@ -1669,6 +1662,11 @@ org.jacoco jacoco-maven-plugin 0.8.4 + + + ca/uhn/fhir/model/dstu2/**/*.class + + org.apache.maven.plugins @@ -2391,7 +2389,6 @@ hapi-fhir-jaxrsserver-base hapi-fhir-jaxrsserver-example hapi-fhir-jpaserver-base - hapi-fhir-jpaserver-elasticsearch hapi-fhir-jpaserver-migrate restful-server-example hapi-fhir-testpage-overlay diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d1ef194eab9..0beec66cdf9 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -12,7 +12,8 @@ latest versions (dependent HAPI modules listed in brackets): -
  • Hibernate Core (Core): 5.4.2.Final -> 5.4.4.Final
  • +
  • Hibernate Core (JPA): 5.4.2.Final -> 5.4.4.Final
  • +
  • Hibernate Search (JPA): 5.11.1.Final -> 5.11.3.Final
  • Jackson Databind (JPA): 2.9.9 -> 2.9.10 (CVE-2019-16335, CVE-2019-14540)
  • ]]> @@ -222,6 +223,11 @@ The JPA server failed to find codes defined in not-present codesystems in some cases, and reported that the CodeSystem did not exist. This has been corrected. + + Support for ElasticSearch has been added to the JPA server directly (i.e. without needing a separate + module) and a new class called "ElasticsearchHibernatePropertiesBuilder" has been added to facilitate + the creation of relevant properties. +