From 7831250d9273d53e7fb876178b8d9e2082d67a4b Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 11 Aug 2019 18:35:43 -0400 Subject: [PATCH] Get R5 support working --- pom.xml | 7 + .../fhir/jpa/starter/ApplicationContext.java | 2 + .../fhir/jpa/starter/FhirServerConfigR5.java | 58 +++++++ .../uhn/fhir/jpa/starter/HapiProperties.java | 15 ++ .../fhir/jpa/starter/JpaRestfulServer.java | 59 +++++-- src/main/resources/hapi.properties | 12 ++ .../jpa/starter/ExampleServerDstu2IT.java | 13 +- .../jpa/starter/ExampleServerDstu3IT.java | 44 +++-- .../fhir/jpa/starter/ExampleServerR4IT.java | 18 +- .../fhir/jpa/starter/ExampleServerR5IT.java | 157 ++++++++++++++++++ 10 files changed, 332 insertions(+), 53 deletions(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java create mode 100644 src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java diff --git a/pom.xml b/pom.xml index b875f33..9df45f1 100644 --- a/pom.xml +++ b/pom.xml @@ -198,6 +198,13 @@ provided + + ca.uhn.hapi.fhir + hapi-fhir-test-utilities + ${project.version} + test + + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java index ddddbe0..24b4f79 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java @@ -13,6 +13,8 @@ public class ApplicationContext extends AnnotationConfigWebApplicationContext { register(FhirServerConfigDstu3.class, FhirServerConfigCommon.class); } else if (fhirVersion == FhirVersionEnum.R4) { register(FhirServerConfigR4.class, FhirServerConfigCommon.class); + } else if (fhirVersion == FhirVersionEnum.R5) { + register(FhirServerConfigR5.class, FhirServerConfigCommon.class); } else { throw new IllegalStateException(); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java new file mode 100644 index 0000000..e176080 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; +import ca.uhn.fhir.jpa.config.BaseJavaConfigR5; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +@Configuration +public class FhirServerConfigR5 extends BaseJavaConfigR5 { + + @Autowired + private DataSource myDataSource; + + /** + * We override the paging provider definition so that we can customize + * the default/max page sizes for search results. You can set these however + * you want, although very large page sizes will require a lot of RAM. + */ + @Override + public DatabaseBackedPagingProvider databaseBackedPagingProvider() { + DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider(); + pagingProvider.setDefaultPageSize(HapiProperties.getDefaultPageSize()); + pagingProvider.setMaximumPageSize(HapiProperties.getMaximumPageSize()); + return pagingProvider; + } + + @Override + @Bean() + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); + retVal.setPersistenceUnitName("HAPI_PU"); + + try { + retVal.setDataSource(myDataSource); + } catch (Exception e) { + throw new ConfigurationException("Could not set the data source due to a configuration issue", e); + } + + retVal.setJpaProperties(HapiProperties.getProperties()); + return retVal; + } + + @Bean() + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(entityManagerFactory); + return retVal; + } + +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index fe0b2bb..3ef8bcb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -52,6 +52,8 @@ public class HapiProperties { static final String ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS = "allow_override_default_search_params"; static final String EMAIL_FROM = "email.from"; public static final String BINARY_STORAGE_ENABLED = "binary_storage.enabled"; + private static final String VALIDATE_REQUESTS_ENABLED = "validation.requests.enabled"; + private static final String VALIDATE_RESPONSES_ENABLED = "validation.responses.enabled"; private static Properties properties; @@ -146,6 +148,10 @@ public class HapiProperties { return Boolean.parseBoolean(value); } + private static boolean getBooleanProperty(String propertyName, boolean defaultValue) { + return getBooleanProperty(propertyName, Boolean.valueOf(defaultValue)); + } + private static Integer getIntegerProperty(String propertyName, Integer defaultValue) { String value = HapiProperties.getProperty(propertyName); @@ -346,4 +352,13 @@ public class HapiProperties { String value = HapiProperties.getProperty(REUSE_CACHED_SEARCH_RESULTS_MILLIS, "-1"); return Long.valueOf(value); } + + public static boolean getValidateRequestsEnabled() { + return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); + } + + public static boolean getValidateResponsesEnabled() { + return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false); + } + } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index ec211bb..c8fa268 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor; -import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; @@ -13,12 +12,13 @@ import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; +import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5; +import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader; import ca.uhn.fhir.jpa.subscription.module.interceptor.SubscriptionDebugLogInterceptor; @@ -27,9 +27,9 @@ import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; -import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import ca.uhn.fhir.rest.server.interceptor.*; +import ca.uhn.fhir.validation.IValidatorModule; +import ca.uhn.fhir.validation.ResultSeverityEnum; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Meta; import org.springframework.context.ApplicationContext; @@ -37,6 +37,7 @@ import org.springframework.web.cors.CorsConfiguration; import javax.servlet.ServletException; import java.util.Arrays; +import java.util.Collections; import java.util.Set; public class JpaRestfulServer extends RestfulServer { @@ -76,6 +77,9 @@ public class JpaRestfulServer extends RestfulServer { } else if (fhirVersion == FhirVersionEnum.R4) { resourceProviders = appCtx.getBean("myResourceProvidersR4", ResourceProviderFactory.class); systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class); + } else if (fhirVersion == FhirVersionEnum.R5) { + resourceProviders = appCtx.getBean("myResourceProvidersR5", ResourceProviderFactory.class); + systemProvider = appCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class); } else { throw new IllegalStateException(); } @@ -108,6 +112,11 @@ public class JpaRestfulServer extends RestfulServer { JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, appCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription("HAPI FHIR R4 Server"); setServerConformanceProvider(confProvider); + } else if (fhirVersion == FhirVersionEnum.R5) { + IFhirSystemDao systemDao = appCtx.getBean("mySystemDaoR5", IFhirSystemDao.class); + JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, appCtx.getBean(DaoConfig.class)); + confProvider.setImplementationDescription("HAPI FHIR R5 Server"); + setServerConformanceProvider(confProvider); } else { throw new IllegalStateException(); } @@ -179,11 +188,7 @@ public class JpaRestfulServer extends RestfulServer { * with this feature. */ if (false) { // <-- DISABLED RIGHT NOW - if (fhirVersion == FhirVersionEnum.DSTU3) { - registerProvider(appCtx.getBean(TerminologyUploaderProviderDstu3.class)); - } else if (fhirVersion == FhirVersionEnum.R4) { - registerProvider(appCtx.getBean(TerminologyUploaderProviderR4.class)); - } + registerProvider(appCtx.getBean(TerminologyUploaderProvider.class)); } // If you want to enable the $trigger-subscription operation to allow @@ -244,6 +249,38 @@ public class JpaRestfulServer extends RestfulServer { BinaryStorageInterceptor binaryStorageInterceptor = appCtx.getBean(BinaryStorageInterceptor.class); getInterceptorService().registerInterceptor(binaryStorageInterceptor); } + + // Validation + IValidatorModule validatorModule; + switch (fhirVersion) { + case DSTU3: + validatorModule = appCtx.getBean("myInstanceValidatorDstu3", IValidatorModule.class); + break; + case R4: + validatorModule = appCtx.getBean("myInstanceValidatorR4", IValidatorModule.class); + break; + case R5: + validatorModule = appCtx.getBean("myInstanceValidatorR5", IValidatorModule.class); + break; + default: + validatorModule = null; + break; + } + if (validatorModule != null) { + if (HapiProperties.getValidateRequestsEnabled()) { + RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor(); + interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); + interceptor.setValidatorModules(Collections.singletonList(validatorModule)); + registerInterceptor(interceptor); + } + if (HapiProperties.getValidateResponsesEnabled()) { + ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor(); + interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); + interceptor.setValidatorModules(Collections.singletonList(validatorModule)); + registerInterceptor(interceptor); + } + } + } } diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index 8b5a4be..6b23b23 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -39,6 +39,18 @@ server.name=Local Tester server.id=home test.port= +################################################### +# Validation +################################################### +# Should all incoming requests be validated +validation.requests.enabled=false +# Should outgoing responses be validated +validation.responses.enabled=false + + +################################################### +# Supported Resources +################################################### # Enable the following property if you want to customize the # list of resources that is supported by the server (i.e. to # disable specific resources) diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java index 70b4e5f..ee6ebf8 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.hl7.fhir.instance.model.api.IIdType; @@ -23,16 +23,13 @@ public class ExampleServerDstu2IT { private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; - private static Server ourServer; - private static String ourServerBase; static { HapiProperties.forceReload(); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU2"); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr2;create=true"); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr2"); ourCtx = FhirContext.forDstu2(); - ourPort = PortUtil.findFreePort(); } @Test @@ -59,7 +56,7 @@ public class ExampleServerDstu2IT { ourLog.info("Project base path is: {}", path); - ourServer = new Server(ourPort); + ourServer = new Server(0); WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); @@ -70,9 +67,11 @@ public class ExampleServerDstu2IT { ourServer.setHandler(webAppContext); ourServer.start(); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; + String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java index 4580f9c..7507bfb 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -1,18 +1,13 @@ package ca.uhn.fhir.jpa.starter; -import static ca.uhn.fhir.util.TestUtil.waitForSize; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.Paths; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; @@ -23,12 +18,17 @@ import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Subscription; import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.*; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static ca.uhn.fhir.util.TestUtil.waitForSize; +import static org.junit.Assert.assertEquals; public class ExampleServerDstu3IT { @@ -36,17 +36,14 @@ public class ExampleServerDstu3IT { private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; - private static Server ourServer; - private static String ourServerBase; static { HapiProperties.forceReload(); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU3"); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr3;create=true"); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr3"); HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true"); ourCtx = FhirContext.forDstu3(); - ourPort = PortUtil.findFreePort(); } @Test @@ -131,10 +128,7 @@ public class ExampleServerDstu3IT { ourLog.info("Project base path is: {}", path); - if (ourPort == 0) { - ourPort = RandomServerPortProvider.findFreePort(); - } - ourServer = new Server(ourPort); + ourServer = new Server(0); WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); @@ -145,9 +139,11 @@ public class ExampleServerDstu3IT { ourServer.setHandler(webAppContext); ourServer.start(); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; + String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); } diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java index bff12bb..1eedafd 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.util.PortUtil; +import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; @@ -36,22 +36,19 @@ public class ExampleServerR4IT { private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; - private static Server ourServer; - private static String ourServerBase; static { HapiProperties.forceReload(); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr4;create=true"); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4"); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4"); HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true"); ourCtx = FhirContext.forR4(); - ourPort = PortUtil.findFreePort(); } @Test public void testCreateAndRead() { - ourLog.info("Base URL is: " + HapiProperties.getServerAddress()); + ourLog.info("Base URL is: " + HapiProperties.getServerAddress()); String methodName = "testCreateResourceConditional"; Patient pt = new Patient(); @@ -131,10 +128,7 @@ public class ExampleServerR4IT { ourLog.info("Project base path is: {}", path); - if (ourPort == 0) { - ourPort = RandomServerPortProvider.findFreePort(); - } - ourServer = new Server(ourPort); + ourServer = new Server(0); WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); @@ -146,9 +140,11 @@ public class ExampleServerR4IT { ourServer.setHandler(webAppContext); ourServer.start(); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = HapiProperties.getServerAddress(); + String ourServerBase = HapiProperties.getServerAddress(); ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java new file mode 100644 index 0000000..04aee05 --- /dev/null +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java @@ -0,0 +1,157 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.CacheControlDirective; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.JettyUtil; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Subscription; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.net.URI; +import java.nio.file.Paths; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static ca.uhn.fhir.util.TestUtil.waitForSize; +import static org.junit.Assert.assertEquals; + +public class ExampleServerR5IT { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR5IT.class); + private static IGenericClient ourClient; + private static FhirContext ourCtx; + private static int ourPort; + private static Server ourServer; + + static { + HapiProperties.forceReload(); + HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr5"); + HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R5"); + HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true"); + ourCtx = FhirContext.forR4(); + } + + @Test + public void testCreateAndRead() { + ourLog.info("Base URL is: " + HapiProperties.getServerAddress()); + String methodName = "testCreateResourceConditional"; + + Patient pt = new Patient(); + pt.addName().setFamily(methodName); + IIdType id = ourClient.create().resource(pt).execute().getId(); + + Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute(); + assertEquals(methodName, pt2.getName().get(0).getFamily()); + } + + @Test + public void testWebsocketSubscription() throws Exception { + /* + * Create subscription + */ + Subscription subscription = new Subscription(); + subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)"); + subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); + subscription.setCriteria("Observation?status=final"); + + Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent(); + channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET); + channel.setPayload("application/json"); + subscription.setChannel(channel); + + MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute(); + IIdType mySubscriptionId = methodOutcome.getId(); + + // Wait for the subscription to be activated + waitForSize(1, () -> ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size()); + + /* + * Attach websocket + */ + + WebSocketClient myWebSocketClient = new WebSocketClient(); + SocketImplementation mySocketImplementation = new SocketImplementation(mySubscriptionId.getIdPart(), EncodingEnum.JSON); + + myWebSocketClient.start(); + URI echoUri = new URI("ws://localhost:" + ourPort + "/hapi-fhir-jpaserver/websocket"); + ClientUpgradeRequest request = new ClientUpgradeRequest(); + ourLog.info("Connecting to : {}", echoUri); + Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); + Session session = connection.get(2, TimeUnit.SECONDS); + + ourLog.info("Connected to WS: {}", session.isOpen()); + + /* + * Create a matching resource + */ + Observation obs = new Observation(); + obs.setStatus(Observation.ObservationStatus.FINAL); + ourClient.create().resource(obs).execute(); + + // Give some time for the subscription to deliver + Thread.sleep(2000); + + /* + * Ensure that we receive a ping on the websocket + */ + waitForSize(1, () -> mySocketImplementation.myPingCount); + + /* + * Clean up + */ + ourClient.delete().resourceById(mySubscriptionId).execute(); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + String path = Paths.get("").toAbsolutePath().toString(); + + ourLog.info("Project base path is: {}", path); + + ourServer = new Server(0); + + WebAppContext webAppContext = new WebAppContext(); + webAppContext.setContextPath("/hapi-fhir-jpaserver"); + webAppContext.setDisplayName("HAPI FHIR"); + webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml"); + webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter"); + webAppContext.setParentLoaderPriority(true); + + ourServer.setHandler(webAppContext); + ourServer.start(); + + ourPort = JettyUtil.getPortForStartedServer(ourServer); + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/"; + + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + ourClient.registerInterceptor(new LoggingInterceptor(true)); + } + + public static void main(String[] theArgs) throws Exception { + ourPort = 8080; + beforeClass(); + } +}