fixes for support of R4B / 6.2.0 (#455)
This commit is contained in:
committed by
GitHub
parent
64aeb9b2fe
commit
2e1f5f5276
11
pom.xml
11
pom.xml
@@ -14,7 +14,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.2.0-PRE18-SNAPSHOT</version>
|
<version>6.2.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-jpaserver-starter</artifactId>
|
<artifactId>hapi-fhir-jpaserver-starter</artifactId>
|
||||||
@@ -316,11 +316,18 @@
|
|||||||
<version>${spring_boot_version}</version>
|
<version>${spring_boot_version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-core</artifactId>
|
||||||
|
<version>1.9.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus -->
|
<!-- https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.micrometer</groupId>
|
<groupId>io.micrometer</groupId>
|
||||||
<artifactId>micrometer-registry-prometheus</artifactId>
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
<version>1.8.5</version>
|
<version>1.9.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ public class OnEitherVersion extends AnyNestedCondition {
|
|||||||
static class OnR4 {
|
static class OnR4 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Conditional(OnR4BCondition.class)
|
||||||
|
static class OnR4B {
|
||||||
|
}
|
||||||
|
|
||||||
@Conditional(OnR5Condition.class)
|
@Conditional(OnR5Condition.class)
|
||||||
static class OnR5 {
|
static class OnR5 {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter.annotations;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import org.springframework.context.annotation.Condition;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
|
|
||||||
|
public class OnR4BCondition implements Condition {
|
||||||
|
@Override
|
||||||
|
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
|
||||||
|
String version = conditionContext.
|
||||||
|
getEnvironment()
|
||||||
|
.getProperty("hapi.fhir.fhir_version")
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
|
return FhirVersionEnum.R4B.name().equals(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter.common;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.config.r4b.JpaR4BConfig;
|
||||||
|
import ca.uhn.fhir.jpa.starter.annotations.OnR4BCondition;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Conditional(OnR4BCondition.class)
|
||||||
|
@Import({
|
||||||
|
JpaR4BConfig.class,
|
||||||
|
StarterJpaConfig.class,
|
||||||
|
ElasticsearchConfig.class
|
||||||
|
})
|
||||||
|
public class FhirServerConfigR4B {
|
||||||
|
}
|
||||||
@@ -26,7 +26,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
|||||||
import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl;
|
import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl;
|
||||||
import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl;
|
import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl;
|
||||||
import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper;
|
import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper;
|
||||||
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
|
||||||
import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc;
|
import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||||
@@ -423,7 +422,6 @@ public class StarterJpaConfig {
|
|||||||
public static IServerConformanceProvider<?> calculateConformanceProvider(IFhirSystemDao fhirSystemDao, RestfulServer fhirServer, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport) {
|
public static IServerConformanceProvider<?> calculateConformanceProvider(IFhirSystemDao fhirSystemDao, RestfulServer fhirServer, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport) {
|
||||||
FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion();
|
FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion();
|
||||||
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
if (fhirVersion == FhirVersionEnum.DSTU2) {
|
||||||
|
|
||||||
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(fhirServer, fhirSystemDao, daoConfig);
|
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(fhirServer, fhirSystemDao, daoConfig);
|
||||||
confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server");
|
confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server");
|
||||||
return confProvider;
|
return confProvider;
|
||||||
@@ -437,6 +435,11 @@ public class StarterJpaConfig {
|
|||||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
||||||
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
||||||
return confProvider;
|
return confProvider;
|
||||||
|
} else if (fhirVersion == FhirVersionEnum.R4B) {
|
||||||
|
|
||||||
|
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
||||||
|
confProvider.setImplementationDescription("HAPI FHIR R4B Server");
|
||||||
|
return confProvider;
|
||||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||||
|
|
||||||
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport);
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter.common.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
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 ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.starter.annotations.OnR4BCondition;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import org.hl7.fhir.r4b.model.StructureDefinition;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||||
|
* on this server.
|
||||||
|
* <p>
|
||||||
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
|
* in order to use this class.
|
||||||
|
*/
|
||||||
|
@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true")
|
||||||
|
@Configuration
|
||||||
|
@Conditional(OnR4BCondition.class)
|
||||||
|
public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryValidationInterceptorFactory {
|
||||||
|
|
||||||
|
private final FhirContext fhirContext;
|
||||||
|
private final RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder;
|
||||||
|
private final IFhirResourceDao structureDefinitionResourceProvider;
|
||||||
|
|
||||||
|
public RepositoryValidationInterceptorFactoryR4B(RepositoryValidatingRuleBuilder repositoryValidatingRuleBuilder, DaoRegistry daoRegistry) {
|
||||||
|
this.repositoryValidatingRuleBuilder = repositoryValidatingRuleBuilder;
|
||||||
|
this.fhirContext = daoRegistry.getSystemDao().getContext();
|
||||||
|
structureDefinitionResourceProvider = daoRegistry.getResourceDao("StructureDefinition");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions() {
|
||||||
|
|
||||||
|
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap().add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||||
|
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size())
|
||||||
|
.stream()
|
||||||
|
.map(StructureDefinition.class::cast)
|
||||||
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
|
structureDefintions.forEach((key, value) -> {
|
||||||
|
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
|
});
|
||||||
|
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepositoryValidatingInterceptor build() {
|
||||||
|
|
||||||
|
// Customize the ruleBuilder here to have the rules you want! We will give a simple example
|
||||||
|
// of enabling validation for all Patient resources
|
||||||
|
repositoryValidatingRuleBuilder.forResourcesOfType("Patient").requireAtLeastProfile("http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient").and().requireValidationToDeclaredProfiles();
|
||||||
|
|
||||||
|
// Do not customize below this line
|
||||||
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
|
return new RepositoryValidatingInterceptor(fhirContext, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -48,8 +48,8 @@ spring:
|
|||||||
# hibernate.search.backend.directory.root: target/lucenefiles
|
# hibernate.search.backend.directory.root: target/lucenefiles
|
||||||
# hibernate.search.backend.lucene_version: lucene_current
|
# hibernate.search.backend.lucene_version: lucene_current
|
||||||
### elastic parameters ===> see also elasticsearch section below <===
|
### elastic parameters ===> see also elasticsearch section below <===
|
||||||
hibernate.search.backend.type: elasticsearch
|
# hibernate.search.backend.type: elasticsearch
|
||||||
hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
|
# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
|
||||||
hapi:
|
hapi:
|
||||||
fhir:
|
fhir:
|
||||||
### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html)
|
### This enables the swagger-ui at /fhir/swagger-ui/index.html as well as the /fhir/api-docs (see https://hapifhir.io/hapi-fhir/docs/server_plain/openapi.html)
|
||||||
|
|||||||
120
src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java
Normal file
120
src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4BIT.java
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4b.model.Bundle;
|
||||||
|
import org.hl7.fhir.r4b.model.Patient;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Order;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = {
|
||||||
|
"spring.datasource.url=jdbc:h2:mem:dbr4b",
|
||||||
|
"hapi.fhir.enable_repository_validating_interceptor=true",
|
||||||
|
"hapi.fhir.fhir_version=r4b",
|
||||||
|
"hapi.fhir.subscription.websocket_enabled=false",
|
||||||
|
"hapi.fhir.mdm_enabled=false",
|
||||||
|
"hapi.fhir.implementationguides.dk-core.name=hl7.fhir.dk.core",
|
||||||
|
"hapi.fhir.implementationguides.dk-core.version=1.1.0",
|
||||||
|
// 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"})
|
||||||
|
class ExampleServerR4BIT {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR4BIT.class);
|
||||||
|
private IGenericClient ourClient;
|
||||||
|
private FhirContext ourCtx;
|
||||||
|
|
||||||
|
@LocalServerPort
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
void testCreateAndRead() {
|
||||||
|
String methodName = "testCreateAndRead";
|
||||||
|
ourLog.info("Entering " + methodName + "()...");
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBatchPutWithIdenticalTags() {
|
||||||
|
String batchPuts = "{\n" +
|
||||||
|
"\t\"resourceType\": \"Bundle\",\n" +
|
||||||
|
"\t\"id\": \"patients\",\n" +
|
||||||
|
"\t\"type\": \"batch\",\n" +
|
||||||
|
"\t\"entry\": [\n" +
|
||||||
|
"\t\t{\n" +
|
||||||
|
"\t\t\t\"request\": {\n" +
|
||||||
|
"\t\t\t\t\"method\": \"PUT\",\n" +
|
||||||
|
"\t\t\t\t\"url\": \"Patient/pat-1\"\n" +
|
||||||
|
"\t\t\t},\n" +
|
||||||
|
"\t\t\t\"resource\": {\n" +
|
||||||
|
"\t\t\t\t\"resourceType\": \"Patient\",\n" +
|
||||||
|
"\t\t\t\t\"id\": \"pat-1\",\n" +
|
||||||
|
"\t\t\t\t\"meta\": {\n" +
|
||||||
|
"\t\t\t\t\t\"tag\": [\n" +
|
||||||
|
"\t\t\t\t\t\t{\n" +
|
||||||
|
"\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" +
|
||||||
|
"\t\t\t\t\t\t\t\"code\": \"value2\"\n" +
|
||||||
|
"\t\t\t\t\t\t}\n" +
|
||||||
|
"\t\t\t\t\t]\n" +
|
||||||
|
"\t\t\t\t}\n" +
|
||||||
|
"\t\t\t},\n" +
|
||||||
|
"\t\t\t\"fullUrl\": \"/Patient/pat-1\"\n" +
|
||||||
|
"\t\t},\n" +
|
||||||
|
"\t\t{\n" +
|
||||||
|
"\t\t\t\"request\": {\n" +
|
||||||
|
"\t\t\t\t\"method\": \"PUT\",\n" +
|
||||||
|
"\t\t\t\t\"url\": \"Patient/pat-2\"\n" +
|
||||||
|
"\t\t\t},\n" +
|
||||||
|
"\t\t\t\"resource\": {\n" +
|
||||||
|
"\t\t\t\t\"resourceType\": \"Patient\",\n" +
|
||||||
|
"\t\t\t\t\"id\": \"pat-2\",\n" +
|
||||||
|
"\t\t\t\t\"meta\": {\n" +
|
||||||
|
"\t\t\t\t\t\"tag\": [\n" +
|
||||||
|
"\t\t\t\t\t\t{\n" +
|
||||||
|
"\t\t\t\t\t\t\t\"system\": \"http://mysystem.org\",\n" +
|
||||||
|
"\t\t\t\t\t\t\t\"code\": \"value2\"\n" +
|
||||||
|
"\t\t\t\t\t\t}\n" +
|
||||||
|
"\t\t\t\t\t]\n" +
|
||||||
|
"\t\t\t\t}\n" +
|
||||||
|
"\t\t\t},\n" +
|
||||||
|
"\t\t\t\"fullUrl\": \"/Patient/pat-2\"\n" +
|
||||||
|
"\t\t}\n" +
|
||||||
|
"\t]\n" +
|
||||||
|
"}";
|
||||||
|
Bundle bundle = FhirContext.forR4B().newJsonParser().parseResource(Bundle.class, batchPuts);
|
||||||
|
ourClient.transaction().withBundle(bundle).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() {
|
||||||
|
|
||||||
|
ourCtx = FhirContext.forR4B();
|
||||||
|
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
|
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
|
||||||
|
String ourServerBase = "http://localhost:" + port + "/fhir/";
|
||||||
|
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user