From 695ef2583fe53199cf8eaa7f44e8fd31c38de529 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 13 Dec 2020 09:23:39 -0500 Subject: [PATCH 01/10] Create prerelase branch for 5.3.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ecf437a..0c08c3a 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.2.0 + 5.3.0-SNAPSHOT hapi-fhir-jpaserver-starter From 803f713be6ee30b00fd34a46b10178d5a6da4002 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Fri, 1 Jan 2021 16:06:18 -0500 Subject: [PATCH 02/10] Add repository validating interceptor, and update for EMPI->MDM rename --- .editorconfig | 4 +- README.md | 4 +- pom.xml | 6 +- .../java/ca/uhn/fhir/jpa/empi/EmpiConfig.java | 45 ---- .../java/ca/uhn/fhir/jpa/mdm/MdmConfig.java | 45 ++++ .../MdmConfigCondition.java} | 6 +- .../uhn/fhir/jpa/starter/AppProperties.java | 21 +- .../ca/uhn/fhir/jpa/starter/Application.java | 4 +- .../jpa/starter/BaseJpaRestfulServer.java | 7 + .../jpa/starter/FhirServerConfigCommon.java | 7 + ...epositoryValidationInterceptorFactory.java | 38 ++++ src/main/resources/application.yaml | 1 + .../{empi-rules.json => mdm-rules.json} | 1 + .../fhir/jpa/starter/ExampleServerR4IT.java | 213 +++++++++--------- 14 files changed, 233 insertions(+), 169 deletions(-) delete mode 100644 src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java create mode 100644 src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfig.java rename src/main/java/ca/uhn/fhir/jpa/{empi/EmpiConfigCondition.java => mdm/MdmConfigCondition.java} (77%) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactory.java rename src/main/resources/{empi-rules.json => mdm-rules.json} (95%) diff --git a/.editorconfig b/.editorconfig index cd68918..904096d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ [*.java] charset = utf-8 -indent_style = space -indent_size = 2 +indent_style = tab +indent_size = 3 diff --git a/README.md b/README.md index b2f1e2e..4d1b251 100644 --- a/README.md +++ b/README.md @@ -316,9 +316,9 @@ The server may be configured with subscription support by enabling properties in - `hapi.fhir.subscription.websocket.enabled` - Enables websocket subscriptions. With this enabled, your server will accept incoming websocket connections on the following URL (this example uses the default context path and port, you may need to tweak depending on your deployment environment): [ws://localhost:8080/websocket](ws://localhost:8080/websocket) -## Enabling EMPI +## Enabling MDM (EMPI) -Set `hapi.fhir.empi_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable EMPI on this server. The EMPI matching rules are configured in [empi-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/empi-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that EMPI relies on subscriptions, so for EMPI to work, subscriptions must be enabled. +Set `hapi.fhir.mdm_enabled=true` in the [application.yaml](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/application.yaml) file to enable MDM on this server. The MDM matching rules are configured in [mdm-rules.json](https://github.com/hapifhir/hapi-fhir-jpaserver-starter/blob/master/src/main/resources/mdm-rules.json). The rules in this example file should be replaced with actual matching rules appropriate to your data. Note that MDM relies on subscriptions, so for MDM to work, subscriptions must be enabled. ## Using Elasticsearch diff --git a/pom.xml b/pom.xml index 0c08c3a..9dbdcf1 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,6 @@ 8 - 2.3.4.RELEASE @@ -100,10 +99,10 @@ - + ca.uhn.hapi.fhir - hapi-fhir-jpaserver-empi + hapi-fhir-jpaserver-mdm ${project.version} @@ -358,7 +357,6 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M5 true diff --git a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java b/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java deleted file mode 100644 index 66317fc..0000000 --- a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package ca.uhn.fhir.jpa.empi; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.empi.api.IEmpiSettings; -import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator; -import ca.uhn.fhir.empi.rules.config.EmpiSettings; -import ca.uhn.fhir.jpa.empi.config.EmpiConsumerConfig; -import ca.uhn.fhir.jpa.empi.config.EmpiSubmitterConfig; -import ca.uhn.fhir.jpa.starter.AppProperties; -import ca.uhn.fhir.rest.server.util.ISearchParamRetriever; -import com.google.common.base.Charsets; -import org.apache.commons.io.IOUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; - -import java.io.IOException; - -/** - * TODO: Move this to package "ca.uhn.fhir.jpa.starter" in HAPI FHIR 5.2.0+. The lousy component scan - * in 5.1.0 picks this up even if EMPI is disabled currently. - */ -@Configuration -@Conditional(EmpiConfigCondition.class) -@Import({EmpiConsumerConfig.class, EmpiSubmitterConfig.class}) -public class EmpiConfig { - - @Bean - EmpiRuleValidator empiRuleValidator(FhirContext theFhirContext, ISearchParamRetriever theSearchParamRetriever) { - return new EmpiRuleValidator(theFhirContext, theSearchParamRetriever); - } - - @Bean - IEmpiSettings empiSettings(@Autowired EmpiRuleValidator theEmpiRuleValidator, AppProperties appProperties) throws IOException { - DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); - Resource resource = resourceLoader.getResource("empi-rules.json"); - String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); - return new EmpiSettings(theEmpiRuleValidator).setEnabled(appProperties.getEmpi_enabled()).setScriptText(json); - } - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfig.java b/src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfig.java new file mode 100644 index 0000000..b14c7c4 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfig.java @@ -0,0 +1,45 @@ +package ca.uhn.fhir.jpa.mdm; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig; +import ca.uhn.fhir.jpa.mdm.config.MdmSubmitterConfig; +import ca.uhn.fhir.jpa.starter.AppProperties; +import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator; +import ca.uhn.fhir.mdm.rules.config.MdmSettings; +import ca.uhn.fhir.rest.server.util.ISearchParamRetriever; +import com.google.common.base.Charsets; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; + +import java.io.IOException; + +/** + * TODO: Move this to package "ca.uhn.fhir.jpa.starter" in HAPI FHIR 5.2.0+. The lousy component scan + * in 5.1.0 picks this up even if MDM is disabled currently. + */ +@Configuration +@Conditional(MdmConfigCondition.class) +@Import({MdmConsumerConfig.class, MdmSubmitterConfig.class}) +public class MdmConfig { + + @Bean + MdmRuleValidator mdmRuleValidator(FhirContext theFhirContext, ISearchParamRetriever theSearchParamRetriever) { + return new MdmRuleValidator(theFhirContext, theSearchParamRetriever); + } + + @Bean + IMdmSettings mdmSettings(@Autowired MdmRuleValidator theMdmRuleValidator, AppProperties appProperties) throws IOException { + DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource("mdm-rules.json"); + String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); + return new MdmSettings(theMdmRuleValidator).setEnabled(appProperties.getMdm_enabled()).setScriptText(json); + } + +} diff --git a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfigCondition.java b/src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfigCondition.java similarity index 77% rename from src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfigCondition.java rename to src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfigCondition.java index 4854bde..fd2b7bb 100644 --- a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfigCondition.java +++ b/src/main/java/ca/uhn/fhir/jpa/mdm/MdmConfigCondition.java @@ -1,13 +1,13 @@ -package ca.uhn.fhir.jpa.empi; +package ca.uhn.fhir.jpa.mdm; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; -public class EmpiConfigCondition implements Condition { +public class MdmConfigCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { - String property = conditionContext.getEnvironment().getProperty("hapi.fhir.empi_enabled"); + String property = conditionContext.getEnvironment().getProperty("hapi.fhir.mdm_enabled"); return Boolean.parseBoolean(property); } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 30df010..d38d1a1 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -23,7 +23,7 @@ import org.springframework.context.annotation.Configuration; @EnableConfigurationProperties public class AppProperties { - private Boolean empi_enabled = false; + private Boolean mdm_enabled = false; private Boolean allow_cascading_deletes = false; private Boolean allow_contains_searches = true; private Boolean allow_external_references = false; @@ -32,6 +32,7 @@ public class AppProperties { private Boolean allow_placeholder_references = true; private Boolean auto_create_placeholder_reference_targets = true; private Boolean enable_index_missing_fields = false; + private Boolean enable_repository_validating_interceptor = false; private Boolean enforce_referential_integrity_on_delete = true; private Boolean enforce_referential_integrity_on_write = true; private Boolean etag_support_enabled = true; @@ -90,12 +91,12 @@ public class AppProperties { this.partitioning = partitioning; } - public Boolean getEmpi_enabled() { - return empi_enabled; + public Boolean getMdm_enabled() { + return mdm_enabled; } - public void setEmpi_enabled(Boolean empi_enabled) { - this.empi_enabled = empi_enabled; + public void setMdm_enabled(Boolean mdm_enabled) { + this.mdm_enabled = mdm_enabled; } @@ -246,7 +247,15 @@ public class AppProperties { this.enable_index_missing_fields = enable_index_missing_fields; } - public Boolean getEnforce_referential_integrity_on_delete() { + public Boolean getEnable_repository_validating_interceptor() { + return enable_repository_validating_interceptor; + } + + public void setEnable_repository_validating_interceptor(Boolean theEnable_repository_validating_interceptor) { + enable_repository_validating_interceptor = theEnable_repository_validating_interceptor; + } + + public Boolean getEnforce_referential_integrity_on_delete() { return enforce_referential_integrity_on_delete; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index 272feb6..5a55646 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.starter; -import ca.uhn.fhir.jpa.empi.EmpiConfig; +import ca.uhn.fhir.jpa.mdm.MdmConfig; import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; @@ -24,7 +24,7 @@ import org.springframework.web.servlet.DispatcherServlet; @ServletComponentScan(basePackageClasses = { JpaRestfulServer.class}) @SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class}) -@Import({SubscriptionSubmitterConfig.class, SubscriptionProcessorConfig.class, SubscriptionChannelConfig.class, WebsocketDispatcherConfig.class, EmpiConfig.class}) +@Import({SubscriptionSubmitterConfig.class, SubscriptionProcessorConfig.class, SubscriptionChannelConfig.class, WebsocketDispatcherConfig.class, MdmConfig.class}) public class Application extends SpringBootServletInitializer { public static void main(String[] args) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index dfbe8c8..d65d95c 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; +import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; @@ -362,6 +363,12 @@ public class BaseJpaRestfulServer extends RestfulServer { daoConfig.setLastNEnabled(true); } + // Repository Validating Interceptor + if (Boolean.TRUE.equals(appProperties.getEnable_repository_validating_interceptor())) { + RepositoryValidationInterceptorFactory repositoryValidationInterceptorFactory = myApplicationContext.getBean(RepositoryValidationInterceptorFactory.class); + RepositoryValidatingInterceptor interceptor = repositoryValidationInterceptorFactory.build(); + interceptorService.registerInterceptor(interceptor); + } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java index 4b7d721..1d766fa 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -132,6 +132,13 @@ public class FhirServerConfigCommon { } + @Bean + @Lazy + public RepositoryValidationInterceptorFactory repositoryValidationInterceptorFactory() { + return new RepositoryValidationInterceptorFactory(); + } + + @Primary @Bean public HibernateDialectProvider jpaStarterDialectProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactory.java b/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactory.java new file mode 100644 index 0000000..0ea6652 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactory.java @@ -0,0 +1,38 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.interceptor.validation.IRepositoryValidatingRule; +import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor; +import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; + +import java.util.List; + +/** + * This class can be customized to enable the {@link ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor} + * on this server. + * + * The enable_repository_validating_interceptor property must be enabled in application.yaml + * in order to use this class. + */ +public class RepositoryValidationInterceptorFactory { + + @Autowired + private ApplicationContext myApplicationContext; + @Autowired + private FhirContext myFhirContext; + + public RepositoryValidatingInterceptor build() { + RepositoryValidatingRuleBuilder ruleBuilder = myApplicationContext.getBean(RepositoryValidatingRuleBuilder.class); + + // Customize the ruleBuilder here to have the rules you want! We will give a simple example + // of enabling validation for all Patient resources + ruleBuilder.forResourcesOfType("Patient").requireValidationToDeclaredProfiles(); + + // Do not customize below this line + List rules = ruleBuilder.build(); + return new RepositoryValidatingInterceptor(myFhirContext, rules); + } + +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d081516..7a8987c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -57,6 +57,7 @@ hapi: # default_encoding: JSON # default_pretty_print: true # default_page_size: 20 +# enable_repository_validating_interceptor: false # enable_index_missing_fields: false # enforce_referential_integrity_on_delete: false # enforce_referential_integrity_on_write: false diff --git a/src/main/resources/empi-rules.json b/src/main/resources/mdm-rules.json similarity index 95% rename from src/main/resources/empi-rules.json rename to src/main/resources/mdm-rules.json index cb1d710..446fb68 100644 --- a/src/main/resources/empi-rules.json +++ b/src/main/resources/mdm-rules.json @@ -1,5 +1,6 @@ { "version": "1", + "mdmTypes": ["Patient", "Practitioner"], "candidateSearchParams": [ { "resourceType": "Patient", 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 8a011be..9c5b749 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -12,8 +12,12 @@ 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.*; +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.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.test.context.SpringBootTest; @@ -21,146 +25,145 @@ import org.springframework.boot.web.server.LocalServerPort; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.net.URI; -import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import static ca.uhn.fhir.util.TestUtil.waitForSize; +import static java.util.Comparator.comparing; import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = - { - "spring.batch.job.enabled=false", - "spring.datasource.url=jdbc:h2:mem:dbr4", - "hapi.fhir.fhir_version=r4", - "hapi.fhir.subscription.websocket_enabled=true", - "hapi.fhir.empi_enabled=true", - //Override is currently required when using Empi as the construction of the Empi beans are ambiguous as they are constructed multiple places. This is evident when running in a spring boot environment - "spring.main.allow-bean-definition-overriding=true" - }) + { + "spring.batch.job.enabled=false", + "spring.datasource.url=jdbc:h2:mem:dbr4", + "hapi.fhir.enable_repository_validating_interceptor=true", + "hapi.fhir.fhir_version=r4", + "hapi.fhir.subscription.websocket_enabled=true", + "hapi.fhir.mdm_enabled=true", + //Override is currently required when using MDM as the construction of the MDM beans are ambiguous as they are constructed multiple places. This is evident when running in a spring boot environment + "spring.main.allow-bean-definition-overriding=true" + }) public class ExampleServerR4IT { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class); - private IGenericClient ourClient; - private FhirContext ourCtx; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class); + private IGenericClient ourClient; + private FhirContext ourCtx; - @LocalServerPort - private int port; + @LocalServerPort + private int port; - @Test - void testCreateAndRead() { + @Test + @Order(0) + void testCreateAndRead() { - String methodName = "testCreateResourceConditional"; + String methodName = "testCreateResourceConditional"; - Patient pt = new Patient(); - pt.setActive(true); - pt.getBirthDateElement().setValueAsString("2020-01-01"); - pt.addIdentifier().setSystem("http://foo").setValue("12345"); - pt.addName().setFamily(methodName); - IIdType id = ourClient.create().resource(pt).execute().getId(); + Patient pt = new Patient(); + pt.setActive(true); + pt.getBirthDateElement().setValueAsString("2020-01-01"); + pt.addIdentifier().setSystem("http://foo").setValue("12345"); + 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()); + Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute(); + assertEquals(methodName, pt2.getName().get(0).getFamily()); - // Test EMPI + // Test MDM - // Wait until the EMPI message has been processed - await().until(() -> getPeople().size() > 0); - List persons = getPeople(); + // Wait until the MDM message has been processed + await().until(() -> getPatients().size(), equalTo(2)); + List persons = getPatients(); + Patient goldenRecord = persons.get(0); - // Verify a Person was created that links to our Patient - Optional personLinkToCreatedPatient = persons.stream() - .map(Person::getLink) - .flatMap(Collection::stream) - .map(Person.PersonLinkComponent::getTarget) - .map(Reference::getReference) - .filter(pid -> id.toUnqualifiedVersionless().getValue().equals(pid)) - .findAny(); - assertTrue(personLinkToCreatedPatient.isPresent()); - } + // Verify that a golden record Patient was created + assertNotNull(goldenRecord.getMeta().getTag("http://hapifhir.io/fhir/NamingSystem/mdm-record-status", "GOLDEN_RECORD")); + } - private List getPeople() { - Bundle bundle = ourClient.search().forResource(Person.class).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute(); - return BundleUtil.toListOfResourcesOfType(ourCtx, bundle, Person.class); - } + private List getPatients() { + Bundle bundle = ourClient.search().forResource(Patient.class).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute(); + List retVal = BundleUtil.toListOfResourcesOfType(ourCtx, bundle, Patient.class); + retVal.sort(comparing(o -> ((Patient) o).getMeta().getLastUpdated()).reversed()); + return retVal; + } - @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"); + @Test + @Order(1) + 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); + 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(); + MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute(); + IIdType mySubscriptionId = methodOutcome.getId(); - // Wait for the subscription to be activated - await().until(() -> activeSubscriptionCount() == 3); + // Wait for the subscription to be activated + await().until(() -> activeSubscriptionCount() == 3); - /* - * Attach websocket - */ + /* + * Attach websocket + */ - WebSocketClient myWebSocketClient = new WebSocketClient(); - SocketImplementation mySocketImplementation = new SocketImplementation(mySubscriptionId.getIdPart(), EncodingEnum.JSON); + WebSocketClient myWebSocketClient = new WebSocketClient(); + SocketImplementation mySocketImplementation = new SocketImplementation(mySubscriptionId.getIdPart(), EncodingEnum.JSON); - myWebSocketClient.start(); - URI echoUri = new URI("ws://localhost:" + port + "/websocket"); - ClientUpgradeRequest request = new ClientUpgradeRequest(); - ourLog.info("Connecting to : {}", echoUri); - Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); - Session session = connection.get(2, TimeUnit.SECONDS); + myWebSocketClient.start(); + URI echoUri = new URI("ws://localhost:" + port + "/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()); + 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(); + /* + * 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); + // Give some time for the subscription to deliver + Thread.sleep(2000); - /* - * Ensure that we receive a ping on the websocket - */ - waitForSize(1, () -> mySocketImplementation.myPingCount); + /* + * Ensure that we receive a ping on the websocket + */ + waitForSize(1, () -> mySocketImplementation.myPingCount); - /* - * Clean up - */ - ourClient.delete().resourceById(mySubscriptionId).execute(); - } + /* + * Clean up + */ + ourClient.delete().resourceById(mySubscriptionId).execute(); + } - private int activeSubscriptionCount() { - return ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size(); - } + private int activeSubscriptionCount() { + return ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size(); + } - @BeforeEach - void beforeEach() { + @BeforeEach + void beforeEach() { - ourCtx = FhirContext.forR4(); - ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); - ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - String ourServerBase = "http://localhost:" + port + "/fhir/"; - ourClient = ourCtx.newRestfulGenericClient(ourServerBase); - ourClient.registerInterceptor(new LoggingInterceptor(true)); - } + ourCtx = FhirContext.forR4(); + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + String ourServerBase = "http://localhost:" + port + "/fhir/"; + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + ourClient.registerInterceptor(new LoggingInterceptor(true)); + } } From 4be7d41948cfe473542f13ace53a864a54df0421 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 6 Jan 2021 18:27:50 -0500 Subject: [PATCH 03/10] UPdate to support new HS6 changes --- README.md | 3 +- pom.xml | 15 +++++++ .../fhir/jpa/starter/EnvironmentHelper.java | 45 ++++++++++++------- .../jpa/starter/FhirServerConfigCommon.java | 6 +-- .../fhir/jpa/starter/FhirServerConfigR4.java | 10 ++++- ...va => JpaHibernatePropertiesProvider.java} | 6 +-- .../jpa/starter/ElasticsearchLastNR4IT.java | 35 ++++++--------- 7 files changed, 75 insertions(+), 45 deletions(-) rename src/main/java/ca/uhn/fhir/jpa/starter/{JpaHibernateDialectProvider.java => JpaHibernatePropertiesProvider.java} (78%) diff --git a/README.md b/README.md index 4d1b251..f26e02c 100644 --- a/README.md +++ b/README.md @@ -328,9 +328,10 @@ For example: ```properties elasticsearch.enabled=true -elasticsearch.rest_url=http://localhost:9200 +elasticsearch.rest_url=localhost:9200 elasticsearch.username=SomeUsername elasticsearch.password=SomePassword +elasticsearch.protocol=http elasticsearch.required_index_status=YELLOW elasticsearch.schema_management_strategy=CREATE ``` diff --git a/pom.xml b/pom.xml index 9dbdcf1..2efc2df 100644 --- a/pom.xml +++ b/pom.xml @@ -233,6 +233,21 @@ jetty-webapp test + + org.testcontainers + testcontainers + test + + + org.testcontainers + elasticsearch + test + + + org.testcontainers + junit-jupiter + test +