Add support for R5 resources (#1416)

* Work so far on R5 support

* Add support for R5

* Docs changes
This commit is contained in:
James Agnew 2019-08-06 17:30:31 -04:00 committed by GitHub
parent 808da73854
commit 60eab3ad70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
445 changed files with 3292343 additions and 2503 deletions

View File

@ -925,7 +925,7 @@ public class FhirContext {
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU3 DSTU3}
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#R4 R4}
*
* @since 3.0.0
*/
@ -933,6 +933,15 @@ public class FhirContext {
return new FhirContext(FhirVersionEnum.R4);
}
/**
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#R5 R5}
*
* @since 4.0.0
*/
public static FhirContext forR5() {
return new FhirContext(FhirVersionEnum.R5);
}
private static Collection<Class<? extends IBaseResource>> toCollection(Class<? extends IBaseResource> theResourceType) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<>(1);
retVal.add(theResourceType);
@ -950,4 +959,5 @@ public class FhirContext {
}
return retVal;
}
}

View File

@ -42,7 +42,9 @@ public enum FhirVersionEnum {
DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Dstu3Version()),
R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()),;
R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()),
R5("org.hl7.fhir.r5.hapi.ctx.FhirR5", null, true, new R5Version());
private final FhirVersionEnum myEquivalent;
private final boolean myIsRi;
@ -134,6 +136,8 @@ public enum FhirVersionEnum {
return FhirContext.forDstu3();
case R4:
return FhirContext.forR4();
case R5:
return FhirContext.forR5();
}
throw new IllegalStateException("Unknown version: " + this); // should not happen
}
@ -217,4 +221,24 @@ public enum FhirVersionEnum {
}
private static class R5Version implements IVersionProvider {
private String myVersion;
R5Version() {
try {
Class<?> c = Class.forName("org.hl7.fhir.r5.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null);
} catch (Exception e) {
myVersion = "5.0.0";
}
}
@Override
public String provideVersion() {
return myVersion;
}
}
}

View File

@ -93,7 +93,7 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}

View File

@ -0,0 +1,65 @@
package org.hl7.fhir.convertors;
/*
* #%L
* HAPI FHIR - Converter
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.dstu2.model.Resource;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.ValueSet;
public class NullVersionConverterAdvisor50 implements VersionConvertorAdvisor50 {
@Override
public boolean ignoreEntry(Bundle.BundleEntryComponent src) {
return false;
}
@Override
public Resource convertR2(org.hl7.fhir.r5.model.Resource resource) throws FHIRException {
return null;
}
@Override
public org.hl7.fhir.dstu2016may.model.Resource convertR2016May(org.hl7.fhir.r5.model.Resource resource) throws FHIRException {
return null;
}
@Override
public org.hl7.fhir.dstu3.model.Resource convertR3(org.hl7.fhir.r5.model.Resource resource) throws FHIRException {
return null;
}
@Override
public org.hl7.fhir.r4.model.Resource convertR4(org.hl7.fhir.r5.model.Resource resource) throws FHIRException {
return null;
}
@Override
public void handleCodeSystem(CodeSystem tgtcs, ValueSet source) throws FHIRException {
}
@Override
public CodeSystem getCodeSystem(ValueSet src) throws FHIRException {
return null;
}
}

View File

@ -105,6 +105,11 @@
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r5</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
@ -125,6 +130,11 @@
<artifactId>hapi-fhir-validation-resources-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-r5</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.ttddyy</groupId>
@ -676,21 +686,6 @@
<artifactId>hapi-tinder-plugin</artifactId>
<version>${project.version}</version>
<executions>
<!--
<execution>
<id>build_dstu1</id>
<goals>
<goal>generate-jparest-server</goal>
</goals>
<configuration>
<version>dstu</version>
<configPackageBase>ca.uhn.fhir.jpa.config</configPackageBase>
<packageBase>ca.uhn.fhir.jpa.rp.dstu</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-dstu1.xml</targetResourceSpringBeansFile>
<baseResourceNames></baseResourceNames>
</configuration>
</execution>
-->
<execution>
<id>build_dstu2</id>
<goals>
@ -737,6 +732,19 @@
</excludeResourceNames>
</configuration>
</execution>
<execution>
<id>build_r5</id>
<goals>
<goal>generate-jparest-server</goal>
</goals>
<configuration>
<version>r5</version>
<configPackageBase>ca.uhn.fhir.jpa.config</configPackageBase>
<packageBase>ca.uhn.fhir.jpa.rp.r5</packageBase>
<targetResourceSpringBeansFile>hapi-fhir-server-resourceproviders-r5.xml</targetResourceSpringBeansFile>
<baseResourceNames></baseResourceNames>
</configuration>
</execution>
</executions>
<dependencies>

View File

@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
import ca.uhn.fhir.jpa.dao.DaoRegistry;
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
@ -211,6 +212,13 @@ public abstract class BaseConfig implements SchedulingConfigurer {
return new JpaConsentContextServices();
}
@Bean
@Lazy
public TerminologyUploaderProvider terminologyUploaderProvider() {
TerminologyUploaderProvider retVal = new TerminologyUploaderProvider();
return retVal;
}
public static void configureEntityManagerFactory(LocalContainerEntityManagerFactoryBean theFactory, FhirContext theCtx) {
theFactory.setJpaDialect(hibernateJpaDialect(theCtx.getLocalizer()));
theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity");

View File

@ -17,7 +17,7 @@ import org.hl7.fhir.instance.hapi.validation.CachingValidationSupport;
import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@ -24,7 +24,7 @@ import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.CachingValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -150,13 +150,6 @@ public class BaseDstu3Config extends BaseConfig {
return new HapiTerminologySvcDstu3();
}
@Bean(autowire = Autowire.BY_TYPE)
public TerminologyUploaderProviderDstu3 terminologyUploaderProvider() {
TerminologyUploaderProviderDstu3 retVal = new TerminologyUploaderProviderDstu3();
retVal.setContext(fhirContextDstu3());
return retVal;
}
@Primary
@Bean(autowire = Autowire.BY_NAME, name = "myJpaValidationSupportChainDstu3")
public IValidationSupport validationSupportChainDstu3() {

View File

@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4;
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR4;
@ -27,7 +26,7 @@ import org.hl7.fhir.r4.hapi.validation.CachingValidationSupport;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.utils.GraphQLEngine;
import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -102,7 +101,8 @@ public class BaseR4Config extends BaseConfig {
@Lazy
public IValidatorModule instanceValidatorR4() {
FhirInstanceValidator val = new FhirInstanceValidator();
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
IResourceValidator.BestPracticeWarningLevel level = IResourceValidator.BestPracticeWarningLevel.Warning;
val.setBestPracticeWarningLevel(level);
val.setValidationSupport(validationSupportChainR4());
return val;
}
@ -165,13 +165,6 @@ public class BaseR4Config extends BaseConfig {
return new HapiTerminologySvcR4();
}
@Bean(autowire = Autowire.BY_TYPE)
public TerminologyUploaderProviderR4 terminologyUploaderProvider() {
TerminologyUploaderProviderR4 retVal = new TerminologyUploaderProviderR4();
retVal.setContext(fhirContextR4());
return retVal;
}
@Primary
@Bean(autowire = Autowire.BY_NAME, name = "myJpaValidationSupportChainR4")
public IValidationSupport validationSupportChainR4() {

View File

@ -0,0 +1,174 @@
package ca.uhn.fhir.jpa.config.r5;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR5;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR5;
import ca.uhn.fhir.jpa.term.HapiTerminologySvcR5;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR5;
import ca.uhn.fhir.jpa.term.TerminologyLoaderSvcImpl;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR5;
import ca.uhn.fhir.validation.IValidatorModule;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.hapi.rest.server.GraphQLProvider;
import org.hl7.fhir.r5.hapi.validation.CachingValidationSupport;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.utils.GraphQLEngine;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
@Configuration
@EnableTransactionManagement
public class BaseR5Config extends BaseConfig {
@Override
public FhirContext fhirContext() {
return fhirContextR5();
}
@Bean
@Primary
public FhirContext fhirContextR5() {
FhirContext retVal = FhirContext.forR5();
// Don't strip versions in some places
ParserOptions parserOptions = retVal.getParserOptions();
parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.what");
return retVal;
}
@Bean
public TransactionProcessor.ITransactionProcessorVersionAdapter transactionProcessorVersionFacade() {
return new TransactionProcessorVersionAdapterR5();
}
@Bean
public TransactionProcessor<Bundle, Bundle.BundleEntryComponent> transactionProcessor() {
return new TransactionProcessor<>();
}
// @Bean(name = GRAPHQL_PROVIDER_NAME)
// @Lazy
// public GraphQLProvider graphQLProvider() {
// return new GraphQLProvider(fhirContextR5(), validationSupportChainR5(), graphqlStorageServices());
// }
//
// @Bean
// @Lazy
// public GraphQLEngine.IGraphQLStorageServices graphqlStorageServices() {
// return new JpaStorageServices();
// }
@Bean(name = "myInstanceValidatorR5")
@Lazy
public IValidatorModule instanceValidatorR5() {
FhirInstanceValidator val = new FhirInstanceValidator();
IResourceValidator.BestPracticeWarningLevel level = IResourceValidator.BestPracticeWarningLevel.Warning;
val.setBestPracticeWarningLevel(level);
val.setValidationSupport(validationSupportChainR5());
return val;
}
@Bean
public JpaValidationSupportChainR5 jpaValidationSupportChain() {
return new JpaValidationSupportChainR5();
}
@Bean(name = "myJpaValidationSupportR5", autowire = Autowire.BY_NAME)
public ca.uhn.fhir.jpa.dao.r5.IJpaValidationSupportR5 jpaValidationSupportR5() {
ca.uhn.fhir.jpa.dao.r5.JpaValidationSupportR5 retVal = new ca.uhn.fhir.jpa.dao.r5.JpaValidationSupportR5();
return retVal;
}
@Bean(name = "myResourceCountsCache")
public ResourceCountCache resourceCountsCache() {
ResourceCountCache retVal = new ResourceCountCache(() -> systemDaoR5().getResourceCounts());
retVal.setCacheMillis(4 * DateUtils.MILLIS_PER_HOUR);
return retVal;
}
@Bean(autowire = Autowire.BY_TYPE)
public IFulltextSearchSvc searchDaoR5() {
FulltextSearchSvcImpl searchDao = new FulltextSearchSvcImpl();
return searchDao;
}
@Bean(autowire = Autowire.BY_TYPE)
public SearchParamExtractorR5 searchParamExtractor() {
return new SearchParamExtractorR5();
}
@Bean
public ISearchParamRegistry searchParamRegistry() {
return new SearchParamRegistryR5();
}
@Bean(name = "mySystemDaoR5", autowire = Autowire.BY_NAME)
public IFhirSystemDao<Bundle, org.hl7.fhir.r5.model.Meta> systemDaoR5() {
ca.uhn.fhir.jpa.dao.r5.FhirSystemDaoR5 retVal = new ca.uhn.fhir.jpa.dao.r5.FhirSystemDaoR5();
return retVal;
}
@Bean(name = "mySystemProviderR5")
public ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5 systemProviderR5() {
ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5 retVal = new ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5();
retVal.setContext(fhirContextR5());
retVal.setDao(systemDaoR5());
return retVal;
}
@Bean(autowire = Autowire.BY_TYPE)
public IHapiTerminologyLoaderSvc terminologyLoaderService() {
return new TerminologyLoaderSvcImpl();
}
@Bean(autowire = Autowire.BY_TYPE)
public IHapiTerminologySvcR5 terminologyService() {
return new HapiTerminologySvcR5();
}
@Primary
@Bean(autowire = Autowire.BY_NAME, name = "myJpaValidationSupportChainR5")
public IValidationSupport validationSupportChainR5() {
return new CachingValidationSupport(jpaValidationSupportChain());
}
}

View File

@ -26,7 +26,6 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
import ca.uhn.fhir.jpa.delete.DeleteConflictList;
import ca.uhn.fhir.jpa.model.entity.*;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
public class FhirResourceDaoStructureDefinitionDstu2 extends FhirResourceDaoDstu2<StructureDefinition> implements IFhirResourceDaoStructureDefinition<StructureDefinition> {
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theName) {
// FIXME: implement
return null;
}

View File

@ -24,6 +24,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
public interface IFhirResourceDaoStructureDefinition<T extends IBaseResource> extends IFhirResourceDao<T> {
T generateSnapshot(T theInput, String theUrl, String theName);
T generateSnapshot(T theInput, String theUrl, String theWebUrl, String theName);
}

View File

@ -32,7 +32,7 @@ public class FhirResourceDaoStructureDefinitionDstu3 extends FhirResourceDaoDstu
private IValidationSupport myValidationSupport;
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theName) {
StructureDefinition output = myValidationSupport.generateSnapshot(theInput, theUrl, theName);
Validate.notNull(output);
return output;

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
import ca.uhn.fhir.jpa.dao.r4.MatchResourceUrlService;
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;

View File

@ -32,8 +32,8 @@ public class FhirResourceDaoStructureDefinitionR4 extends FhirResourceDaoR4<Stru
private IValidationSupport myValidationSupport;
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theName) {
StructureDefinition output = myValidationSupport.generateSnapshot(theInput, theUrl, theName);
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theName) {
StructureDefinition output = myValidationSupport.generateSnapshot(theInput, theUrl, theWebUrl, theName);
Validate.notNull(output);
return output;
}

View File

@ -6,18 +6,11 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@ -26,7 +19,6 @@ import org.springframework.context.ApplicationContextAware;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -201,7 +193,7 @@ public class JpaValidationSupportR4 implements IJpaValidationSupportR4, Applicat
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}

View File

@ -0,0 +1,44 @@
package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.hl7.fhir.r5.model.Bundle;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.defaultString;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class FhirResourceDaoBundleR5 extends FhirResourceDaoR5<Bundle> {
@Override
protected void preProcessResourceForStorage(Bundle theResource) {
super.preProcessResourceForStorage(theResource);
Set<String> allowedBundleTypes = getConfig().getBundleTypesAllowedForStorage();
if (theResource.getType() == null || !allowedBundleTypes.contains(defaultString(theResource.getType().toCode()))) {
String message = "Unable to store a Bundle resource on this server with a Bundle.type value of: " + (theResource.getType() != null ? theResource.getType().toCode() : "(missing)");
throw new UnprocessableEntityException(message);
}
}
}

View File

@ -0,0 +1,144 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.IdType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoCodeSystemR5 extends FhirResourceDaoR5<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemR5.class);
@Autowired
private ITermCodeSystemDao myCsDao;
@Autowired
private ValidationSupportChain myValidationSupport;
@Override
public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) {
List<IIdType> valueSetIds;
Set<Long> ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest);
valueSetIds = new ArrayList<>();
for (Long next : ids) {
valueSetIds.add(new IdType("CodeSystem", next));
}
return valueSetIds;
}
@Nonnull
@Override
public IContextValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) {
boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode());
boolean haveCode = theCode != null && theCode.isEmpty() == false;
boolean haveSystem = theSystem != null && theSystem.isEmpty() == false;
if (!haveCoding && !(haveSystem && haveCode)) {
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) {
throw new InvalidRequestException("$lookup can only validate (system AND code) OR (coding.system AND coding.code)");
}
String code;
String system;
if (haveCoding) {
code = theCoding.getCode();
system = theCoding.getSystem();
} else {
code = theCode.getValue();
system = theSystem.getValue();
}
ourLog.info("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(getContext(), system)) {
ourLog.info("Code system {} is supported", system);
IContextValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(getContext(), system, code);
if (retVal != null) {
return retVal;
}
}
// We didn't find it..
return IContextValidationSupport.LookupCodeResult.notFound(system, code);
}
@Override
public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) {
return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB);
}
@Override
protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) {
super.preDelete(theResourceToDelete, theEntityToDelete);
String codeSystemUrl = theResourceToDelete.getUrl();
if (isNotBlank(codeSystemUrl)) {
TermCodeSystem persCs = myCsDao.findByCodeSystemUri(codeSystemUrl);
if (persCs != null) {
myTerminologySvc.deleteCodeSystem(persCs);
}
}
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
CodeSystem cs = (CodeSystem) theResource;
addPidToResource(theEntity, theResource);
myTerminologySvc.storeNewCodeSystemVersionIfNeeded(org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(cs), theEntity);
return retVal;
}
}

View File

@ -0,0 +1,55 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoComposition;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.Composition;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
public class FhirResourceDaoCompositionR5 extends FhirResourceDaoR5<Composition> implements IFhirResourceDaoComposition<Composition> {
@Override
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdate);
if (theId != null) {
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
return search(paramMap, theRequestDetails);
}
}

View File

@ -0,0 +1,173 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.TranslationMatch;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class FhirResourceDaoConceptMapR5 extends FhirResourceDaoR5<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@Override
public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest));
}
return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest));
}
private TranslationResult buildTranslationResult(List<TermConceptMapGroupElementTarget> theTargets) {
TranslationResult retVal = new TranslationResult();
String msg;
if (theTargets.isEmpty()) {
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(false)));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR5.class,
"noMatchesFound");
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
} else {
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(true)));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR5.class,
"matchesFound");
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
TranslationMatch translationMatch;
Set<TermConceptMapGroupElementTarget> targetsToReturn = new HashSet<>();
for (TermConceptMapGroupElementTarget target : theTargets) {
if (targetsToReturn.add(target)) {
translationMatch = new TranslationMatch();
if (target.getEquivalence() != null) {
translationMatch.setEquivalence(VersionConvertor_40_50.convertCode(new CodeType(target.getEquivalence().toCode())));
}
translationMatch.setConcept(VersionConvertor_40_50.convertCoding(
new Coding()
.setCode(target.getCode())
.setSystem(target.getSystem())
.setVersion(target.getSystemVersion())
.setDisplay(target.getDisplay())
));
translationMatch.setSource(VersionConvertor_40_50.convertUri(new UriType(target.getConceptMapUrl())));
retVal.addMatch(translationMatch);
}
}
}
return retVal;
}
private TranslationResult buildReverseTranslationResult(List<TermConceptMapGroupElement> theElements) {
TranslationResult retVal = new TranslationResult();
String msg;
if (theElements.isEmpty()) {
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(false)));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR5.class,
"noMatchesFound");
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
} else {
retVal.setResult(VersionConvertor_40_50.convertBoolean(new BooleanType(true)));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR5.class,
"matchesFound");
retVal.setMessage(VersionConvertor_40_50.convertString(new StringType(msg)));
TranslationMatch translationMatch;
Set<TermConceptMapGroupElement> elementsToReturn = new HashSet<>();
for (TermConceptMapGroupElement element : theElements) {
if (elementsToReturn.add(element)) {
translationMatch = new TranslationMatch();
translationMatch.setConcept(VersionConvertor_40_50.convertCoding(
new Coding()
.setCode(element.getCode())
.setSystem(element.getSystem())
.setVersion(element.getSystemVersion())
.setDisplay(element.getDisplay())
));
translationMatch.setSource(VersionConvertor_40_50.convertUri(new UriType(element.getConceptMapUrl())));
if (element.getConceptMapGroupElementTargets().size() == 1) {
translationMatch.setEquivalence(VersionConvertor_40_50.convertCode(new CodeType(element.getConceptMapGroupElementTargets().get(0).getEquivalence().toCode())));
}
retVal.addMatch(translationMatch);
}
}
}
return retVal;
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (retVal.getDeleted() == null) {
ConceptMap conceptMap = (ConceptMap) theResource;
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, org.hl7.fhir.convertors.conv40_50.ConceptMap.convertConceptMap(conceptMap));
} else {
myHapiTerminologySvc.deleteConceptMapAndChildren(retVal);
}
return retVal;
}
}

View File

@ -0,0 +1,64 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoEncounter;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.Encounter;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
public class FhirResourceDaoEncounterR5 extends FhirResourceDaoR5<Encounter> implements IFhirResourceDaoEncounter<Encounter> {
@Override
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
paramMap.setEverythingMode(theId != null ? EverythingModeEnum.ENCOUNTER_INSTANCE : EverythingModeEnum.ENCOUNTER_TYPE);
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdated);
if (theId != null) {
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
IBundleProvider retVal = search(paramMap);
return retVal;
}
@Override
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
}
}

View File

@ -0,0 +1,28 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoMessageHeader;
import org.hl7.fhir.r5.model.MessageHeader;
public class FhirResourceDaoMessageHeaderR5 extends FhirResourceDaoR5<MessageHeader> implements IFhirResourceDaoMessageHeader<MessageHeader> {
// nothing right now
}

View File

@ -0,0 +1,80 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoPatient;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.Patient;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
public class FhirResourceDaoPatientR5 extends FhirResourceDaoR5<Patient> implements IFhirResourceDaoPatient<Patient> {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theContent != null) {
paramMap.add(Constants.PARAM_CONTENT, theContent);
}
if (theNarrative != null) {
paramMap.add(Constants.PARAM_TEXT, theNarrative);
}
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
paramMap.setEverythingMode(theId != null ? EverythingModeEnum.PATIENT_INSTANCE : EverythingModeEnum.PATIENT_TYPE);
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdated);
if (theId != null) {
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
if (!isPagingProviderDatabaseBacked(theRequest)) {
paramMap.setLoadSynchronous(true);
}
return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName(), new CacheControlDirective().parse(theRequest.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequest);
}
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
}
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
}
}

View File

@ -0,0 +1,85 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r5.model.QuestionnaireResponse;
public class FhirResourceDaoQuestionnaireResponseR5 extends FhirResourceDaoR5<QuestionnaireResponse> {
// @Override
// protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) {
// super.validateResourceForStorage(theResource, theEntityToSave, theRequestDetails);
// if (!myValidateResponses) {
// return;
// }
//
// if (theResource == null || theResource.getQuestionnaire() == null || theResource.getQuestionnaire().getReference() == null || theResource.getQuestionnaire().getReference().isEmpty()) {
// return;
// }
//
// FhirValidator val = getContext().newValidator();
// val.setValidateAgainstStandardSchema(false);
// val.setValidateAgainstStandardSchematron(false);
//
// val.registerValidatorModule(myQuestionnaireResponseValidatorR5);
//
// ValidationResult result = val.validateWithResult(getContext().newJsonParser().parseResource(getContext().newJsonParser().encodeResourceToString(theResource)));
// if (!result.isSuccessful()) {
// IBaseOperationOutcome oo = getContext().newJsonParser().parseResource(OperationOutcome.class, getContext().newJsonParser().encodeResourceToString(result.toOperationOutcome()));
// throw new UnprocessableEntityException(getContext(), oo);
// }
// }
//
// public class JpaResourceLoader implements IResourceLoader {
//
// private RequestDetails myRequestDetails;
//
// public JpaResourceLoader(RequestDetails theRequestDetails) {
// super();
// myRequestDetails = theRequestDetails;
// }
//
// @Override
// public <T extends IBaseResource> T load(Class<T> theType, IIdType theId) throws ResourceNotFoundException {
//
// /*
// * The QuestionnaireResponse validator uses RI structures, so for now we need to convert between that and HAPI
// * structures. This is a bit hackish, but hopefully it will go away at some point.
// */
// if ("ValueSet".equals(theType.getSimpleName())) {
// IFhirResourceDao<ValueSet> dao = getDao(ValueSet.class);
// ValueSet in = dao.read(theId, myRequestDetails);
// return (T) in;
// } else if ("Questionnaire".equals(theType.getSimpleName())) {
// IFhirResourceDao<Questionnaire> dao = getDao(Questionnaire.class);
// Questionnaire vs = dao.read(theId, myRequestDetails);
// return (T) vs;
// } else {
// // Should not happen, validator will only ask for these two
// throw new IllegalStateException("Unexpected request to load resource of type " + theType);
// }
//
// }
//
// }
}

View File

@ -0,0 +1,163 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.delete.DeleteConflictList;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoR5<T extends IAnyResource> extends BaseHapiFhirResourceDao<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR5.class);
@Autowired()
@Qualifier("myInstanceValidatorR5")
private IValidatorModule myInstanceValidator;
@Override
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage, String theCode) {
OperationOutcome oo = new OperationOutcome();
OperationOutcomeIssueComponent issue = oo.addIssue();
issue.getSeverityElement().setValueAsString(theSeverity);
issue.setDiagnostics(theMessage);
try {
issue.setCode(OperationOutcome.IssueType.fromCode(theCode));
} catch (FHIRException e) {
ourLog.error("Unknown code: {}", theCode);
}
return oo;
}
@Override
public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId);
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
if (theMode == ValidationModeEnum.DELETE) {
if (theId == null || theId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied. ID is required when validating with mode=DELETE");
}
final ResourceTable entity = readEntityLatestVersion(theId, theRequest);
// Validate that there are no resources pointing to the candidate that
// would prevent deletion
DeleteConflictList deleteConflicts = new DeleteConflictList();
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete()) {
myDeleteConflictService.validateOkToDelete(deleteConflicts, entity, true, theRequest);
}
myDeleteConflictService.validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Ok to delete");
return new MethodOutcome(new IdType(theId.getValue()), oo);
}
FhirValidator validator = getContext().newValidator();
validator.registerValidatorModule(myInstanceValidator);
validator.registerValidatorModule(new IdChecker(theMode));
IBaseResource resourceToValidateById = null;
if (theId != null && theId.hasResourceType() && theId.hasIdPart()) {
Class<? extends IBaseResource> type = getContext().getResourceDefinition(theId.getResourceType()).getImplementingClass();
IFhirResourceDao<? extends IBaseResource> dao = getDao(type);
resourceToValidateById = dao.read(theId, theRequest);
}
ValidationResult result;
if (theResource == null) {
if (resourceToValidateById != null) {
result = validator.validateWithResult(resourceToValidateById);
} else {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
throw new InvalidRequestException(msg);
}
} else if (isNotBlank(theRawResource)) {
result = validator.validateWithResult(theRawResource);
} else {
result = validator.validateWithResult(theResource);
}
if (result.isSuccessful()) {
MethodOutcome retVal = new MethodOutcome();
retVal.setOperationOutcome(result.toOperationOutcome());
return retVal;
} else {
throw new PreconditionFailedException("Validation failed", result.toOperationOutcome());
}
}
private class IdChecker implements IValidatorModule {
private ValidationModeEnum myMode;
public IdChecker(ValidationModeEnum theMode) {
myMode = theMode;
}
@Override
public void validateResource(IValidationContext<IBaseResource> theCtx) {
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
if (myMode == ValidationModeEnum.CREATE) {
if (hasId) {
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
}
} else if (myMode == ValidationModeEnum.UPDATE) {
if (hasId == false) {
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
}
}
}
}
}

View File

@ -0,0 +1,155 @@
package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ElementUtil;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.utils.FHIRLexer;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isBlank;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class FhirResourceDaoSearchParameterR5 extends FhirResourceDaoR5<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
public static final DefaultProfileValidationSupport VALIDATION_SUPPORT = new DefaultProfileValidationSupport();
@Autowired
private IFhirSystemDao<Bundle, Meta> mySystemDao;
protected void markAffectedResources(SearchParameter theResource) {
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
String expression = theResource != null ? theResource.getExpression() : null;
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
}
@Override
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
super.postPersist(theEntity, theResource);
markAffectedResources(theResource);
}
@Override
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource) {
super.postUpdate(theEntity, theResource);
markAffectedResources(theResource);
}
@Override
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete) {
super.preDelete(theResourceToDelete, theEntityToDelete);
markAffectedResources(theResourceToDelete);
}
@Override
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource, theEntityToSave);
Enum<?> status = theResource.getStatus();
List<CodeType> base = theResource.getBase();
String expression = theResource.getExpression();
FhirContext context = getContext();
Enum<?> type = theResource.getType();
FhirResourceDaoSearchParameterR5.validateSearchParam(type, status, base, expression, context, getConfig());
}
public static void validateSearchParam(Enum<?> theType, Enum<?> theStatus, List<? extends IPrimitiveType> theBase, String theExpression, FhirContext theContext, DaoConfig theDaoConfig) {
if (theStatus == null) {
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid");
}
if (ElementUtil.isEmpty(theBase) && (theType == null || !Enumerations.SearchParamType.COMPOSITE.name().equals(theType.name()))) {
throw new UnprocessableEntityException("SearchParameter.base is missing");
}
if (theType != null && theType.name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theExpression)) {
// this is ok
} else if (isBlank(theExpression)) {
throw new UnprocessableEntityException("SearchParameter.expression is missing");
} else {
theExpression = theExpression.trim();
if (!theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R5)) {
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(theExpression);
for (String nextPath : expressionSplit) {
nextPath = nextPath.trim();
int dotIdx = nextPath.indexOf('.');
if (dotIdx == -1) {
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
}
String resourceName = nextPath.substring(0, dotIdx);
try {
theContext.getResourceDefinition(resourceName);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
}
if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
IBaseResource temporaryInstance = theContext.getResourceDefinition(resourceName).newInstance();
try {
theContext.newFluentPath().evaluate(temporaryInstance, nextPath, IBase.class);
} catch (Exception e) {
String msg = theContext.getLocalizer().getMessage(FhirResourceDaoSearchParameterR5.class, "invalidSearchParamExpression", nextPath, e.getMessage());
throw new UnprocessableEntityException(msg, e);
}
}
}
}
} else {
FHIRPathEngine fhirPathEngine = new FHIRPathEngine(new HapiWorkerContext(theContext, VALIDATION_SUPPORT));
try {
fhirPathEngine.parse(theExpression);
} catch (FHIRLexer.FHIRLexerException e) {
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + theExpression + "\": " + e.getMessage());
}
}
} // if have expression
}
}

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoStructureDefinition;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.springframework.beans.factory.annotation.Autowired;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class FhirResourceDaoStructureDefinitionR5 extends FhirResourceDaoR5<StructureDefinition> implements IFhirResourceDaoStructureDefinition<StructureDefinition> {
@Autowired
private IValidationSupport myValidationSupport;
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theName) {
StructureDefinition output = myValidationSupport.generateSnapshot(theInput, theUrl, theWebUrl, theName);
Validate.notNull(output);
return output;
}
}

View File

@ -0,0 +1,169 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
import ca.uhn.fhir.jpa.dao.data.ISubscriptionTableDao;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.Subscription;
import org.hl7.fhir.r5.model.Subscription.SubscriptionChannelType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nullable;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class FhirResourceDaoSubscriptionR5 extends FhirResourceDaoR5<Subscription> implements IFhirResourceDaoSubscription<Subscription> {
@Autowired
private ISubscriptionTableDao mySubscriptionTableDao;
private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
SubscriptionTable subscriptionEntity = new SubscriptionTable();
subscriptionEntity.setCreated(new Date());
subscriptionEntity.setSubscriptionResource(theEntity);
myEntityManager.persist(subscriptionEntity);
}
@Override
public Long getSubscriptionTablePidForSubscriptionResource(IIdType theId, RequestDetails theRequest) {
ResourceTable entity = readEntityLatestVersion(theId, theRequest);
SubscriptionTable table = mySubscriptionTableDao.findOneByResourcePid(entity.getId());
if (table == null) {
return null;
}
return table.getId();
}
@Override
protected void postPersist(ResourceTable theEntity, Subscription theSubscription) {
super.postPersist(theEntity, theSubscription);
createSubscriptionTable(theEntity, theSubscription);
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (theDeletedTimestampOrNull != null) {
Long subscriptionId = getSubscriptionTablePidForSubscriptionResource(theEntity.getIdDt(), theRequest);
if (subscriptionId != null) {
mySubscriptionTableDao.deleteAllForSubscription(retVal);
}
}
return retVal;
}
protected void validateChannelEndpoint(Subscription theResource) {
if (isBlank(theResource.getChannel().getEndpoint())) {
throw new UnprocessableEntityException("Rest-hook subscriptions must have Subscription.channel.endpoint defined");
}
}
protected void validateChannelPayload(Subscription theResource) {
if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
}
}
@Nullable
public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
if (theResource.getStatus() == null) {
throw new UnprocessableEntityException("Can not process submitted Subscription - Subscription.status must be populated on this server");
}
switch (theResource.getStatus()) {
case REQUESTED:
case ACTIVE:
break;
case ERROR:
case OFF:
case NULL:
return null;
}
String query = theResource.getCriteria();
if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated");
}
int sep = query.indexOf('?');
if (sep <= 1) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
String resType = query.substring(0, sep);
if (resType.contains("/")) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
if (theResource.getChannel().getType() == null) {
throw new UnprocessableEntityException("Subscription.channel.type must be populated");
} else if (theResource.getChannel().getType() == SubscriptionChannelType.RESTHOOK) {
validateChannelPayload(theResource);
validateChannelEndpoint(theResource);
}
RuntimeResourceDefinition resDef;
try {
resDef = getContext().getResourceDefinition(resType);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
}
return resDef;
}
@Override
protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource, theEntityToSave);
RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
if (resDef == null) {
return;
}
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
if (dao == null) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
}
if (theResource.getChannel().getType() == null) {
throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
}
}
}

View File

@ -0,0 +1,325 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.ElementUtil;
import org.apache.commons.codec.binary.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.FilterOperator;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoValueSetR5 extends FhirResourceDaoR5<ValueSet> implements IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> {
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@Autowired
@Qualifier("myJpaValidationSupportChainR5")
private IValidationSupport myValidationSupport;
@Autowired
private IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemDao;
@Override
public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
ValueSet source = read(theId, theRequestDetails);
return expand(source, theFilter);
}
private ValueSet doExpand(ValueSet theSource) {
/*
* If all of the code systems are supported by the HAPI FHIR terminology service, let's
* use that as it's more efficient.
*/
boolean allSystemsAreSuppportedByTerminologyService = true;
for (ConceptSetComponent next : theSource.getCompose().getInclude()) {
if (!isBlank(next.getSystem()) && !myTerminologySvc.supportsSystem(next.getSystem())) {
allSystemsAreSuppportedByTerminologyService = false;
}
}
for (ConceptSetComponent next : theSource.getCompose().getExclude()) {
if (!isBlank(next.getSystem()) && !myTerminologySvc.supportsSystem(next.getSystem())) {
allSystemsAreSuppportedByTerminologyService = false;
}
}
if (allSystemsAreSuppportedByTerminologyService) {
return (ValueSet) myTerminologySvc.expandValueSet(theSource);
}
HapiWorkerContext workerContext = new HapiWorkerContext(getContext(), myValidationSupport);
ValueSetExpansionOutcome outcome = workerContext.expand(theSource, null);
ValueSet retVal = outcome.getValueset();
retVal.setStatus(PublicationStatus.ACTIVE);
return retVal;
// ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
//
// ValueSet retVal = new ValueSet();
// retVal.getMeta().setLastUpdated(new Date());
// retVal.setExpansion(expansion);
// return retVal;
}
private void validateIncludes(String name, List<ConceptSetComponent> listToValidate) {
for (ConceptSetComponent nextExclude : listToValidate) {
if (isBlank(nextExclude.getSystem()) && !ElementUtil.isEmpty(nextExclude.getConcept(), nextExclude.getFilter())) {
throw new InvalidRequestException("ValueSet contains " + name + " criteria with no system defined");
}
}
}
@Override
public ValueSet expandByIdentifier(String theUri, String theFilter) {
if (isBlank(theUri)) {
throw new InvalidRequestException("URI must not be blank or missing");
}
ValueSet source = new ValueSet();
source.getCompose().addInclude().addValueSet(theUri);
if (isNotBlank(theFilter)) {
ConceptSetComponent include = source.getCompose().addInclude();
ConceptSetFilterComponent filter = include.addFilter();
filter.setProperty("display");
filter.setOp(FilterOperator.EQUAL);
filter.setValue(theFilter);
}
ValueSet retVal = doExpand(source);
return retVal;
// if (defaultValueSet != null) {
// source = getContext().newJsonParser().parseResource(ValueSet.class, getContext().newJsonParser().encodeResourceToString(defaultValueSet));
// } else {
// IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
// if (ids.size() == 0) {
// throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
// }
// source = (ValueSet) ids.getResources(0, 1).get(0);
// }
//
// return expand(defaultValueSet, theFilter);
}
@Override
public ValueSet expand(ValueSet source, String theFilter) {
ValueSet toExpand = new ValueSet();
// for (UriType next : source.getCompose().getInclude()) {
// ConceptSetComponent include = toExpand.getCompose().addInclude();
// include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include);
// }
for (ConceptSetComponent next : source.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
addFilterIfPresent(theFilter, next);
}
if (toExpand.getCompose().isEmpty()) {
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
}
toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
}
private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionContainsComponent> theContains, String theFilter) {
for (int idx = 0; idx < theContains.size(); idx++) {
ValueSetExpansionContainsComponent next = theContains.get(idx);
if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) {
theContains.remove(idx);
idx--;
if (theTotalElement.getValue() != null) {
theTotalElement.setValue(theTotalElement.getValue() - 1);
}
}
applyFilter(theTotalElement, next.getContains(), theFilter);
}
}
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
if (ElementUtil.isEmpty(include.getConcept())) {
if (isNotBlank(theFilter)) {
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
}
}
}
@Override
public ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
List<IIdType> valueSetIds = Collections.emptyList();
boolean haveCodeableConcept = theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0;
boolean haveCoding = theCoding != null && theCoding.isEmpty() == false;
boolean haveCode = theCode != null && theCode.isEmpty() == false;
if (!haveCodeableConcept && !haveCoding && !haveCode) {
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate");
}
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
throw new InvalidRequestException("$validate-code can only validate (system AND code) OR (coding) OR (codeableConcept)");
}
boolean haveIdentifierParam = theValueSetIdentifier != null && theValueSetIdentifier.isEmpty() == false;
ValueSet vs = null;
if (theId != null) {
vs = read(theId, theRequestDetails);
} else if (haveIdentifierParam) {
vs = myValidationSupport.fetchResource(getContext(), ValueSet.class, theValueSetIdentifier.getValue());
if (vs == null) {
throw new InvalidRequestException("Unknown ValueSet identifier: " + theValueSetIdentifier.getValue());
}
} else {
if (theCode == null || theCode.isEmpty()) {
throw new InvalidRequestException("Either ValueSet ID or ValueSet identifier or system and code must be provided. Unable to validate.");
}
// String code = theCode.getValue();
// String system = toStringOrNull(theSystem);
IContextValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(theCode, theSystem, null, null);
if (result.isFound()) {
ValidateCodeResult retVal = new ValidateCodeResult(true, "Found code", result.getCodeDisplay());
return retVal;
}
}
if (vs != null) {
ValueSet expansion = doExpand(vs);
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
if (result != null) {
if (theDisplay != null && isNotBlank(theDisplay.getValue()) && isNotBlank(result.getDisplay())) {
if (!theDisplay.getValue().equals(result.getDisplay())) {
return new ValidateCodeResult(false, "Display for code does not match", result.getDisplay());
}
}
return result;
}
}
return new ValidateCodeResult(false, "Code not found", null);
}
private String toStringOrNull(IPrimitiveType<String> thePrimitive) {
return thePrimitive != null ? thePrimitive.getValue() : null;
}
private ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode,
Coding theCoding, CodeableConcept theCodeableConcept) {
for (ValueSetExpansionContainsComponent nextCode : contains) {
ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept);
if (result != null) {
return result;
}
String system = nextCode.getSystem();
String code = nextCode.getCode();
if (isNotBlank(theCode)) {
if (theCode.equals(code) && (isBlank(theSystem) || theSystem.equals(system))) {
return new ValidateCodeResult(true, "Validation succeeded", nextCode.getDisplay());
}
} else if (theCoding != null) {
if (StringUtils.equals(system, theCoding.getSystem()) && StringUtils.equals(code, theCoding.getCode())) {
return new ValidateCodeResult(true, "Validation succeeded", nextCode.getDisplay());
}
} else {
for (Coding next : theCodeableConcept.getCoding()) {
if (StringUtils.equals(system, next.getSystem()) && StringUtils.equals(code, next.getCode())) {
return new ValidateCodeResult(true, "Validation succeeded", nextCode.getDisplay());
}
}
}
}
return null;
}
@Override
public void purgeCaches() {
// nothing
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
if (myDaoConfig.isPreExpandValueSetsExperimental()) {
if (retVal.getDeleted() == null) {
ValueSet valueSet = (ValueSet) theResource;
myHapiTerminologySvc.storeTermValueSetAndChildren(retVal, org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSet));
} else {
myHapiTerminologySvc.deleteValueSetAndChildren(retVal);
}
}
return retVal;
}
}

View File

@ -0,0 +1,99 @@
package ca.uhn.fhir.jpa.dao.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2;
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Meta;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import javax.persistence.TypedQuery;
import java.util.Collection;
import java.util.List;
public class FhirSystemDaoR5 extends BaseHapiFhirSystemDao<Bundle, Meta> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoR5.class);
@Autowired
private TransactionProcessor<Bundle, BundleEntryComponent> myTransactionProcessor;
@PostConstruct
public void start() {
myTransactionProcessor.setDao(this);
}
@Override
public Meta metaGetOperation(RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails);
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)";
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList();
return toMeta(tagDefinitions);
}
@Override
public IBaseBundle processMessage(RequestDetails theRequestDetails, IBaseBundle theMessage) {
return FhirResourceDaoMessageHeaderDstu2.throwProcessMessageNotImplemented();
}
protected Meta toMeta(Collection<TagDefinition> tagDefinitions) {
Meta retVal = new Meta();
for (TagDefinition next : tagDefinitions) {
switch (next.getTagType()) {
case PROFILE:
retVal.addProfile(next.getCode());
break;
case SECURITY_LABEL:
retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
break;
case TAG:
retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
break;
}
}
return retVal;
}
@Transactional(propagation = Propagation.NEVER)
@Override
public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) {
return myTransactionProcessor.transaction(theRequestDetails, theRequest);
}
}

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.jpa.dao.r5;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IJpaValidationSupportR5 extends IValidationSupport {
// nothing yet
}

View File

@ -0,0 +1,200 @@
package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import java.util.Collections;
import java.util.List;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
@Transactional(value = TxType.REQUIRED)
public class JpaValidationSupportR5 implements IJpaValidationSupportR5, ApplicationContextAware {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaValidationSupportR5.class);
private IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
private IFhirResourceDao<ValueSet> myValueSetDao;
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
private IFhirResourceDao<CodeSystem> myCodeSystemDao;
@Autowired
private FhirContext myR5Ctx;
private ApplicationContext myApplicationContext;
/**
* Constructor
*/
public JpaValidationSupportR5() {
super();
}
@Override
@Transactional(value = TxType.SUPPORTS)
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) {
return null;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
@Transactional(value = TxType.SUPPORTS)
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) {
return fetchResource(theCtx, CodeSystem.class, theSystem);
}
@Override
public ValueSet fetchValueSet(FhirContext theCtx, String theSystem) {
return fetchResource(theCtx, ValueSet.class, theSystem);
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
IdType id = new IdType(theUri);
boolean localReference = false;
if (id.hasBaseUrl() == false && id.hasIdPart() == true) {
localReference = true;
}
String resourceName = myR5Ctx.getResourceDefinition(theClass).getName();
IBundleProvider search;
if ("ValueSet".equals(resourceName)) {
if (localReference) {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(IAnyResource.SP_RES_ID, new StringParam(theUri));
search = myValueSetDao.search(params);
if (search.size() == 0) {
params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
search = myValueSetDao.search(params);
}
} else {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
search = myValueSetDao.search(params);
}
} else if ("StructureDefinition".equals(resourceName)) {
// Don't allow the core FHIR definitions to be overwritten
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
String typeName = theUri.substring("http://hl7.org/fhir/StructureDefinition/".length());
if (myR5Ctx.getElementDefinition(typeName) != null) {
return null;
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(StructureDefinition.SP_URL, new UriParam(theUri));
search = myStructureDefinitionDao.search(params);
} else if ("Questionnaire".equals(resourceName)) {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(IAnyResource.SP_RES_ID, new StringParam(id.getIdPart()));
search = myQuestionnaireDao.search(params);
} else if ("CodeSystem".equals(resourceName)) {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(CodeSystem.SP_URL, new UriParam(theUri));
search = myCodeSystemDao.search(params);
} else {
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
}
if (search.size() == 0) {
return null;
}
if (search.size() > 1) {
ourLog.warn("Found multiple {} instances with URL search value of: {}", resourceName, theUri);
}
return (T) search.getResources(0, 1).get(0);
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return fetchResource(theCtx, StructureDefinition.class, theUrl);
}
@Override
@Transactional(value = TxType.SUPPORTS)
public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) {
return false;
}
@Override
public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException {
myApplicationContext = theApplicationContext;
}
@PostConstruct
public void start() {
myStructureDefinitionDao = myApplicationContext.getBean("myStructureDefinitionDaoR5", IFhirResourceDao.class);
myValueSetDao = myApplicationContext.getBean("myValueSetDaoR5", IFhirResourceDao.class);
myQuestionnaireDao = myApplicationContext.getBean("myQuestionnaireDaoR5", IFhirResourceDao.class);
myCodeSystemDao = myApplicationContext.getBean("myCodeSystemDaoR5", IFhirResourceDao.class);
}
@Override
@Transactional(value = TxType.SUPPORTS)
public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) {
return null;
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return null;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}
}

View File

@ -0,0 +1,166 @@
package ca.uhn.fhir.jpa.dao.r5;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.Resource;
import java.util.Date;
import java.util.List;
public class TransactionProcessorVersionAdapterR5 implements TransactionProcessor.ITransactionProcessorVersionAdapter<Bundle, Bundle.BundleEntryComponent> {
@Override
public void setResponseStatus(Bundle.BundleEntryComponent theBundleEntry, String theStatus) {
theBundleEntry.getResponse().setStatus(theStatus);
}
@Override
public void setResponseLastModified(Bundle.BundleEntryComponent theBundleEntry, Date theLastModified) {
theBundleEntry.getResponse().setLastModified(theLastModified);
}
@Override
public void setResource(Bundle.BundleEntryComponent theBundleEntry, IBaseResource theResource) {
theBundleEntry.setResource((Resource) theResource);
}
@Override
public IBaseResource getResource(Bundle.BundleEntryComponent theBundleEntry) {
return theBundleEntry.getResource();
}
@Override
public String getBundleType(Bundle theRequest) {
if (theRequest.getType() == null) {
return null;
}
return theRequest.getTypeElement().getValue().toCode();
}
@Override
public void populateEntryWithOperationOutcome(BaseServerResponseException theCaughtEx, Bundle.BundleEntryComponent theEntry) {
OperationOutcome oo = new OperationOutcome();
oo.addIssue()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setDiagnostics(theCaughtEx.getMessage())
.setCode(OperationOutcome.IssueType.EXCEPTION);
theEntry.getResponse().setOutcome(oo);
}
@Override
public Bundle createBundle(String theBundleType) {
Bundle resp = new Bundle();
try {
resp.setType(Bundle.BundleType.fromCode(theBundleType));
} catch (FHIRException theE) {
throw new InternalErrorException("Unknown bundle type: " + theBundleType);
}
return resp;
}
@Override
public List<Bundle.BundleEntryComponent> getEntries(Bundle theRequest) {
return theRequest.getEntry();
}
@Override
public void addEntry(Bundle theBundle, Bundle.BundleEntryComponent theEntry) {
theBundle.addEntry(theEntry);
}
@Override
public Bundle.BundleEntryComponent addEntry(Bundle theBundle) {
return theBundle.addEntry();
}
@Override
public String getEntryRequestVerb(Bundle.BundleEntryComponent theEntry) {
String retVal = null;
Bundle.HTTPVerb value = theEntry.getRequest().getMethodElement().getValue();
if (value != null) {
retVal = value.toCode();
}
return retVal;
}
@Override
public String getFullUrl(Bundle.BundleEntryComponent theEntry) {
return theEntry.getFullUrl();
}
@Override
public String getEntryIfNoneExist(Bundle.BundleEntryComponent theEntry) {
return theEntry.getRequest().getIfNoneExist();
}
@Override
public String getEntryRequestUrl(Bundle.BundleEntryComponent theEntry) {
return theEntry.getRequest().getUrl();
}
@Override
public void setResponseLocation(Bundle.BundleEntryComponent theEntry, String theResponseLocation) {
theEntry.getResponse().setLocation(theResponseLocation);
}
@Override
public void setResponseETag(Bundle.BundleEntryComponent theEntry, String theEtag) {
theEntry.getResponse().setEtag(theEtag);
}
@Override
public String getEntryRequestIfMatch(Bundle.BundleEntryComponent theEntry) {
return theEntry.getRequest().getIfMatch();
}
@Override
public String getEntryRequestIfNoneExist(Bundle.BundleEntryComponent theEntry) {
return theEntry.getRequest().getIfNoneExist();
}
@Override
public String getEntryRequestIfNoneMatch(Bundle.BundleEntryComponent theEntry) {
return theEntry.getRequest().getIfNoneMatch();
}
@Override
public void setResponseOutcome(Bundle.BundleEntryComponent theEntry, IBaseOperationOutcome theOperationOutcome) {
theEntry.getResponse().setOutcome((Resource) theOperationOutcome);
}
@Override
public void setRequestVerb(Bundle.BundleEntryComponent theEntry, String theVerb) {
theEntry.getRequest().setMethod(Bundle.HTTPVerb.fromCode(theVerb));
}
@Override
public void setRequestUrl(Bundle.BundleEntryComponent theEntry, String theUrl) {
theEntry.getRequest().setUrl(theUrl);
}
}

View File

@ -68,7 +68,7 @@ public class BaseJpaResourceProviderStructureDefinitionDstu3 extends JpaResource
sd = (StructureDefinition) outcome.getResources(0, 1).get(0);
}
return getDao().generateSnapshot(sd, null, null);
return getDao().generateSnapshot(sd, null, null, null);
}
@Override

View File

@ -62,13 +62,15 @@ public class BaseJpaResourceProviderStructureDefinitionR4 extends JpaResourcePro
map.setLoadSynchronousUpTo(2);
map.add(StructureDefinition.SP_URL, new UriParam(theUrl.getValue()));
IBundleProvider outcome = getDao().search(map, theRequestDetails);
if (outcome.size() == 0) {
Integer numResults = outcome.size();
assert numResults != null;
if (numResults == 0) {
throw new ResourceNotFoundException("No StructureDefiniton found with url = '" + theUrl.getValue() + "'");
}
sd = (StructureDefinition) outcome.getResources(0, 1).get(0);
}
return getDao().generateSnapshot(sd, null, null);
return getDao().generateSnapshot(sd, null, null, null);
}
@Override

View File

@ -0,0 +1,92 @@
package ca.uhn.fhir.jpa.provider.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.r5.model.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
public class BaseJpaResourceProviderCodeSystemR5 extends JpaResourceProviderR5<CodeSystem> {
/**
* $lookup operation
*/
@SuppressWarnings("unchecked")
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name="name", type=StringType.class, min=1),
@OperationParam(name="version", type=StringType.class, min=0),
@OperationParam(name="display", type=StringType.class, min=1),
@OperationParam(name="abstract", type=BooleanType.class, min=1),
})
public Parameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeType theCode,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> theProperties,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
IContextValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails);
result.throwNotFoundIfAppropriate();
return (Parameters) result.toParameters(theRequestDetails.getFhirContext(), theProperties);
} finally {
endRequest(theServletRequest);
}
}
/**
* $subsumes operation
*/
@Operation(name = JpaConstants.OPERATION_SUBSUMES, idempotent = true, returnParameters= {
@OperationParam(name="outcome", type=CodeType.class, min=1),
})
public Parameters subsumes(
HttpServletRequest theServletRequest,
@OperationParam(name="codeA", min=0, max=1) CodeType theCodeA,
@OperationParam(name="codeB", min=0, max=1) CodeType theCodeB,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="codingA", min=0, max=1) Coding theCodingA,
@OperationParam(name="codingB", min=0, max=1) Coding theCodingB,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
IFhirResourceDaoCodeSystem.SubsumesResult result = dao.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB, theRequestDetails);
return (Parameters) result.toParameters(theRequestDetails.getFhirContext());
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -0,0 +1,85 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoComposition;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.*;
import java.util.List;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class BaseJpaResourceProviderCompositionR5 extends JpaResourceProviderR5<Composition> {
/**
* Composition/123/$document
*/
@Operation(name = JpaConstants.OPERATION_DOCUMENT, idempotent = true, bundleType=BundleTypeEnum.DOCUMENT)
public IBaseBundle getDocumentForComposition(
javax.servlet.http.HttpServletRequest theServletRequest,
@IdParam
IdType theId,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@Sort
SortSpec theSortSpec,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails);
List<IBaseResource> resourceList = bundleProvider.getResources(0, bundleProvider.size());
boolean foundCompositionResource = false;
Bundle bundle = new Bundle().setType(Bundle.BundleType.DOCUMENT);
for (IBaseResource resource : resourceList) {
bundle.addEntry(new Bundle.BundleEntryComponent().setResource((Resource) resource));
}
return bundle;
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -0,0 +1,144 @@
package ca.uhn.fhir.jpa.provider.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.convertors.VersionConvertor_40_50;
import org.hl7.fhir.r5.model.*;
import javax.servlet.http.HttpServletRequest;
public class BaseJpaResourceProviderConceptMapR5 extends JpaResourceProviderR5<ConceptMap> {
@Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1),
@OperationParam(name = "message", type = StringType.class, min = 0, max = 1),
})
public Parameters translate(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "code", min = 0, max = 1) CodeType theSourceCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSourceCodeSystem,
@OperationParam(name = "version", min = 0, max = 1) StringType theSourceCodeSystemVersion,
@OperationParam(name = "source", min = 0, max = 1) UriType theSourceValueSet,
@OperationParam(name = "coding", min = 0, max = 1) Coding theSourceCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theSourceCodeableConcept,
@OperationParam(name = "target", min = 0, max = 1) UriType theTargetValueSet,
@OperationParam(name = "targetsystem", min = 0, max = 1) UriType theTargetCodeSystem,
@OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse,
RequestDetails theRequestDetails
) {
boolean haveSourceCode = theSourceCode != null
&& theSourceCode.hasCode();
boolean haveSourceCodeSystem = theSourceCodeSystem != null
&& theSourceCodeSystem.hasValue();
boolean haveSourceCodeSystemVersion = theSourceCodeSystemVersion != null
&& theSourceCodeSystemVersion.hasValue();
boolean haveSourceValueSet = theSourceValueSet != null
&& theSourceValueSet.hasValue();
boolean haveSourceCoding = theSourceCoding != null
&& theSourceCoding.hasCode();
boolean haveSourceCodeableConcept= theSourceCodeableConcept != null
&& theSourceCodeableConcept.hasCoding()
&& theSourceCodeableConcept.getCodingFirstRep().hasCode();
boolean haveTargetValueSet = theTargetValueSet != null
&& theTargetValueSet.hasValue();
boolean haveTargetCodeSystem = theTargetCodeSystem != null
&& theTargetCodeSystem.hasValue();
boolean haveReverse = theReverse != null;
boolean haveId = theId != null && theId.hasIdPart();
// <editor-fold desc="Filters">
if ((!haveSourceCode && !haveSourceCoding && !haveSourceCodeableConcept)
|| moreThanOneTrue(haveSourceCode, haveSourceCoding, haveSourceCodeableConcept)) {
throw new InvalidRequestException("One (and only one) of the in parameters (code, coding, codeableConcept) must be provided, to identify the code that is to be translated.");
}
TranslationRequest translationRequest = new TranslationRequest();
if (haveSourceCode) {
translationRequest.getCodeableConcept().addCoding().setCodeElement(VersionConvertor_40_50.convertCode(theSourceCode));
if (haveSourceCodeSystem) {
translationRequest.getCodeableConcept().getCodingFirstRep().setSystemElement(VersionConvertor_40_50.convertUri(theSourceCodeSystem));
}
if (haveSourceCodeSystemVersion) {
translationRequest.getCodeableConcept().getCodingFirstRep().setVersionElement(VersionConvertor_40_50.convertString(theSourceCodeSystemVersion));
}
} else if (haveSourceCoding) {
translationRequest.getCodeableConcept().addCoding(VersionConvertor_40_50.convertCoding(theSourceCoding));
} else {
translationRequest.setCodeableConcept(VersionConvertor_40_50.convertCodeableConcept(theSourceCodeableConcept));
}
if (haveSourceValueSet) {
translationRequest.setSource(VersionConvertor_40_50.convertUri(theSourceValueSet));
}
if (haveTargetValueSet) {
translationRequest.setTarget(VersionConvertor_40_50.convertUri(theTargetValueSet));
}
if (haveTargetCodeSystem) {
translationRequest.setTargetSystem(VersionConvertor_40_50.convertUri(theTargetCodeSystem));
}
if (haveReverse) {
translationRequest.setReverse(VersionConvertor_40_50.convertBoolean(theReverse));
}
if (haveId) {
translationRequest.setResourceId(theId.getIdPartAsLong());
}
startRequest(theServletRequest);
try {
IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao();
TranslationResult result = dao.translate(translationRequest, theRequestDetails);
org.hl7.fhir.r4.model.Parameters parameters = result.toParameters();
return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters);
} finally {
endRequest(theServletRequest);
}
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,100 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoEncounter;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateRangeParam;
import org.hl7.fhir.r5.model.Encounter;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.UnsignedIntType;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5<Encounter> {
/**
* Encounter/123/$everything
*/
@Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET)
public IBundleProvider EncounterInstanceEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@IdParam
IdType theId,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@Sort
SortSpec theSortSpec
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}}
/**
* /Encounter/$everything
*/
@Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType=BundleTypeEnum.SEARCHSET)
public IBundleProvider EncounterTypeEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@Sort
SortSpec theSortSpec
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.jpa.provider.r5;
import org.hl7.fhir.r5.model.MessageHeader;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class BaseJpaResourceProviderMessageHeaderR5 extends JpaResourceProviderR5<MessageHeader> {
// nothing right now
}

View File

@ -0,0 +1,145 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoPatient;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.UnsignedIntType;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Patient> {
/**
* Patient/123/$everything
*/
@Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
public IBundleProvider patientInstanceEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@IdParam
IdType theId,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
@OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED)
List<StringType> theContent,
@Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
List<StringType> theNarrative,
@Sort
SortSpec theSortSpec,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
} finally {
endRequest(theServletRequest);
}
}
/**
* /Patient/$everything
*/
@Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
public IBundleProvider patientTypeEverything(
javax.servlet.http.HttpServletRequest theServletRequest,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@Description(shortDefinition = "Filter the resources to return only resources matching the given _content filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
@OperationParam(name = Constants.PARAM_CONTENT, min = 0, max = OperationParam.MAX_UNLIMITED)
List<StringType> theContent,
@Description(shortDefinition = "Filter the resources to return only resources matching the given _text filter (note that this filter is applied only to results which link to the given patient, not to the patient itself or to supporting resources linked to by the matched resources)")
@OperationParam(name = Constants.PARAM_TEXT, min = 0, max = OperationParam.MAX_UNLIMITED)
List<StringType> theNarrative,
@Sort
SortSpec theSortSpec,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), theRequestDetails);
} finally {
endRequest(theServletRequest);
}
}
private StringAndListParam toStringAndList(List<StringType> theNarrative) {
StringAndListParam retVal = new StringAndListParam();
if (theNarrative != null) {
for (StringType next : theNarrative) {
if (isNotBlank(next.getValue())) {
retVal.addAnd(new StringOrListParam().addOr(new StringParam(next.getValue())));
}
}
}
if (retVal.getValuesAsQueryTokens().isEmpty()) {
return null;
}
return retVal;
}
}

View File

@ -0,0 +1,81 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoStructureDefinition;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.ValidateUtil;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.StructureDefinition;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class BaseJpaResourceProviderStructureDefinitionR5 extends JpaResourceProviderR5<StructureDefinition> {
/**
* <code>$snapshot</code> operation
*/
@Operation(name=JpaConstants.OPERATION_SNAPSHOT, idempotent = true)
public StructureDefinition snapshot(
@IdParam(optional = true) IdType theId,
@OperationParam(name = "definition") StructureDefinition theStructureDefinition,
@OperationParam(name = "url") StringType theUrl,
RequestDetails theRequestDetails) {
ValidateUtil.exactlyOneNotNullOrThrowInvalidRequestException(
new Object[]{ theId, theStructureDefinition, theUrl },
"Must supply either an ID or a StructureDefinition or a URL (but not more than one of these things)"
);
StructureDefinition sd;
if (theId == null && theStructureDefinition != null && theUrl == null) {
sd = theStructureDefinition;
} else if (theId != null && theStructureDefinition == null) {
sd = getDao().read(theId, theRequestDetails);
} else {
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronousUpTo(2);
map.add(StructureDefinition.SP_URL, new UriParam(theUrl.getValue()));
IBundleProvider outcome = getDao().search(map, theRequestDetails);
Integer numResults = outcome.size();
assert numResults != null;
if (numResults == 0) {
throw new ResourceNotFoundException("No StructureDefiniton found with url = '" + theUrl.getValue() + "'");
}
sd = (StructureDefinition) outcome.getResources(0, 1).get(0);
}
return getDao().generateSnapshot(sd, null, null, null);
}
@Override
public IFhirResourceDaoStructureDefinition<StructureDefinition> getDao() {
return (IFhirResourceDaoStructureDefinition<StructureDefinition>) super.getDao();
}
}

View File

@ -0,0 +1,134 @@
package ca.uhn.fhir.jpa.provider.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.r5.model.*;
import javax.servlet.http.HttpServletRequest;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class BaseJpaResourceProviderValueSetR5 extends JpaResourceProviderR5<ValueSet> {
@Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
public ValueSet expand(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
@OperationParam(name = "url", min = 0, max = 1) UriType theUrl,
@OperationParam(name = "filter", min = 0, max = 1) StringType theFilter,
RequestDetails theRequestDetails) {
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.getValue());
boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false;
if (!haveId && !haveIdentifier && !haveValueSet) {
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request");
}
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
}
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
if (haveId) {
return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
} else if (haveIdentifier) {
return dao.expandByIdentifier(theUrl.getValue(), toFilterString(theFilter));
} else {
return dao.expand(theValueSet, toFilterString(theFilter));
}
} finally {
endRequest(theServletRequest);
}
}
private String toFilterString(StringType theFilter) {
return theFilter != null ? theFilter.getValue() : null;
}
@SuppressWarnings("unchecked")
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@OperationParam(name = "message", type = StringType.class),
@OperationParam(name = "display", type = StringType.class)
})
public Parameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
RequestDetails theRequestDetails
) {
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
ValidateCodeResult result = dao.validateCode(theValueSetUrl, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
Parameters retVal = new Parameters();
retVal.addParameter().setName("result").setValue(new BooleanType(result.isResult()));
if (isNotBlank(result.getMessage())) {
retVal.addParameter().setName("message").setValue(new StringType(result.getMessage()));
}
if (isNotBlank(result.getDisplay())) {
retVal.addParameter().setName("display").setValue(new StringType(result.getDisplay()));
}
return retVal;
} finally {
endRequest(theServletRequest);
}
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,205 @@
package ca.uhn.fhir.jpa.provider.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.ExtensionConstants;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.CapabilityStatement.*;
import org.hl7.fhir.r5.model.Enumerations.SearchParamType;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class JpaConformanceProviderR5 extends org.hl7.fhir.r5.hapi.rest.server.ServerCapabilityStatementProvider {
private volatile CapabilityStatement myCachedValue;
private DaoConfig myDaoConfig;
private ISearchParamRegistry mySearchParamRegistry;
private String myImplementationDescription;
private boolean myIncludeResourceCounts;
private RestfulServer myRestfulServer;
private IFhirSystemDao<Bundle, Meta> mySystemDao;
/**
* Constructor
*/
@CoverageIgnore
public JpaConformanceProviderR5(){
super();
super.setCache(false);
setIncludeResourceCounts(true);
}
/**
* Constructor
*/
public JpaConformanceProviderR5(RestfulServer theRestfulServer, IFhirSystemDao<Bundle, Meta> theSystemDao, DaoConfig theDaoConfig) {
super(theRestfulServer);
myRestfulServer = theRestfulServer;
mySystemDao = theSystemDao;
myDaoConfig = theDaoConfig;
super.setCache(false);
setIncludeResourceCounts(true);
setSearchParamRegistry(theSystemDao.getSearchParamRegistry());
}
public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) {
mySearchParamRegistry = theSearchParamRegistry;
}
@Override
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
CapabilityStatement retVal = myCachedValue;
Map<String, Long> counts = null;
if (myIncludeResourceCounts) {
counts = mySystemDao.getResourceCountsFromCache();
}
counts = defaultIfNull(counts, Collections.emptyMap());
retVal = super.getServerConformance(theRequest, theRequestDetails);
for (CapabilityStatementRestComponent nextRest : retVal.getRest()) {
for (CapabilityStatementRestResourceComponent nextResource : nextRest.getResource()) {
nextResource.setVersioning(ResourceVersionPolicy.VERSIONEDUPDATE);
ConditionalDeleteStatus conditionalDelete = nextResource.getConditionalDelete();
if (conditionalDelete == ConditionalDeleteStatus.MULTIPLE && myDaoConfig.isAllowMultipleDelete() == false) {
nextResource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
}
// Add resource counts
Long count = counts.get(nextResource.getTypeElement().getValueAsString());
if (count != null) {
nextResource.addExtension(new Extension(ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalType(count)));
}
nextResource.getSearchParam().clear();
String resourceName = nextResource.getType();
RuntimeResourceDefinition resourceDef = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
Collection<RuntimeSearchParam> searchParams = mySearchParamRegistry.getSearchParamsByResourceType(resourceDef);
for (RuntimeSearchParam runtimeSp : searchParams) {
CapabilityStatementRestResourceSearchParamComponent confSp = nextResource.addSearchParam();
confSp.setName(runtimeSp.getName());
confSp.setDocumentation(runtimeSp.getDescription());
confSp.setDefinition(runtimeSp.getUri());
switch (runtimeSp.getParamType()) {
case COMPOSITE:
confSp.setType(SearchParamType.COMPOSITE);
break;
case DATE:
confSp.setType(SearchParamType.DATE);
break;
case NUMBER:
confSp.setType(SearchParamType.NUMBER);
break;
case QUANTITY:
confSp.setType(SearchParamType.QUANTITY);
break;
case REFERENCE:
confSp.setType(SearchParamType.REFERENCE);
break;
case STRING:
confSp.setType(SearchParamType.STRING);
break;
case TOKEN:
confSp.setType(SearchParamType.TOKEN);
break;
case URI:
confSp.setType(SearchParamType.URI);
break;
case HAS:
// Shouldn't happen
break;
}
}
}
}
if (myDaoConfig.getSupportedSubscriptionTypes().contains(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET)) {
if (isNotBlank(myDaoConfig.getWebsocketContextPath())) {
Extension websocketExtension = new Extension();
websocketExtension.setUrl(Constants.CAPABILITYSTATEMENT_WEBSOCKET_URL);
websocketExtension.setValue(new UriType(myDaoConfig.getWebsocketContextPath()));
retVal.getRestFirstRep().addExtension(websocketExtension);
}
}
massage(retVal);
retVal.getImplementation().setDescription(myImplementationDescription);
myCachedValue = retVal;
return retVal;
}
public boolean isIncludeResourceCounts() {
return myIncludeResourceCounts;
}
/**
* Subclasses may override
*/
protected void massage(CapabilityStatement theStatement) {
// nothing
}
public void setDaoConfig(DaoConfig myDaoConfig) {
this.myDaoConfig = myDaoConfig;
}
@CoverageIgnore
public void setImplementationDescription(String theImplDesc) {
myImplementationDescription = theImplDesc;
}
public void setIncludeResourceCounts(boolean theIncludeResourceCounts) {
myIncludeResourceCounts = theIncludeResourceCounts;
}
@Override
public void setRestfulServer(RestfulServer theRestfulServer) {
this.myRestfulServer = theRestfulServer;
super.setRestfulServer(theRestfulServer);
}
@CoverageIgnore
public void setSystemDao(IFhirSystemDao<Bundle, Meta> mySystemDao) {
this.mySystemDao = mySystemDao;
}
}

View File

@ -0,0 +1,176 @@
package ca.uhn.fhir.jpa.provider.r5;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.*;
import javax.servlet.http.HttpServletRequest;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.*;
public class JpaResourceProviderR5<T extends IAnyResource> extends BaseJpaResourceProvider<T> {
public JpaResourceProviderR5() {
// nothing
}
public JpaResourceProviderR5(IFhirResourceDao<T> theDao) {
super(theDao);
}
@Create
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
startRequest(theRequest);
try {
if (theConditional != null) {
return getDao().create(theResource, theConditional, theRequestDetails);
} else {
return getDao().create(theResource, theRequestDetails);
}
} finally {
endRequest(theRequest);
}
}
@Delete()
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdType theResource, @ConditionalUrlParam(supportsMultiple = true) String theConditional, RequestDetails theRequestDetails) {
startRequest(theRequest);
try {
if (theConditional != null) {
return getDao().deleteByUrl(theConditional, theRequestDetails);
} else {
return getDao().delete(theResource, theRequestDetails);
}
} finally {
endRequest(theRequest);
}
}
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
})
public Parameters expunge(
@IdParam IIdType theIdParam,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
RequestDetails theRequest) {
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theIdParam, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters);
}
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
})
public Parameters expunge(
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
RequestDetails theRequest) {
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(null, theLimit, theExpungeDeletedResources, theExpungeOldVersions, null, theRequest);
return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters);
}
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
public Parameters meta(RequestDetails theRequestDetails) {
Parameters parameters = new Parameters();
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theRequestDetails);
parameters.addParameter().setName("return").setValue(metaGetOperation);
return parameters;
}
@Operation(name = OPERATION_META, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
public Parameters meta(@IdParam IdType theId, RequestDetails theRequestDetails) {
Parameters parameters = new Parameters();
Meta metaGetOperation = getDao().metaGetOperation(Meta.class, theId, theRequestDetails);
parameters.addParameter().setName("return").setValue(metaGetOperation);
return parameters;
}
@Operation(name = OPERATION_META_ADD, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
public Parameters metaAdd(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
if (theMeta == null) {
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
}
Parameters parameters = new Parameters();
Meta metaAddOperation = getDao().metaAddOperation(theId, theMeta, theRequestDetails);
parameters.addParameter().setName("return").setValue(metaAddOperation);
return parameters;
}
@Operation(name = OPERATION_META_DELETE, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
public Parameters metaDelete(@IdParam IdType theId, @OperationParam(name = "meta") Meta theMeta, RequestDetails theRequestDetails) {
if (theMeta == null) {
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
}
Parameters parameters = new Parameters();
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta, theRequestDetails));
return parameters;
}
@Update
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdType theId, @ConditionalUrlParam String theConditional, RequestDetails theRequestDetails) {
startRequest(theRequest);
try {
if (theConditional != null) {
return getDao().update(theResource, theConditional, theRequestDetails);
} else {
return getDao().update(theResource, theRequestDetails);
}
} finally {
endRequest(theRequest);
}
}
@Validate
public MethodOutcome validate(@ResourceParam T theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
return validate(theResource, null, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
}
@Validate
public MethodOutcome validate(@ResourceParam T theResource, @IdParam IdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode,
@Validate.Profile String theProfile, RequestDetails theRequestDetails) {
return getDao().validate(theResource, theId, theRawResource, theEncoding, theMode, theProfile, theRequestDetails);
}
}

View File

@ -0,0 +1,270 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProviderDstu2Plus;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class JpaSystemProviderR5 extends BaseJpaSystemProviderDstu2Plus<Bundle, Meta> {
@Autowired()
@Qualifier("mySystemDaoR5")
private IFhirSystemDao<Bundle, Meta> mySystemDao;
@Autowired(required = false)
private IFulltextSearchSvc mySearchDao;
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
})
public Parameters expunge(
@IdParam IIdType theIdParam,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
RequestDetails theRequestDetails
) {
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters);
}
@Operation(name = JpaConstants.OPERATION_EXPUNGE, idempotent = false, returnParameters = {
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_OUT_PARAM_EXPUNGE_COUNT, type = IntegerType.class)
})
public Parameters expunge(
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_LIMIT) IntegerType theLimit,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_DELETED_RESOURCES) BooleanType theExpungeDeletedResources,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_PREVIOUS_VERSIONS) BooleanType theExpungeOldVersions,
@OperationParam(name = JpaConstants.OPERATION_EXPUNGE_PARAM_EXPUNGE_EVERYTHING) BooleanType theExpungeEverything,
RequestDetails theRequestDetails
) {
org.hl7.fhir.r4.model.Parameters parameters = super.doExpunge(theLimit, theExpungeDeletedResources, theExpungeOldVersions, theExpungeEverything, theRequestDetails);
return org.hl7.fhir.convertors.conv40_50.Parameters.convertParameters(parameters);
}
// This is generated by hand:
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
@Operation(name = JpaConstants.OPERATION_GET_RESOURCE_COUNTS, idempotent = true, returnParameters = {
@OperationParam(name = "AllergyIntolerance", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Appointment", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "AppointmentResponse", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "AuditEvent", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Basic", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Binary", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "BodySite", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Bundle", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "CarePlan", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "CarePlan2", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Claim", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ClaimResponse", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ClinicalImpression", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Communication", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "CommunicationRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Composition", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ConceptMap", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Condition", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Conformance", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Contract", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Contraindication", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Coverage", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DataElement", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Device", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DeviceComponent", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DeviceMetric", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DeviceUseRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DeviceUseStatement", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DiagnosticOrder", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DiagnosticReport", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DocumentManifest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "DocumentReference", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "EligibilityRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "EligibilityResponse", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Encounter", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "EnrollmentRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "EnrollmentResponse", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "EpisodeOfCare", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ExplanationOfBenefit", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "FamilyMemberHistory", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Flag", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Goal", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Group", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "HealthcareService", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ImagingObjectSelection", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ImagingStudy", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Immunization", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ImmunizationRecommendation", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ListResource", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Location", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Media", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Medication", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "MedicationAdministration", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "MedicationDispense", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "MedicationPrescription", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "MedicationStatement", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "MessageHeader", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "NamingSystem", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "NutritionOrder", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Observation", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "OperationDefinition", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "OperationOutcome", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Order", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "OrderResponse", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Organization", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Parameters", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Patient", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "PaymentNotice", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "PaymentReconciliation", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Person", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Practitioner", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Procedure", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ProcedureRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ProcessRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ProcessResponse", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Provenance", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Questionnaire", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "QuestionnaireAnswers", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ReferralRequest", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "RelatedPerson", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "RiskAssessment", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Schedule", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "SearchParameter", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Slot", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Specimen", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "StructureDefinition", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Subscription", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Substance", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "Supply", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "ValueSet", type = IntegerType.class, min = 0, max = 1),
@OperationParam(name = "VisionPrescription", type = IntegerType.class, min = 0, max = 1)
})
@Description(shortDefinition = "Provides the number of resources currently stored on the server, broken down by resource type")
public Parameters getResourceCounts() {
Parameters retVal = new Parameters();
Map<String, Long> counts = mySystemDao.getResourceCountsFromCache();
counts = defaultIfNull(counts, Collections.emptyMap());
counts = new TreeMap<>(counts);
for (Entry<String, Long> nextEntry : counts.entrySet()) {
retVal.addParameter().setName((nextEntry.getKey())).setValue(new IntegerType(nextEntry.getValue().intValue()));
}
return retVal;
}
@Operation(name = JpaConstants.OPERATION_META, idempotent = true, returnParameters = {
@OperationParam(name = "return", type = Meta.class)
})
public Parameters meta(RequestDetails theRequestDetails) {
Parameters parameters = new Parameters();
parameters.addParameter().setName("return").setValue(getDao().metaGetOperation(theRequestDetails));
return parameters;
}
@Operation(name = JpaConstants.OPERATION_SUGGEST_KEYWORDS, idempotent = true)
public Parameters suggestKeywords(
@OperationParam(name = "context", min = 1, max = 1) String theContext,
@OperationParam(name = "searchParam", min = 1, max = 1) String theSearchParam,
@OperationParam(name = "text", min = 1, max = 1) String theText,
RequestDetails theRequest) {
ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3.validateFulltextSearchEnabled(mySearchDao);
if (isBlank(theContext)) {
throw new InvalidRequestException("Parameter 'context' must be provided");
}
if (isBlank(theSearchParam)) {
throw new InvalidRequestException("Parameter 'searchParam' must be provided");
}
if (isBlank(theText)) {
throw new InvalidRequestException("Parameter 'text' must be provided");
}
List<Suggestion> keywords = mySearchDao.suggestKeywords(theContext, theSearchParam, theText, theRequest);
Parameters retVal = new Parameters();
for (Suggestion next : keywords) {
//@formatter:off
retVal.addParameter()
.addPart(new ParametersParameterComponent().setName("keyword").setValue(new StringType(next.getTerm())))
.addPart(new ParametersParameterComponent().setName("score").setValue(new DecimalType(next.getScore())));
//@formatter:on
}
return retVal;
}
/**
* /$process-message
*/
@Operation(name = JpaConstants.OPERATION_PROCESS_MESSAGE, idempotent = false)
public IBaseBundle processMessage(
HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@OperationParam(name = "content", min = 1, max = 1)
@Description(formalDefinition = "The message to process (or, if using asynchronous messaging, it may be a response message to accept)")
Bundle theMessageToProcess
) {
startRequest(theServletRequest);
try {
return getDao().processMessage(theRequestDetails, theMessageToProcess);
} finally {
endRequest(theServletRequest);
}
}
@Transaction
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
try {
return getDao().transaction(theRequestDetails, theResources);
} finally {
endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
}
}
}

View File

@ -242,7 +242,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}

View File

@ -0,0 +1,334 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public class HapiTerminologySvcR5 extends BaseHapiTerminologySvcImpl implements IValidationSupport, IHapiTerminologySvcR5 {
@Autowired
@Qualifier("myValueSetDaoR5")
private IFhirResourceDao<ValueSet> myValueSetResourceDao;
@Autowired
@Qualifier("myConceptMapDaoR5")
private IFhirResourceDao<ConceptMap> myConceptMapResourceDao;
@Autowired
private IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemResourceDao;
@Autowired
private IValidationSupport myValidationSupport;
@Autowired
private IHapiTerminologySvc myTerminologySvc;
@Autowired
private PlatformTransactionManager myTransactionManager;
/**
* Constructor
*/
public HapiTerminologySvcR5() {
super();
}
private void addAllChildren(String theSystemString, ConceptDefinitionComponent theCode, List<VersionIndependentConcept> theListToPopulate) {
if (isNotBlank(theCode.getCode())) {
theListToPopulate.add(new VersionIndependentConcept(theSystemString, theCode.getCode()));
}
for (ConceptDefinitionComponent nextChild : theCode.getConcept()) {
addAllChildren(theSystemString, nextChild, theListToPopulate);
}
}
private boolean addTreeIfItContainsCode(String theSystemString, ConceptDefinitionComponent theNext, String theCode, List<VersionIndependentConcept> theListToPopulate) {
boolean foundCodeInChild = false;
for (ConceptDefinitionComponent nextChild : theNext.getConcept()) {
foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate);
}
if (theCode.equals(theNext.getCode()) || foundCodeInChild) {
theListToPopulate.add(new VersionIndependentConcept(theSystemString, theNext.getCode()));
return true;
}
return false;
}
@Override
protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) {
CodeSystem resourceToStore;
try {
resourceToStore = org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(theCodeSystemResource);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
validateCodeSystemForStorage(theCodeSystemResource);
if (isBlank(resourceToStore.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId();
} else {
return myCodeSystemResourceDao.update(resourceToStore).getId();
}
}
@Override
protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) {
ConceptMap resourceToStore;
try {
resourceToStore = org.hl7.fhir.convertors.conv40_50.ConceptMap.convertConceptMap(theConceptMap);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
if (isBlank(resourceToStore.getIdElement().getIdPart())) {
String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl());
myConceptMapResourceDao.update(resourceToStore, matchUrl);
} else {
myConceptMapResourceDao.update(resourceToStore);
}
}
@Override
protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) {
ValueSet valueSetDstu3;
try {
valueSetDstu3 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(theValueSet);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
if (isBlank(valueSetDstu3.getIdElement().getIdPart())) {
String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl());
myValueSetResourceDao.update(valueSetDstu3, matchUrl);
} else {
myValueSetResourceDao.update(valueSetDstu3);
}
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSet valueSetToExpand = new ValueSet();
valueSetToExpand.getCompose().addInclude(theInclude);
try {
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
valueSetToExpandR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetToExpand);
org.hl7.fhir.r4.model.ValueSet expandedValueSet = super.expandValueSet(valueSetToExpandR4);
return new ValueSetExpander.ValueSetExpansionOutcome(org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expandedValueSet));
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public IBaseResource expandValueSet(IBaseResource theInput) {
ValueSet valueSetToExpand = (ValueSet) theInput;
try {
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
valueSetToExpandR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetToExpand);
org.hl7.fhir.r4.model.ValueSet expandedR4 = super.expandValueSet(valueSetToExpandR4);
return org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(expandedR4);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public void expandValueSet(IBaseResource theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator) {
ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand;
try {
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
valueSetToExpandR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(valueSetToExpand);
super.expandValueSet(valueSetToExpandR4, theValueSetCodeAccumulator);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public List<VersionIndependentConcept> expandValueSet(String theValueSet) {
ValueSet vs = myValidationSupport.fetchResource(myContext, ValueSet.class, theValueSet);
if (vs == null) {
return Collections.emptyList();
}
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
try {
valueSetToExpandR4 = org.hl7.fhir.convertors.conv40_50.ValueSet.convertValueSet(vs);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
return expandValueSetAndReturnVersionIndependentConcepts(valueSetToExpandR4);
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
return null;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return Collections.emptyList();
}
@CoverageIgnore
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return null;
}
@Override
public IBaseResource fetchResource(FhirContext theContext, Class theClass, String theUri) {
return null;
}
@CoverageIgnore
@Override
public ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
return null;
}
@CoverageIgnore
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
return null;
}
private void findCodesAbove(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
for (ConceptDefinitionComponent next : conceptList) {
addTreeIfItContainsCode(theSystemString, next, theCode, theListToPopulate);
}
}
@Override
public List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
if (system != null) {
findCodesAbove(system, theSystem, theCode, retVal);
}
return retVal;
}
private void findCodesBelow(CodeSystem theSystem, String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate) {
List<ConceptDefinitionComponent> conceptList = theSystem.getConcept();
findCodesBelow(theSystemString, theCode, theListToPopulate, conceptList);
}
private void findCodesBelow(String theSystemString, String theCode, List<VersionIndependentConcept> theListToPopulate, List<ConceptDefinitionComponent> conceptList) {
for (ConceptDefinitionComponent next : conceptList) {
if (theCode.equals(next.getCode())) {
addAllChildren(theSystemString, next, theListToPopulate);
} else {
findCodesBelow(theSystemString, theCode, theListToPopulate, next.getConcept());
}
}
}
@Override
public List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<>();
CodeSystem system = myValidationSupport.fetchCodeSystem(myContext, theSystem);
if (system != null) {
findCodesBelow(system, theSystem, theCode, retVal);
}
return retVal;
}
@Override
protected org.hl7.fhir.r4.model.CodeSystem getCodeSystemFromContext(String theSystem) {
CodeSystem codeSystem = myValidationSupport.fetchCodeSystem(myContext, theSystem);
try {
return org.hl7.fhir.convertors.conv40_50.CodeSystem.convertCodeSystem(codeSystem);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return myTerminologySvc.supportsSystem(theSystem);
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}
@CoverageIgnore
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return txTemplate.execute(t->{
Optional<TermConcept> codeOpt = myTerminologySvc.findCode(theCodeSystem, theCode);
if (codeOpt.isPresent()) {
ConceptDefinitionComponent def = new ConceptDefinitionComponent();
TermConcept code = codeOpt.get();
def.setCode(code.getCode());
def.setDisplay(code.getDisplay());
CodeValidationResult retVal = new CodeValidationResult(def);
retVal.setProperties(code.toValidationProperties());
retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName());
return retVal;
}
return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode);
});
}
@Override
public LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return super.lookupCode(theContext, theSystem, theCode);
}
}

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.jpa.term;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
public interface IHapiTerminologySvcR5 extends IHapiTerminologySvc, IValidationSupport {
// nothing
}

View File

@ -69,7 +69,7 @@ public class JpaValidationSupportChainR4 extends ValidationSupportChain {
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
StructureDefinition retVal = super.fetchStructureDefinition(theCtx, theUrl);
if (retVal != null && !retVal.hasSnapshot()) {
retVal = generateSnapshot(retVal, theUrl, null);
retVal = generateSnapshot(retVal, theUrl, null, null);
}
return retVal;
}

View File

@ -0,0 +1,91 @@
package ca.uhn.fhir.jpa.validation;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR5;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r5.hapi.validation.SnapshotGeneratingValidationSupport;
import org.hl7.fhir.r5.hapi.validation.ValidationSupportChain;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class JpaValidationSupportChainR5 extends ValidationSupportChain {
private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
@Autowired
private FhirContext myFhirContext;
@Autowired
@Qualifier("myJpaValidationSupportR5")
public ca.uhn.fhir.jpa.dao.r5.IJpaValidationSupportR5 myJpaValidationSupportR5;
@Autowired
private IHapiTerminologySvcR5 myTerminologyService;
public JpaValidationSupportChainR5() {
super();
}
public void flush() {
myDefaultProfileValidationSupport.flush();
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
if (theClass.equals(StructureDefinition.class)) {
return (T) fetchStructureDefinition(theContext, theUri);
}
return super.fetchResource(theContext, theClass, theUri);
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
StructureDefinition retVal = super.fetchStructureDefinition(theCtx, theUrl);
if (retVal != null && !retVal.hasSnapshot()) {
retVal = generateSnapshot(retVal, theUrl, null, null);
}
return retVal;
}
@PostConstruct
public void postConstruct() {
addValidationSupport(myDefaultProfileValidationSupport);
addValidationSupport(myJpaValidationSupportR5);
addValidationSupport(myTerminologyService);
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext, this));
}
@PreDestroy
public void preDestroy() {
flush();
}
}

View File

@ -0,0 +1,176 @@
package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl;
import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.dialect.H2Dialect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Properties;
import static org.junit.Assert.fail;
@Configuration
@Import(TestJPAConfig.class)
@EnableTransactionManagement()
public class TestR5Config extends BaseJavaConfigR5 {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR5Config.class);
public static Integer ourMaxThreads;
static {
/*
* We use a randomized number of maximum threads in order to try
* and catch any potential deadlocks caused by database connection
* starvation
*/
if (ourMaxThreads == null) {
ourMaxThreads = (int) (Math.random() * 6.0) + 1;
}
}
@Autowired
private Environment myEnvironment;
private Exception myLastStackTrace;
@Bean
public CircularQueueCaptureQueriesListener captureQueriesListener() {
return new CircularQueueCaptureQueriesListener();
}
@Bean
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource() {
@Override
public Connection getConnection() {
ConnectionWrapper retVal;
try {
retVal = new ConnectionWrapper(super.getConnection());
} catch (Exception e) {
ourLog.error("Exceeded maximum wait for connection", e);
logGetConnectionStackTrace();
fail("Exceeded maximum wait for connection: " + e.toString());
retVal = null;
}
try {
throw new Exception();
} catch (Exception e) {
myLastStackTrace = e;
}
return retVal;
}
private void logGetConnectionStackTrace() {
StringBuilder b = new StringBuilder();
b.append("Last connection request stack trace:");
for (StackTraceElement next : myLastStackTrace.getStackTrace()) {
b.append("\n ");
b.append(next.getClassName());
b.append(".");
b.append(next.getMethodName());
b.append("(");
b.append(next.getFileName());
b.append(":");
b.append(next.getLineNumber());
b.append(")");
}
ourLog.info(b.toString());
}
};
retVal.setDriver(new org.h2.Driver());
retVal.setUrl("jdbc:h2:mem:testdb_r5");
retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
retVal.setPassword("");
retVal.setMaxTotal(ourMaxThreads);
DataSource dataSource = ProxyDataSourceBuilder
.create(retVal)
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
// .logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
// .countQuery(new ThreadQueryCountHolder())
.beforeQuery(new BlockLargeNumbersOfParamsListener())
.afterQuery(captureQueriesListener())
.afterQuery(new CurrentThreadCaptureQueriesListener())
.countQuery(singleQueryCountHolder())
.build();
return dataSource;
}
@Bean
public SingleQueryCountHolder singleQueryCountHolder() {
return new SingleQueryCountHolder();
}
@Override
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
retVal.setPersistenceUnitName("PU_HapiFhirJpaR5");
retVal.setDataSource(dataSource());
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.model_mapping", ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "local-heap");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
extraProperties.put("hibernate.search.autoregister_listeners", "true");
return extraProperties;
}
/**
* Bean which validates incoming requests
*/
@Bean
@Lazy
public RequestValidatingInterceptor requestValidatingInterceptor() {
RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor();
requestValidator.setFailOnSeverity(ResultSeverityEnum.ERROR);
requestValidator.setAddResponseHeaderOnSeverity(null);
requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
requestValidator.addValidatorModule(instanceValidatorR5());
return requestValidator;
}
@Bean
public IBinaryStorageSvc binaryStorage() {
return new MemoryBinaryStorageSvcImpl();
}
public static int getMaxThreads() {
return ourMaxThreads;
}
}

View File

@ -24,7 +24,7 @@ public class FhirResourceDaoDstu3StructureDefinitionTest extends BaseJpaDstu3Tes
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/dstu3/profile-differential-patient-dstu3.json");
assertEquals(0, sd.getSnapshot().getElement().size());
StructureDefinition output = myStructureDefinitionDao.generateSnapshot(sd, "http://foo", "THE BEST PROFILE");
StructureDefinition output = myStructureDefinitionDao.generateSnapshot(sd, "http://foo", null, "THE BEST PROFILE");
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(54, output.getSnapshot().getElement().size());

View File

@ -16,7 +16,7 @@ import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Ignore;
@ -84,14 +84,14 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
@After
public void after() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
val.setBestPracticeWarningLevel(org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel.Warning);
}
@Test
public void testValidateWithCanonicalReference() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
IResourceValidator.BestPracticeWarningLevel a = IResourceValidator.BestPracticeWarningLevel.Ignore;
org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel a = IResourceValidator.BestPracticeWarningLevel.Ignore;
val.setBestPracticeWarningLevel(a);
ValueSet vs = new ValueSet();

View File

@ -48,7 +48,7 @@ import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;

View File

@ -1,25 +1,12 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
@ -40,7 +27,7 @@ public class FhirResourceDaoR4StructureDefinitionTest extends BaseJpaR4Test {
StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-differential-patient-r4.json");
assertEquals(0, sd.getSnapshot().getElement().size());
StructureDefinition output = myStructureDefinitionDao.generateSnapshot(sd, "http://foo", "THE BEST PROFILE");
StructureDefinition output = myStructureDefinitionDao.generateSnapshot(sd, "http://foo", null, "THE BEST PROFILE");
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
assertEquals(51, output.getSnapshot().getElement().size());

View File

@ -18,7 +18,7 @@ import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Ignore;
@ -184,7 +184,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
@Test
public void testValidateWithCanonicalReference() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore);
val.setBestPracticeWarningLevel(org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel.Ignore);
ValueSet vs = new ValueSet();
vs.setId("MYVS");

View File

@ -0,0 +1,554 @@
package ca.uhn.fhir.jpa.dao.r5;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
import ca.uhn.fhir.jpa.config.TestR5Config;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.search.warm.ICacheWarmingSvc;
import ca.uhn.fhir.jpa.searchparam.registry.BaseSearchParamRegistry;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionRegistry;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR5;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR5;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.BasePagingProvider;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
import org.hl7.fhir.r5.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.util.AopTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestR5Config.class})
public abstract class BaseJpaR5Test extends BaseJpaTest {
private static JpaValidationSupportChainR5 ourJpaValidationSupportChainR5;
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
@Autowired
@Qualifier("myResourceCountsCache")
protected ResourceCountCache myResourceCountsCache;
@Autowired
protected IResourceLinkDao myResourceLinkDao;
@Autowired
protected ISearchParamPresentDao mySearchParamPresentDao;
@Autowired
protected IResourceIndexedSearchParamStringDao myResourceIndexedSearchParamStringDao;
@Autowired
protected IResourceIndexedSearchParamTokenDao myResourceIndexedSearchParamTokenDao;
@Autowired
protected IResourceIndexedSearchParamQuantityDao myResourceIndexedSearchParamQuantityDao;
@Autowired
protected IResourceIndexedSearchParamDateDao myResourceIndexedSearchParamDateDao;
@Autowired
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
@Qualifier("myAllergyIntoleranceDaoR5")
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
@Autowired
protected BinaryAccessProvider myBinaryAccessProvider;
@Autowired
protected BinaryStorageInterceptor myBinaryStorageInterceptor;
@Autowired
protected ApplicationContext myAppCtx;
@Autowired
@Qualifier("myAppointmentDaoR5")
protected IFhirResourceDao<Appointment> myAppointmentDao;
@Autowired
@Qualifier("myAuditEventDaoR5")
protected IFhirResourceDao<AuditEvent> myAuditEventDao;
@Autowired
@Qualifier("myBundleDaoR5")
protected IFhirResourceDao<Bundle> myBundleDao;
@Autowired
@Qualifier("myCommunicationDaoR5")
protected IFhirResourceDao<Communication> myCommunicationDao;
@Autowired
@Qualifier("myCommunicationRequestDaoR5")
protected IFhirResourceDao<CommunicationRequest> myCommunicationRequestDao;
@Autowired
@Qualifier("myCarePlanDaoR5")
protected IFhirResourceDao<CarePlan> myCarePlanDao;
@Autowired
@Qualifier("myCodeSystemDaoR5")
protected IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> myCodeSystemDao;
@Autowired
protected ITermCodeSystemDao myTermCodeSystemDao;
@Autowired
protected ITermConceptParentChildLinkDao myTermConceptParentChildLinkDao;
@Autowired
protected ITermCodeSystemVersionDao myTermCodeSystemVersionDao;
@Autowired
@Qualifier("myCompartmentDefinitionDaoR5")
protected IFhirResourceDao<CompartmentDefinition> myCompartmentDefinitionDao;
@Autowired
@Qualifier("myConceptMapDaoR5")
protected IFhirResourceDaoConceptMap<ConceptMap> myConceptMapDao;
@Autowired
protected ITermConceptDao myTermConceptDao;
@Autowired
protected ITermConceptDesignationDao myTermConceptDesignationDao;
@Autowired
@Qualifier("myConditionDaoR5")
protected IFhirResourceDao<Condition> myConditionDao;
@Autowired
protected DaoConfig myDaoConfig;
@Autowired
protected ModelConfig myModelConfig;
@Autowired
@Qualifier("myDeviceDaoR5")
protected IFhirResourceDao<Device> myDeviceDao;
@Autowired
@Qualifier("myDiagnosticReportDaoR5")
protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
@Autowired
@Qualifier("myEncounterDaoR5")
protected IFhirResourceDao<Encounter> myEncounterDao;
// @PersistenceContext()
@Autowired
protected EntityManager myEntityManager;
@Autowired
protected FhirContext myFhirCtx;
@Autowired
@Qualifier("myGroupDaoR5")
protected IFhirResourceDao<Group> myGroupDao;
@Autowired
@Qualifier("myMolecularSequenceDaoR5")
protected IFhirResourceDao<MolecularSequence> myMolecularSequenceDao;
@Autowired
@Qualifier("myImmunizationDaoR5")
protected IFhirResourceDao<Immunization> myImmunizationDao;
@Autowired
@Qualifier("myImmunizationRecommendationDaoR5")
protected IFhirResourceDao<ImmunizationRecommendation> myImmunizationRecommendationDao;
@Autowired
@Qualifier("myRiskAssessmentDaoR5")
protected IFhirResourceDao<RiskAssessment> myRiskAssessmentDao;
@Autowired
protected IInterceptorService myInterceptorRegistry;
@Autowired
@Qualifier("myLocationDaoR5")
protected IFhirResourceDao<Location> myLocationDao;
@Autowired
@Qualifier("myMediaDaoR5")
protected IFhirResourceDao<Media> myMediaDao;
@Autowired
@Qualifier("myMedicationAdministrationDaoR5")
protected IFhirResourceDao<MedicationAdministration> myMedicationAdministrationDao;
@Autowired
@Qualifier("myMedicationDaoR5")
protected IFhirResourceDao<Medication> myMedicationDao;
@Autowired
@Qualifier("myMedicationRequestDaoR5")
protected IFhirResourceDao<MedicationRequest> myMedicationRequestDao;
@Autowired
@Qualifier("myProcedureDaoR5")
protected IFhirResourceDao<Procedure> myProcedureDao;
@Autowired
@Qualifier("myNamingSystemDaoR5")
protected IFhirResourceDao<NamingSystem> myNamingSystemDao;
@Autowired
@Qualifier("myChargeItemDaoR5")
protected IFhirResourceDao<ChargeItem> myChargeItemDao;
@Autowired
@Qualifier("myObservationDaoR5")
protected IFhirResourceDao<Observation> myObservationDao;
@Autowired
@Qualifier("myOperationDefinitionDaoR5")
protected IFhirResourceDao<OperationDefinition> myOperationDefinitionDao;
@Autowired
@Qualifier("myOrganizationDaoR5")
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
protected DatabaseBackedPagingProvider myPagingProvider;
@Autowired
@Qualifier("myBinaryDaoR5")
protected IFhirResourceDao<Binary> myBinaryDao;
@Autowired
@Qualifier("myPatientDaoR5")
protected IFhirResourceDaoPatient<Patient> myPatientDao;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired
protected IForcedIdDao myForcedIdDao;
@Autowired
@Qualifier("myCoverageDaoR5")
protected IFhirResourceDao<Coverage> myCoverageDao;
@Autowired
@Qualifier("myPractitionerDaoR5")
protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired
@Qualifier("myServiceRequestDaoR5")
protected IFhirResourceDao<ServiceRequest> myServiceRequestDao;
@Autowired
@Qualifier("myQuestionnaireDaoR5")
protected IFhirResourceDao<Questionnaire> myQuestionnaireDao;
@Autowired
@Qualifier("myQuestionnaireResponseDaoR5")
protected IFhirResourceDao<QuestionnaireResponse> myQuestionnaireResponseDao;
@Autowired
@Qualifier("myResourceProvidersR5")
protected ResourceProviderFactory myResourceProviders;
@Autowired
protected IResourceTagDao myResourceTagDao;
@Autowired
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
@Autowired(required = false)
protected IFulltextSearchSvc mySearchDao;
@Autowired
protected ISearchDao mySearchEntityDao;
@Autowired
protected ISearchResultDao mySearchResultDao;
@Autowired
protected ISearchIncludeDao mySearchIncludeDao;
@Autowired
protected IResourceReindexJobDao myResourceReindexJobDao;
@Autowired
@Qualifier("mySearchParameterDaoR5")
protected IFhirResourceDao<SearchParameter> mySearchParameterDao;
@Autowired
protected BaseSearchParamRegistry mySearchParamRegistry;
@Autowired
protected IStaleSearchDeletingSvc myStaleSearchDeletingSvc;
@Autowired
@Qualifier("myStructureDefinitionDaoR5")
protected IFhirResourceDaoStructureDefinition<StructureDefinition> myStructureDefinitionDao;
@Autowired
@Qualifier("myConsentDaoR5")
protected IFhirResourceDao<Consent> myConsentDao;
@Autowired
@Qualifier("mySubscriptionDaoR5")
protected IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
@Autowired
@Qualifier("mySubstanceDaoR5")
protected IFhirResourceDao<Substance> mySubstanceDao;
@Autowired
@Qualifier("mySystemDaoR5")
protected IFhirSystemDao<Bundle, Meta> mySystemDao;
@Autowired
protected IResourceReindexingSvc myResourceReindexingSvc;
@Autowired
@Qualifier("mySystemProviderR5")
protected JpaSystemProviderR5 mySystemProvider;
@Autowired
protected ITagDefinitionDao myTagDefinitionDao;
@Autowired
@Qualifier("myTaskDaoR5")
protected IFhirResourceDao<Task> myTaskDao;
@Autowired
protected IHapiTerminologySvcR5 myTermSvc;
@Autowired
protected PlatformTransactionManager myTransactionMgr;
@Autowired
protected PlatformTransactionManager myTxManager;
@Autowired
@Qualifier("myJpaValidationSupportChainR5")
protected IValidationSupport myValidationSupport;
@Autowired
@Qualifier("myValueSetDaoR5")
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
@Autowired
protected ITermValueSetDao myTermValueSetDao;
@Autowired
protected ITermValueSetConceptDao myTermValueSetConceptDao;
@Autowired
protected ITermValueSetConceptDesignationDao myTermValueSetConceptDesignationDao;
@Autowired
protected ITermConceptMapDao myTermConceptMapDao;
@Autowired
protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao;
@Autowired
protected ICacheWarmingSvc myCacheWarmingSvc;
@Autowired
protected SubscriptionRegistry mySubscriptionRegistry;
protected IServerInterceptor myInterceptor;
@Autowired
private JpaValidationSupportChainR5 myJpaValidationSupportChainR5;
private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor;
private List<Object> mySystemInterceptors;
@Autowired
private DaoRegistry myDaoRegistry;
@After()
public void afterCleanupDao() {
myDaoConfig.setExpireSearchResults(new DaoConfig().isExpireSearchResults());
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
myDaoConfig.setExpireSearchResultsAfterMillis(new DaoConfig().getExpireSearchResultsAfterMillis());
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange());
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myPagingProvider.setDefaultPageSize(BasePagingProvider.DEFAULT_DEFAULT_PAGE_SIZE);
myPagingProvider.setMaximumPageSize(BasePagingProvider.DEFAULT_MAX_PAGE_SIZE);
}
@After
public void afterResetInterceptors() {
myInterceptorRegistry.unregisterAllInterceptors();
}
@After
public void afterClearTerminologyCaches() {
BaseHapiTerminologySvcImpl baseHapiTerminologySvc = AopTestUtils.getTargetObject(myTermSvc);
baseHapiTerminologySvc.clearTranslationCache();
baseHapiTerminologySvc.clearTranslationWithReverseCache();
baseHapiTerminologySvc.clearDeferred();
BaseHapiTerminologySvcImpl.clearOurLastResultsFromTranslationCache();
BaseHapiTerminologySvcImpl.clearOurLastResultsFromTranslationWithReverseCache();
}
@After()
public void afterGrabCaches() {
ourValueSetDao = myValueSetDao;
ourJpaValidationSupportChainR5 = myJpaValidationSupportChainR5;
}
@Before
public void beforeCreateInterceptor() {
mySystemInterceptors = myInterceptorRegistry.getAllRegisteredInterceptors();
myInterceptor = mock(IServerInterceptor.class);
myPerformanceTracingLoggingInterceptor = new PerformanceTracingLoggingInterceptor();
myInterceptorRegistry.registerInterceptor(myPerformanceTracingLoggingInterceptor);
}
@Before
public void beforeFlushFT() {
runInTransaction(() -> {
FullTextEntityManager ftem = Search.getFullTextEntityManager(myEntityManager);
ftem.purgeAll(ResourceTable.class);
ftem.purgeAll(ResourceIndexedSearchParamString.class);
ftem.flushToIndexes();
});
myDaoConfig.setSchedulingDisabled(true);
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
}
@Before
@Transactional()
public void beforePurgeDatabase() {
purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry);
}
@Before
public void beforeResetConfig() {
myDaoConfig.setHardSearchLimit(1000);
myDaoConfig.setHardTagListLimit(1000);
myDaoConfig.setIncludeLimit(2000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
}
@Override
protected FhirContext getContext() {
return myFhirCtx;
}
@Override
protected PlatformTransactionManager getTxManager() {
return myTxManager;
}
protected <T extends IBaseResource> T loadResourceFromClasspath(Class<T> type, String resourceName) throws IOException {
InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName);
if (stream == null) {
fail("Unable to load resource: " + resourceName);
}
String string = IOUtils.toString(stream, "UTF-8");
IParser newJsonParser = EncodingEnum.detectEncodingNoDefault(string).newParser(myFhirCtx);
return newJsonParser.parseResource(type, string);
}
protected void validate(IBaseResource theResource) {
FhirValidator validatorModule = myFhirCtx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myValidationSupport);
instanceValidator.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore);
validatorModule.registerValidatorModule(instanceValidator);
ValidationResult result = validatorModule.validateWithResult(theResource);
if (!result.isSuccessful()) {
fail(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
}
}
@SuppressWarnings("unchecked")
protected void upload(String theClasspath) throws IOException {
String resource = loadResource(theClasspath);
IParser parser = EncodingEnum.detectEncoding(resource).newParser(myFhirCtx);
IBaseResource resourceParsed = parser.parseResource(resource);
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceParsed.getIdElement().getResourceType());
dao.update(resourceParsed);
}
protected String loadResource(String theClasspath) throws IOException {
return IOUtils.toString(BaseJpaR5Test.class.getResourceAsStream(theClasspath), Charsets.UTF_8);
}
@AfterClass
public static void afterClassClearContextBaseJpaR5Test() {
ourValueSetDao.purgeCaches();
ourJpaValidationSupportChainR5.flush();
TestUtil.clearAllStaticFieldsForUnitTest();
}
/**
* Creates a single {@link ConceptMap} entity that includes:
* <br>
* <ul>
* <li>
* One group with two elements, each identifying one target apiece.
* </li>
* <li>
* One group with one element, identifying two targets.
* </li>
* <li>
* One group with one element, identifying a target that also appears
* in the first element of the first group.
* </li>
* </ul>
* </br>
* The first two groups identify the same source code system and different target code systems.
* </br>
* The first two groups also include an element with the same source code.
*
* @return A {@link ConceptMap} entity for testing.
*/
public static ConceptMap createConceptMap() {
ConceptMap conceptMap = new ConceptMap();
conceptMap.setUrl(CM_URL);
conceptMap.setSource(new UriType(VS_URL));
conceptMap.setTarget(new UriType(VS_URL_2));
ConceptMapGroupComponent group = conceptMap.addGroup();
group.setSource(CS_URL);
group.setSourceVersion("Version 1");
group.setTarget(CS_URL_2);
group.setTargetVersion("Version 2");
SourceElementComponent element = group.addElement();
element.setCode("12345");
element.setDisplay("Source Code 12345");
TargetElementComponent target = element.addTarget();
target.setCode("34567");
target.setDisplay("Target Code 34567");
target.setEquivalence(ConceptMapEquivalence.EQUAL);
element = group.addElement();
element.setCode("23456");
element.setDisplay("Source Code 23456");
target = element.addTarget();
target.setCode("45678");
target.setDisplay("Target Code 45678");
target.setEquivalence(ConceptMapEquivalence.WIDER);
// Add a duplicate
target = element.addTarget();
target.setCode("45678");
target.setDisplay("Target Code 45678");
target.setEquivalence(ConceptMapEquivalence.WIDER);
group = conceptMap.addGroup();
group.setSource(CS_URL);
group.setSourceVersion("Version 3");
group.setTarget(CS_URL_3);
group.setTargetVersion("Version 4");
element = group.addElement();
element.setCode("12345");
element.setDisplay("Source Code 12345");
target = element.addTarget();
target.setCode("56789");
target.setDisplay("Target Code 56789");
target.setEquivalence(ConceptMapEquivalence.EQUAL);
target = element.addTarget();
target.setCode("67890");
target.setDisplay("Target Code 67890");
target.setEquivalence(ConceptMapEquivalence.WIDER);
group = conceptMap.addGroup();
group.setSource(CS_URL_4);
group.setSourceVersion("Version 5");
group.setTarget(CS_URL_2);
group.setTargetVersion("Version 2");
element = group.addElement();
element.setCode("78901");
element.setDisplay("Source Code 78901");
target = element.addTarget();
target.setCode("34567");
target.setDisplay("Target Code 34567");
target.setEquivalence(ConceptMapEquivalence.NARROWER);
return conceptMap;
}
public static String toSearchUuidFromLinkNext(Bundle theBundle) {
String linkNext = theBundle.getLink("next").getUrl();
linkNext = linkNext.substring(linkNext.indexOf('?'));
Map<String, String[]> params = UrlUtil.parseQueryString(linkNext);
String[] uuidParams = params.get(Constants.PARAM_PAGINGACTION);
String uuid = uuidParams[0];
return uuid;
}
}

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryDstu3;
@ -61,9 +62,8 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
protected static SearchParamRegistryDstu3 ourSearchParamRegistry;
protected static DatabaseBackedPagingProvider ourPagingProvider;
protected static ISearchDao mySearchEntityDao;
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
protected static ISearchCoordinatorSvc ourSearchCoordinatorSvc;
private static Server ourServer;
private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider;
protected static SubscriptionTriggeringProvider ourSubscriptionTriggeringProvider;
public BaseResourceProviderDstu3Test() {
@ -91,8 +91,8 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class);
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
TerminologyUploaderProvider terminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
ourRestServer.registerProviders(mySystemProvider, terminologyUploaderProvider);
SubscriptionTriggeringProvider subscriptionTriggeringProvider = myAppCtx.getBean(SubscriptionTriggeringProvider.class);
ourRestServer.registerProvider(subscriptionTriggeringProvider);
@ -102,6 +102,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
ourSearchCoordinatorSvc = myAppCtx.getBean(ISearchCoordinatorSvc.class);
Server server = new Server(0);
@ -151,7 +152,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
myValidationSupport = wac.getBean(JpaValidationSupportChainDstu3.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
ourSearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
mySearchEntityDao = wac.getBean(ISearchDao.class);
ourSearchParamRegistry = wac.getBean(SearchParamRegistryDstu3.class);
ourSubscriptionTriggeringProvider = wac.getBean(SubscriptionTriggeringProvider.class);

View File

@ -162,12 +162,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myDaoConfig.setAllowMultipleDelete(true);
}
@Before
public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(ourSearchCoordinatorSvc);
}
private void checkParamMissing(String paramName) throws IOException {
@ -4043,7 +4040,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
assertEquals(412, response.getStatusLine().getStatusCode());
assertThat(resp, not(containsString("Resource has no id")));
assertThat(resp,
stringContainsInOrder(">ERROR<", "[Patient.contact]", "<pre>SHALL at least contain a contact's details or a reference to an organization", "<issue><severity value=\"error\"/>"));
stringContainsInOrder(">ERROR<", "[Patient.contact[0]]", "<pre>SHALL at least contain a contact's details or a reference to an organization", "<issue><severity value=\"error\"/>"));
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
response.close();

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
import ca.uhn.fhir.jpa.dao.DaoRegistry;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR4;
@ -20,6 +21,7 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.TestUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
@ -52,8 +54,6 @@ import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.junit.Assert.fail;
import ca.uhn.fhir.test.utilities.JettyUtil;
public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
protected static JpaValidationSupportChainR4 myValidationSupport;
@ -70,7 +70,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
protected static Server ourServer;
protected IGenericClient ourClient;
ResourceCountCache ourResourceCountsCache;
private TerminologyUploaderProviderR4 myTerminologyUploaderProvider;
private TerminologyUploaderProvider myTerminologyUploaderProvider;
private Object ourGraphQLProvider;
private boolean ourRestHookSubscriptionInterceptorRequested;
@ -104,7 +104,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class);
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);

View File

@ -5179,7 +5179,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(412, response.getStatusLine().getStatusCode());
assertThat(resp, not(containsString("Resource has no id")));
assertThat(resp,
stringContainsInOrder(">ERROR<", "[Patient.contact]", "<pre>SHALL at least contain a contact's details or a reference to an organization", "<issue><severity value=\"error\"/>"));
stringContainsInOrder(">ERROR<", "[Patient.contact[0]]", "<pre>SHALL at least contain a contact's details or a reference to an organization", "<issue><severity value=\"error\"/>"));
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
response.close();

View File

@ -0,0 +1,292 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
import ca.uhn.fhir.jpa.dao.DaoRegistry;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR5;
import ca.uhn.fhir.jpa.subscription.SubscriptionMatcherInterceptor;
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionLoader;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR5;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.parser.StrictErrorHandler;
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.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.TestUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.DispatcherServlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.junit.Assert.fail;
public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
protected static JpaValidationSupportChainR5 myValidationSupport;
protected static CloseableHttpClient ourHttpClient;
protected static int ourPort;
protected static RestfulServer ourRestServer;
protected static String ourServerBase;
protected static SearchParamRegistryR5 ourSearchParamRegistry;
private static DatabaseBackedPagingProvider ourPagingProvider;
protected static ISearchDao mySearchEntityDao;
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
private static GenericWebApplicationContext ourWebApplicationContext;
private static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor;
protected static Server ourServer;
protected IGenericClient ourClient;
ResourceCountCache ourResourceCountsCache;
private Object ourGraphQLProvider;
private boolean ourRestHookSubscriptionInterceptorRequested;
@Autowired
protected SubscriptionLoader mySubscriptionLoader;
@Autowired
protected DaoRegistry myDaoRegistry;
private TerminologyUploaderProvider myTerminologyUploaderProvider;
public BaseResourceProviderR5Test() {
super();
}
@After
public void after() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
ourRestServer.getInterceptorService().unregisterAllInterceptors();
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Before
public void before() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
if (ourServer == null) {
ourRestServer = new RestfulServer(myFhirCtx);
ourRestServer.registerProviders(myResourceProviders.createProviders());
ourRestServer.registerProvider(myBinaryAccessProvider);
ourRestServer.getInterceptorService().registerInterceptor(myBinaryStorageInterceptor);
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
// ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
// ourRestServer.registerProvider(ourGraphQLProvider);
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(ourRestServer, mySystemDao, myDaoConfig);
confProvider.setImplementationDescription("THIS IS THE DESC");
ourRestServer.setServerConformanceProvider(confProvider);
ourPagingProvider = myAppCtx.getBean(DatabaseBackedPagingProvider.class);
ourResourceCountsCache = (ResourceCountCache) myAppCtx.getBean("myResourceCountsCache");
Server server = new Server(0);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourWebApplicationContext = new GenericWebApplicationContext();
ourWebApplicationContext.setParent(myAppCtx);
ourWebApplicationContext.refresh();
proxyHandler.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ourWebApplicationContext);
DispatcherServlet dispatcherServlet = new DispatcherServlet();
// dispatcherServlet.setApplicationContext(webApplicationContext);
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
ServletHolder subsServletHolder = new ServletHolder();
subsServletHolder.setServlet(dispatcherServlet);
subsServletHolder.setInitParameter(
ContextLoader.CONFIG_LOCATION_PARAM,
WebsocketDispatcherConfig.class.getName());
proxyHandler.addServlet(subsServletHolder, "/*");
// Register a CORS filter
CorsConfiguration config = new CorsConfiguration();
CorsInterceptor corsInterceptor = new CorsInterceptor(config);
config.addAllowedHeader("x-fhir-starter");
config.addAllowedHeader("Origin");
config.addAllowedHeader("Accept");
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
config.addAllowedHeader("Access-Control-Request-Method");
config.addAllowedHeader("Access-Control-Request-Headers");
config.addAllowedOrigin("*");
config.addExposedHeader("Location");
config.addExposedHeader("Content-Location");
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
ourRestServer.registerInterceptor(corsInterceptor);
server.setHandler(proxyHandler);
JettyUtil.startServer(server);
ourPort = JettyUtil.getPortForStartedServer(server);
ourServerBase = "http://localhost:" + ourPort + "/fhir/context";
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
myValidationSupport = wac.getBean(JpaValidationSupportChainR5.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
mySearchEntityDao = wac.getBean(ISearchDao.class);
ourSearchParamRegistry = wac.getBean(SearchParamRegistryR5.class);
ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
ourSubscriptionMatcherInterceptor.start();
myFhirCtx.getRestfulClientFactory().setSocketTimeout(5000000);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
builder.setMaxConnPerRoute(99);
ourHttpClient = builder.build();
ourServer = server;
}
ourRestServer.setPagingProvider(ourPagingProvider);
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
if (shouldLogClient()) {
ourClient.registerInterceptor(new LoggingInterceptor());
}
}
protected boolean shouldLogClient() {
return true;
}
protected List<String> toNameList(Bundle resp) {
List<String> names = new ArrayList<>();
for (BundleEntryComponent next : resp.getEntry()) {
Patient nextPt = (Patient) next.getResource();
String nextStr = nextPt.getName().size() > 0 ? nextPt.getName().get(0).getGivenAsSingleString() + " " + nextPt.getName().get(0).getFamily() : "";
if (isNotBlank(nextStr)) {
names.add(nextStr);
}
}
return names;
}
protected void waitForActivatedSubscriptionCount(int theSize) throws Exception {
for (int i = 0; ; i++) {
if (i == 10) {
fail("Failed to init subscriptions");
}
try {
mySubscriptionLoader.doSyncSubscriptionsForUnitTest();
break;
} catch (ResourceVersionConflictException e) {
Thread.sleep(250);
}
}
TestUtil.waitForSize(theSize, () -> mySubscriptionRegistry.size());
Thread.sleep(500);
}
@AfterClass
public static void afterClassClearContextBaseResourceProviderR5Test() throws Exception {
JettyUtil.closeServer(ourServer);
ourHttpClient.close();
ourServer = null;
ourHttpClient = null;
myValidationSupport.flush();
myValidationSupport = null;
ourWebApplicationContext.close();
ourWebApplicationContext = null;
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static int getNumberOfParametersByName(Parameters theParameters, String theName) {
int retVal = 0;
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
retVal++;
}
}
return retVal;
}
public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return param;
}
}
return new ParametersParameterComponent();
}
public static List<ParametersParameterComponent> getParametersByName(Parameters theParameters, String theName) {
List<ParametersParameterComponent> params = new ArrayList<>();
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
params.add(param);
}
}
return params;
}
public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) {
for (ParametersParameterComponent part : theParameter.getPart()) {
if (part.getName().equals(theName)) {
return part;
}
}
return new ParametersParameterComponent();
}
public static boolean hasParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,93 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
@SuppressWarnings("Duplicates")
public class ResourceProviderR5Test extends BaseResourceProviderR5Test {
private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
ourClient.unregisterInterceptor(myCapturingInterceptor);
}
@Override
public void before() throws Exception {
super.before();
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myDaoConfig.setAllowMultipleDelete(true);
ourClient.registerInterceptor(myCapturingInterceptor);
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
}
@Test
public void testSearchWithContainsLowerCase() {
myDaoConfig.setAllowContainsSearches(true);
Patient pt1 = new Patient();
pt1.addName().setFamily("Elizabeth");
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue();
Patient pt2 = new Patient();
pt2.addName().setFamily("fghijk");
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue();
Patient pt3 = new Patient();
pt3.addName().setFamily("zzzzz");
myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue();
Bundle output = ourClient
.search()
.forResource("Patient")
.where(Patient.NAME.contains().value("ZAB"))
.returnBundle(Bundle.class)
.execute();
List<String> ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
assertThat(ids, containsInAnyOrder(pt1id));
output = ourClient
.search()
.forResource("Patient")
.where(Patient.NAME.contains().value("zab"))
.returnBundle(Bundle.class)
.execute();
ids = output.getEntry().stream().map(t -> t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.toList());
assertThat(ids, containsInAnyOrder(pt1id));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -9,7 +9,6 @@ import org.hl7.fhir.dstu3.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.test.util.AopTestUtils;
import ca.uhn.fhir.jpa.dao.DaoConfig;
@ -41,7 +40,7 @@ public class PagingMultinodeProviderDstu3Test extends BaseResourceProviderDstu3T
myDaoConfig.setAllowMultipleDelete(true);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(ourSearchCoordinatorSvc);
}
@Test

View File

@ -107,7 +107,7 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
.setType(TriggerDefinition.TriggerType.DATAADDED)
.setCondition(new Expression()
.setDescription("Encounter Location = emergency (active/completed encounters, current or previous)")
.setLanguage(Expression.ExpressionLanguage.TEXT_FHIRPATH)
.setLanguage(Expression.ExpressionLanguage.TEXT_FHIRPATH.toCode())
.setExpression("(this | %previous).location.where(location = 'Location/emergency' and status in {'active', 'completed'}).exists()")
)
);

View File

@ -62,6 +62,11 @@
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r5</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
@ -82,6 +87,11 @@
<artifactId>hapi-fhir-validation-resources-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-r5</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>

View File

@ -0,0 +1,886 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
/*
* #%L
* HAPI FHIR Search Parameters
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.entity.*;
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.CapabilityStatement.CapabilityStatementRestSecurityComponent;
import org.hl7.fhir.r5.model.Location.LocationPositionComponent;
import org.hl7.fhir.r5.model.Patient.PatientCommunicationComponent;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.springframework.beans.factory.annotation.Autowired;
import javax.measure.unit.NonSI;
import javax.measure.unit.Unit;
import java.math.BigDecimal;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements ISearchParamExtractor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamExtractorR5.class);
private static final Set<Class<?>> ourIgnoredForSearchDatatypes;
static {
//noinspection unchecked
ourIgnoredForSearchDatatypes = Collections.unmodifiableSet(Sets.newHashSet(
Age.class,
Annotation.class,
Attachment.class,
Count.class,
Distance.class,
Ratio.class,
SampledData.class,
Signature.class,
LocationPositionComponent.class
));
}
@Autowired
private IValidationSupport myValidationSupport;
/**
* Constructor
*/
public SearchParamExtractorR5() {
super();
}
// This constructor is used by tests
@VisibleForTesting
public SearchParamExtractorR5(ModelConfig theModelConfig, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
super(theCtx, theSearchParamRegistry);
myValidationSupport = theValidationSupport;
}
private void addQuantity(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Quantity nextValue) {
if (!nextValue.getValueElement().isEmpty()) {
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
String nextValueString = nextValue.getSystemElement().getValueAsString();
String nextValueCode = nextValue.getCode();
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
private void addMoney(ResourceTable theEntity, HashSet<ResourceIndexedSearchParamQuantity> retVal, String resourceName, Money nextValue) {
if (!nextValue.getValueElement().isEmpty()) {
BigDecimal nextValueValue = nextValue.getValueElement().getValue();
String nextValueString = "urn:iso:std:iso:4217";
String nextValueCode = nextValue.getCurrency();
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValueValue, nextValueString, nextValueCode);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
if (isBlank(searchTerm)) {
return;
}
if (searchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), resourceName, StringNormalizer.normalizeString(searchTerm), searchTerm);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getModelConfig(), nextSpDef.getName(), StringNormalizer.normalizeString(value), value);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
@Override
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
ArrayList<PathAndRef> retVal = new ArrayList<>();
String[] nextPathsSplit = SPLIT_R4.split(theNextSpDef.getPath());
for (String path : nextPathsSplit) {
path = path.trim();
if (isNotBlank(path)) {
for (Object next : extractValues(path, theResource)) {
retVal.add(new PathAndRef(path, next));
}
}
}
return retVal;
}
@Override
public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
// TODO: implement
return Collections.emptySet();
}
/*
* (non-Javadoc)
*
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
*/
@Override
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.DATE) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null) {
continue;
}
ResourceIndexedSearchParamDate nextEntity;
if (nextObject instanceof BaseDateTimeType) {
BaseDateTimeType nextValue = (BaseDateTimeType) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
} else if (nextObject instanceof Period) {
Period nextValue = (Period) nextObject;
if (nextValue.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
} else if (nextObject instanceof Timing) {
Timing nextValue = (Timing) nextObject;
if (nextValue.isEmpty()) {
continue;
}
TreeSet<Date> dates = new TreeSet<>();
String firstValue = null;
for (DateTimeType nextEvent : nextValue.getEvent()) {
if (nextEvent.getValue() != null) {
dates.add(nextEvent.getValue());
if (firstValue == null) {
firstValue = nextEvent.getValueAsString();
}
}
}
if (nextValue.getRepeat().hasBounds()) {
if (nextValue.getRepeat().getBoundsPeriod().getStart() != null) {
dates.add(nextValue.getRepeat().getBoundsPeriod().getStart());
}
if (nextValue.getRepeat().getBoundsPeriod().getEnd() != null) {
dates.add(nextValue.getRepeat().getBoundsPeriod().getEnd());
}
}
if (dates.isEmpty()) {
continue;
}
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last(), firstValue);
} else if (nextObject instanceof StringType) {
// CarePlan.activitydate can be a string
continue;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
}
return retVal;
}
/*
* (non-Javadoc)
*
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
*/
@Override
public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.NUMBER) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof Duration) {
Duration nextValue = (Duration) nextObject;
if (nextValue.getValueElement().isEmpty()) {
continue;
}
if (SearchParamConstants.UCUM_NS.equals(nextValue.getSystem())) {
if (isNotBlank(nextValue.getCode())) {
Unit<? extends javax.measure.quantity.Quantity> unit = Unit.valueOf(nextValue.getCode());
javax.measure.converter.UnitConverter dayConverter = unit.getConverterTo(NonSI.DAY);
double dayValue = dayConverter.convert(nextValue.getValue().doubleValue());
Duration newValue = new Duration();
newValue.setSystem(SearchParamConstants.UCUM_NS);
newValue.setCode(NonSI.DAY.toString());
newValue.setValue(dayValue);
nextValue = newValue;
/*
* @SuppressWarnings("unchecked") PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>>)
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if (unit.isCompatible(UCUM.DAY)) {
*
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY);
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); Duration newValue = new Duration(); newValue.setSystem(UCUM_NS);
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue; }
*/
}
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else if (nextObject instanceof Quantity) {
Quantity nextValue = (Quantity) nextObject;
if (nextValue.getValueElement().isEmpty()) {
continue;
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else if (nextObject instanceof IntegerType) {
IntegerType nextValue = (IntegerType) nextObject;
if (nextValue.getValue() == null) {
continue;
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, new BigDecimal(nextValue.getValue()));
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else if (nextObject instanceof DecimalType) {
DecimalType nextValue = (DecimalType) nextObject;
if (nextValue.getValue() == null) {
continue;
}
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
/*
* (non-Javadoc)
*
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
*/
@Override
public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.QUANTITY) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof Quantity) {
Quantity nextValue = (Quantity) nextObject;
addQuantity(theEntity, retVal, resourceName, nextValue);
} else if (nextObject instanceof Money) {
Money nextValue = (Money) nextObject;
addMoney(theEntity, retVal, resourceName, nextValue);
} else if (nextObject instanceof Range) {
Range nextValue = (Range) nextObject;
addQuantity(theEntity, retVal, resourceName, nextValue.getLow());
addQuantity(theEntity, retVal, resourceName, nextValue.getHigh());
} else if (ourIgnoredForSearchDatatypes.contains(nextObject.getClass())) {
continue;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
/*
* (non-Javadoc)
*
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
*/
@Override
public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<>();
String resourceName = getContext().getResourceDefinition(theResource).getName();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.STRING) {
continue;
}
String nextPath = nextSpDef.getPath();
String nextSpName = nextSpDef.getName();
if (isBlank(nextPath)) {
// // TODO: implement phonetic, and any others that have no path
//
// // TODO: do we still need this check?
// if ("Questionnaire".equals(nextSpName) && nextSpDef.getName().equals("title")) {
// Questionnaire q = (Questionnaire) theResource;
// String title = "";// q.getGroup().getTitle();
// addSearchTerm(theEntity, retVal, nextSpName, title);
// }
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof IPrimitiveType<?>) {
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
String searchTerm = nextValue.getValueAsString();
addSearchTerm(theEntity, retVal, nextSpName, searchTerm);
} else {
if (nextObject instanceof HumanName) {
ArrayList<StringType> allNames = new ArrayList<>();
HumanName nextHumanName = (HumanName) nextObject;
if (isNotBlank(nextHumanName.getFamily())) {
allNames.add(nextHumanName.getFamilyElement());
}
allNames.addAll(nextHumanName.getGiven());
for (StringType nextName : allNames) {
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
}
} else if (nextObject instanceof Address) {
ArrayList<StringType> allNames = new ArrayList<>();
Address nextAddress = (Address) nextObject;
allNames.addAll(nextAddress.getLine());
allNames.add(nextAddress.getCityElement());
allNames.add(nextAddress.getStateElement());
allNames.add(nextAddress.getCountryElement());
allNames.add(nextAddress.getPostalCodeElement());
for (StringType nextName : allNames) {
addSearchTerm(theEntity, retVal, nextSpName, nextName.getValue());
}
} else if (nextObject instanceof ContactPoint) {
ContactPoint nextContact = (ContactPoint) nextObject;
if (nextContact.getValueElement().isEmpty() == false) {
addSearchTerm(theEntity, retVal, nextSpName, nextContact.getValue());
}
} else if (nextObject instanceof Quantity) {
BigDecimal value = ((Quantity) nextObject).getValue();
if (value != null) {
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
}
} else if (nextObject instanceof Range) {
Quantity low = ((Range) nextObject).getLow();
if (low != null) {
BigDecimal value = low.getValue();
if (value != null) {
addSearchTerm(theEntity, retVal, nextSpName, value.toPlainString());
}
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpName + " is of unexpected datatype: " + nextObject.getClass());
}
}
}
}
}
return retVal;
}
/*
* (non-Javadoc)
*
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IBaseResource)
*/
@Override
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<>();
String useSystem = null;
if (theResource instanceof CodeSystem) {
CodeSystem cs = (CodeSystem) theResource;
useSystem = cs.getUrl();
}
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
continue;
}
String resourceType = theEntity.getResourceType();
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
List<String> systems = new ArrayList<>();
List<String> codes = new ArrayList<>();
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null) {
continue;
}
// Patient:language
if (nextObject instanceof PatientCommunicationComponent) {
PatientCommunicationComponent nextValue = (PatientCommunicationComponent) nextObject;
nextObject = nextValue.getLanguage();
}
if (nextObject instanceof Identifier) {
Identifier nextValue = (Identifier) nextObject;
if (nextValue.isEmpty()) {
continue;
}
String system = StringUtils.defaultIfBlank(nextValue.getSystemElement().getValueAsString(), null);
String value = nextValue.getValueElement().getValue();
if (isNotBlank(value)) {
systems.add(system);
codes.add(value);
}
if (isNotBlank(nextValue.getType().getText())) {
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
}
} else if (nextObject instanceof ContactPoint) {
ContactPoint nextValue = (ContactPoint) nextObject;
if (nextValue.isEmpty()) {
continue;
}
systems.add(nextValue.getSystemElement().getValueAsString());
codes.add(nextValue.getValueElement().getValue());
} else if (nextObject instanceof Enumeration<?>) {
Enumeration<?> obj = (Enumeration<?>) nextObject;
String system = extractSystem(obj);
String code = obj.getValueAsString();
if (isNotBlank(code)) {
systems.add(system);
codes.add(code);
}
} else if (nextObject instanceof IPrimitiveType<?>) {
IPrimitiveType<?> nextValue = (IPrimitiveType<?>) nextObject;
if (nextValue.isEmpty()) {
continue;
}
if ("CodeSystem.concept.code".equals(nextPath)) {
systems.add(useSystem);
} else {
systems.add(null);
}
codes.add(nextValue.getValueAsString());
} else if (nextObject instanceof Coding) {
Coding nextValue = (Coding) nextObject;
extractTokensFromCoding(systems, codes, theEntity, retVal, nextSpDef, nextValue);
} else if (nextObject instanceof CodeableConcept) {
CodeableConcept nextCC = (CodeableConcept) nextObject;
if (!nextCC.getTextElement().isEmpty()) {
addStringParam(theEntity, retVal, nextSpDef, nextCC.getTextElement().getValue());
}
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
} else if (nextObject instanceof CapabilityStatementRestSecurityComponent) {
// Conformance.security search param points to something kind of useless right now - This should probably
// be fixed.
CapabilityStatementRestSecurityComponent sec = (CapabilityStatementRestSecurityComponent) nextObject;
for (CodeableConcept nextCC : sec.getService()) {
extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
}
} else if (nextObject instanceof LocationPositionComponent) {
ourLog.warn("Position search not currently supported, not indexing location");
continue;
} else if (nextObject instanceof StructureDefinition.StructureDefinitionContextComponent) {
ourLog.warn("StructureDefinition context indexing not currently supported"); // TODO: implement this
continue;
} else if (resourceType.equals("Consent") && nextPath.equals("Consent.source")) {
// Consent#source-identifier has a path that isn't typed - This is a one-off to deal with that
continue;
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " with path " + nextPath + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
assert systems.size() == codes.size() : "Systems contains " + systems + ", codes contains: " + codes;
Set<Pair<String, String>> haveValues = new HashSet<>();
for (int i = 0; i < systems.size(); i++) {
String system = systems.get(i);
String code = codes.get(i);
if (isBlank(system) && isBlank(code)) {
continue;
}
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
system = system.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
}
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
code = code.substring(0, ResourceIndexedSearchParamToken.MAX_LENGTH);
}
Pair<String, String> nextPair = Pair.of(system, code);
if (haveValues.contains(nextPair)) {
continue;
}
haveValues.add(nextPair);
ResourceIndexedSearchParamToken nextEntity;
nextEntity = new ResourceIndexedSearchParamToken(nextSpDef.getName(), system, code);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
}
return retVal;
}
@Override
public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IBaseResource theResource) {
HashSet<ResourceIndexedSearchParamUri> retVal = new HashSet<>();
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
for (RuntimeSearchParam nextSpDef : searchParams) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.URI) {
continue;
}
String nextPath = nextSpDef.getPath();
if (isBlank(nextPath)) {
continue;
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject == null || ((IBase) nextObject).isEmpty()) {
continue;
}
String resourceName = nextSpDef.getName();
boolean multiType = false;
if (nextPath.endsWith("[x]")) {
multiType = true;
}
if (nextObject instanceof UriType) {
UriType nextValue = (UriType) nextObject;
if (isBlank(nextValue.getValue())) {
continue;
}
ourLog.trace("Adding param: {}, {}", resourceName, nextValue.getValue());
ResourceIndexedSearchParamUri nextEntity = new ResourceIndexedSearchParamUri(resourceName, nextValue.getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
}
}
return retVal;
}
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConcept theCodeableConcept, ResourceTable theEntity,
Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
for (Coding nextCoding : theCodeableConcept.getCoding()) {
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
}
}
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate,
RuntimeSearchParam theParameterDef, Coding nextCoding) {
if (nextCoding != null && !nextCoding.isEmpty()) {
String nextSystem = nextCoding.getSystemElement().getValueAsString();
String nextCode = nextCoding.getCodeElement().getValue();
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
theSystems.add(nextSystem);
theCodes.add(nextCode);
}
if (!nextCoding.getDisplayElement().isEmpty()) {
addStringParam(theEntity, theListToPopulate, theParameterDef, nextCoding.getDisplayElement().getValue());
}
}
}
/**
* Override parent because we're using FHIRPath here
*/
@Override
protected List<Object> extractValues(String thePaths, IBaseResource theResource) {
IWorkerContext worker = new org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker);
fp.setHostServices(new SearchParamExtractorR5HostServices());
List<Object> values = new ArrayList<>();
String[] nextPathsSplit = SPLIT_R4.split(thePaths);
for (String nextPath : nextPathsSplit) {
List<Base> allValues;
try {
allValues = fp.evaluate((Base) theResource, nextPath);
} catch (FHIRException e) {
String msg = getContext().getLocalizer().getMessage(BaseSearchParamExtractor.class, "failedToExtractPaths", nextPath, e.toString());
throw new InternalErrorException(msg, e);
}
if (allValues.isEmpty() == false) {
values.addAll(allValues);
}
}
for (int i = 0; i < values.size(); i++) {
Object nextObject = values.get(i);
if (nextObject instanceof Extension) {
Extension nextExtension = (Extension) nextObject;
nextObject = nextExtension.getValue();
values.set(i, nextObject);
}
}
return values;
}
@VisibleForTesting
void setValidationSupportForTesting(IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
}
private class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext {
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
@Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {
return null;
}
@Override
public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException {
return null;
}
@Override
public boolean log(String argument, List<Base> focus) {
return false;
}
@Override
public FunctionDetails resolveFunction(String functionName) {
return null;
}
@Override
public TypeDetails checkFunction(Object appContext, String functionName, List<TypeDetails> parameters) throws PathEngineException {
return null;
}
@Override
public List<Base> executeFunction(Object appContext, String functionName, List<List<Base>> parameters) {
return null;
}
@Override
public Base resolveReference(Object theAppContext, String theUrl) throws FHIRException {
/*
* When we're doing resolution within the SearchParamExtractor, if we want
* to do a resolve() it's just to check the type, so there is no point
* going through the heavyweight test. We can just return a stub and
* that's good enough since we're just doing something like
* Encounter.patient.where(resolve() is Patient)
*/
IdType url = new IdType(theUrl);
Base retVal = null;
if (isNotBlank(url.getResourceType())) {
retVal = myResourceTypeToStub.get(url.getResourceType());
if (retVal != null) {
return retVal;
}
ResourceType resourceType = ResourceType.fromCode(url.getResourceType());
if (resourceType != null) {
retVal = new Resource() {
@Override
public Resource copy() {
return this;
}
@Override
public ResourceType getResourceType() {
return resourceType;
}
@Override
public String fhirType() {
return url.getResourceType();
}
};
myResourceTypeToStub.put(url.getResourceType(), retVal);
}
}
return retVal;
}
@Override
public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException {
return false;
}
@Override
public ValueSet resolveValueSet(Object theO, String theS) {
return null;
}
}
private static <T extends Enum<?>> String extractSystem(Enumeration<T> theBoundCode) {
if (theBoundCode.getValue() != null) {
return theBoundCode.getEnumFactory().toSystem(theBoundCode.getValue());
}
return null;
}
}

View File

@ -0,0 +1,130 @@
package ca.uhn.fhir.jpa.searchparam.registry;
/*
* #%L
* HAPI FHIR Search Parameters
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.DatatypeUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.SearchParameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class SearchParamRegistryR5 extends BaseSearchParamRegistry<SearchParameter> {
@Override
protected RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case SPECIAL:
paramType = RestSearchParameterTypeEnum.SPECIAL;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
}
}
Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null;
}
}
IIdType id = theNextSp.getIdElement();
String uri = "";
boolean unique = false;
List<Extension> uniqueExts = theNextSp.getExtensionsByUrl(SearchParamConstants.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
unique = true;
}
}
}
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new Reference(next.getDefinition())));
}
return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
}
}

View File

@ -9,22 +9,29 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
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.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.util.ResourceProviderFactory;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
import ca.uhn.fhir.jpa.util.ResourceProviderFactory;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.*;
import ca.uhn.fhir.rest.server.ETagSupportEnum;
import ca.uhn.fhir.rest.server.ElementsSupportEnum;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.BanUnsupportedHttpMethodsInterceptor;
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhirtest.config.*;
import ca.uhn.fhirtest.config.TestDstu2Config;
import ca.uhn.fhirtest.config.TestDstu3Config;
import ca.uhn.fhirtest.config.TestR4Config;
import ca.uhn.fhirtest.config.TestR5Config;
import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
@ -40,6 +47,7 @@ import java.util.List;
public class TestRestfulServer extends RestfulServer {
public static final String FHIR_BASEURL_R5 = "fhir.baseurl.r5";
public static final String FHIR_BASEURL_R4 = "fhir.baseurl.r4";
public static final String FHIR_BASEURL_DSTU2 = "fhir.baseurl.dstu2";
public static final String FHIR_BASEURL_DSTU3 = "fhir.baseurl.dstu3";
@ -118,7 +126,7 @@ public class TestRestfulServer extends RestfulServer {
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class));
confProvider.setImplementationDescription(implDesc);
setServerConformanceProvider(confProvider);
providers.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class));
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
break;
}
case "R4": {
@ -136,10 +144,29 @@ public class TestRestfulServer extends RestfulServer {
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, myAppCtx.getBean(DaoConfig.class));
confProvider.setImplementationDescription(implDesc);
setServerConformanceProvider(confProvider);
providers.add(myAppCtx.getBean(TerminologyUploaderProviderR4.class));
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
providers.add(myAppCtx.getBean(GraphQLProvider.class));
break;
}
case "R5": {
myAppCtx = new AnnotationConfigWebApplicationContext();
myAppCtx.setServletConfig(getServletConfig());
myAppCtx.setParent(parentAppCtx);
myAppCtx.register(TestR5Config.class, WebsocketDispatcherConfig.class);
baseUrlProperty = FHIR_BASEURL_R5;
myAppCtx.refresh();
setFhirContext(FhirContext.forR5());
beans = myAppCtx.getBean("myResourceProvidersR5", ResourceProviderFactory.class);
providers.add(myAppCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class));
systemDao = myAppCtx.getBean("mySystemDaoR5", IFhirSystemDao.class);
etagSupport = ETagSupportEnum.ENABLED;
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, myAppCtx.getBean(DaoConfig.class));
confProvider.setImplementationDescription(implDesc);
setServerConformanceProvider(confProvider);
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
// providers.add(myAppCtx.getBean(GraphQLProvider.class));
break;
}
default:
throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam);
}

View File

@ -42,6 +42,11 @@ public class FhirTesterConfig {
public TesterConfig testerConfig() {
TesterConfig retVal = new TesterConfig();
retVal
.addServer()
.withId("home_r4")
.withFhirVersion(FhirVersionEnum.R4)
.withBaseUrl("http://hapi.fhir.org/baseR4")
.withName("UHN/HAPI Server (R4 FHIR)")
.addServer()
.withId("home_21")
.withFhirVersion(FhirVersionEnum.DSTU3)
@ -53,10 +58,10 @@ public class FhirTesterConfig {
.withBaseUrl("http://hapi.fhir.org/baseDstu2")
.withName("UHN/HAPI Server (DSTU2 FHIR)")
.addServer()
.withId("home_r4")
.withFhirVersion(FhirVersionEnum.R4)
.withBaseUrl("http://hapi.fhir.org/baseR4")
.withName("UHN/HAPI Server (R4 FHIR)")
.withId("home_r5")
.withFhirVersion(FhirVersionEnum.R5)
.withBaseUrl("http://hapi.fhir.org/baseR5")
.withName("UHN/HAPI Server (R5 FHIR)")
// .addServer()
// .withId("tdl_d2")
// .withFhirVersion(FhirVersionEnum.DSTU2)

View File

@ -0,0 +1,170 @@
package ca.uhn.fhirtest.config;
import ca.uhn.fhir.jpa.config.BaseJavaConfigR5;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.dialect.PostgreSQL94Dialect;
import org.hl7.fhir.dstu2.model.Subscription;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
@Import(CommonConfig.class)
@EnableTransactionManagement()
public class TestR5Config extends BaseJavaConfigR5 {
public static final String FHIR_DB_USERNAME = "${fhir.db.username}";
public static final String FHIR_DB_PASSWORD = "${fhir.db.password}";
public static final String FHIR_LUCENE_LOCATION_R5 = "${fhir.lucene.location.r5}";
public static final Integer COUNT_SEARCH_RESULTS_UP_TO = 50000;
@Value(TestR5Config.FHIR_DB_USERNAME)
private String myDbUsername;
@Value(TestR5Config.FHIR_DB_PASSWORD)
private String myDbPassword;
@Value(FHIR_LUCENE_LOCATION_R5)
private String myFhirLuceneLocation;
@Bean
public DaoConfig daoConfig() {
DaoConfig retVal = new DaoConfig();
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.EMAIL);
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.RESTHOOK);
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.WEBSOCKET);
retVal.setWebsocketContextPath("/websocketR5");
retVal.setAllowContainsSearches(true);
retVal.setAllowMultipleDelete(true);
retVal.setAllowInlineMatchUrlReferences(true);
retVal.setAllowExternalReferences(true);
retVal.getTreatBaseUrlsAsLocal().add("http://hapi.fhir.org/baseR5");
retVal.getTreatBaseUrlsAsLocal().add("https://hapi.fhir.org/baseR5");
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseR5");
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseR5");
retVal.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
retVal.setCountSearchResultsUpTo(TestR5Config.COUNT_SEARCH_RESULTS_UP_TO);
retVal.setFetchSizeDefaultMaximum(10000);
retVal.setExpungeEnabled(true);
return retVal;
}
@Bean
public ModelConfig modelConfig() {
return daoConfig().getModelConfig();
}
@Bean(name = "myPersistenceDataSourceR5", destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource();
if (CommonConfig.isLocalTestMode()) {
retVal.setUrl("jdbc:derby:memory:fhirtest_r5;create=true");
} else {
retVal.setDriver(new org.postgresql.Driver());
retVal.setUrl("jdbc:postgresql://localhost/fhirtest_r5");
}
retVal.setUsername(myDbUsername);
retVal.setPassword(myDbPassword);
retVal.setDefaultQueryTimeout(20);
return retVal;
}
@Override
@Bean(autowire = Autowire.BY_TYPE)
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
DatabaseBackedPagingProvider retVal = super.databaseBackedPagingProvider();
retVal.setDefaultPageSize(20);
retVal.setMaximumPageSize(500);
return retVal;
}
@Override
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
retVal.setPersistenceUnitName("PU_HapiFhirJpaR5");
retVal.setDataSource(dataSource());
retVal.setJpaProperties(jpaProperties());
return retVal;
}
private Properties jpaProperties() {
Properties extraProperties = new Properties();
if (CommonConfig.isLocalTestMode()) {
extraProperties.put("hibernate.dialect", DerbyTenSevenHapiFhirDialect.class.getName());
} else {
extraProperties.put("hibernate.dialect", PostgreSQL94Dialect.class.getName());
}
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", myFhirLuceneLocation);
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
return extraProperties;
}
/**
* Bean which validates incoming requests
*/
@Bean
@Lazy
public RequestValidatingInterceptor requestValidatingInterceptor() {
RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor();
requestValidator.setFailOnSeverity(null);
requestValidator.setAddResponseHeaderOnSeverity(null);
requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
requestValidator.addValidatorModule(instanceValidatorR5());
requestValidator.setIgnoreValidatorExceptions(true);
return requestValidator;
}
@Bean
public PublicSecurityInterceptor securityInterceptor() {
return new PublicSecurityInterceptor();
}
@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
/**
* This lets the "@Value" fields reference properties from the properties file
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}

View File

@ -40,6 +40,20 @@
<load-on-startup>2</load-on-startup>
</servlet>
<servlet>
<servlet-name>fhirServletR5</servlet-name>
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
<init-param>
<param-name>ImplementationDescription</param-name>
<param-value>UHN Test Server (R5 Resources)</param-value>
</init-param>
<init-param>
<param-name>FhirVersion</param-name>
<param-value>R5</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>fhirServletR4</servlet-name>
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
@ -111,16 +125,18 @@
</servlet>
-->
<servlet-mapping>
<servlet-name>fhirServletR5</servlet-name>
<url-pattern>/baseR5/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>fhirServletR4</servlet-name>
<url-pattern>/baseR4/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>fhirServletDstu2</servlet-name>
<url-pattern>/baseDstu2/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>fhirServletDstu3</servlet-name>
<url-pattern>/baseDstu2.1/*</url-pattern>

View File

@ -175,7 +175,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName) {
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}

View File

@ -10,6 +10,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.fhir.ucum.UcumService;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r4.context.IWorkerContext;
@ -26,6 +27,7 @@ import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.r4.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.r4.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
@ -61,6 +63,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
return myValidationSupport.fetchAllStructureDefinitions(myCtx);
}
@Override
public List<StructureDefinition> getStructures() {
return allStructures();
}
@Override
public CodeSystem fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) {
@ -149,9 +156,9 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
}
@Override
public ValidationResult validateCode(CodeableConcept theCode, ValueSet theVs) {
public ValidationResult validateCode(TerminologyServiceOptions theOptions, CodeableConcept theCode, ValueSet theVs) {
for (Coding next : theCode.getCoding()) {
ValidationResult retVal = validateCode(next, theVs);
ValidationResult retVal = validateCode(theOptions, next, theVs);
if (retVal != null && retVal.isOk()) {
return retVal;
}
@ -161,15 +168,15 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
}
@Override
public ValidationResult validateCode(Coding theCode, ValueSet theVs) {
public ValidationResult validateCode(TerminologyServiceOptions theOptions, Coding theCode, ValueSet theVs) {
String system = theCode.getSystem();
String code = theCode.getCode();
String display = theCode.getDisplay();
return validateCode(system, code, display, theVs);
return validateCode(theOptions, system, code, display, theVs);
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) {
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay) {
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
if (result == null) {
return null;
@ -178,12 +185,12 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
throw new UnsupportedOperationException();
}
@Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay, ValueSet theVs) {
if (theVs != null && isNotBlank(theCode)) {
for (ConceptSetComponent next : theVs.getCompose().getInclude()) {
@ -264,8 +271,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
}
@Override
public ValidationResult validateCode(String code, ValueSet vs) {
return validateCode(null, code, null, vs);
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String code, ValueSet vs) {
return validateCode(theOptions, null, code, null, vs);
}
@Override
@ -274,6 +281,11 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
throw new UnsupportedOperationException();
}
@Override
public void generateSnapshot(StructureDefinition p) throws DefinitionException, FHIRException {
throw new UnsupportedOperationException();
}
@Override
public Parameters getExpansionParameters() {
return myExpansionProfile;

View File

@ -78,12 +78,9 @@ public interface IValidationSupport
/**
* Generate a snapshot from the given differential profile.
*
* @param theInput
* @param theUrl
* @param theProfileName
* @return Returns null if this module does not know how to handle this request
*/
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theProfileName);
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName);
/**
* Validates that the given code exists and if possible returns a display

View File

@ -1309,29 +1309,6 @@ public class ClientR4Test {
retVal.addEntry().setResource(p);
return theCtx.newXmlParser().encodeResourceToString(retVal);
// String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
// "<title/>\n" +
// "<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" +
// "<os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults>\n" +
// "<author>\n" +
// "<name>ca.uhn.fhir.rest.server.DummyRestfulServer</name>\n" +
// "</author>\n" +
// "<entry>\n" +
// "<content type=\"text/xml\">"
// + "<Patient xmlns=\"http://hl7.org/fhir\">"
// + "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
// + "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
// + "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
// + "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
// + "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
// + "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
// + "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
// + "</Patient>"
// + "</content>\n"
// + " </entry>\n"
// + "</feed>";
// return msg;
}
}

View File

@ -0,0 +1,329 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-structures-r5</artifactId>
<packaging>bundle</packaging>
<name>HAPI FHIR Structures - FHIR R5</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.utilities</artifactId>
<version>${fhir_core_version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.r5</artifactId>
<version>${fhir_core_version}</version>
</dependency>
<dependency>
<groupId>org.fhir</groupId>
<artifactId>ucum</artifactId>
</dependency>
<!--
Optional dependencies from RI codebase
-->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>es.nitaur.markdown</groupId>
<artifactId>txtmark</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>ST4</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>xpp3</groupId>
<artifactId>xpp3</artifactId>
<optional>true</optional>
</dependency>
<!-- Used by the validator -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<optional>true</optional>
</dependency>
<!--
Test dependencies on other optional parts of HAPI
-->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-r5</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-test-utilities</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- Dependencies for Schematron -->
<dependency>
<groupId>com.helger</groupId>
<artifactId>ph-schematron</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.helger</groupId>
<artifactId>ph-commons</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>javax.activation-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>com.sun.xml.bind</groupId>-->
<!--<artifactId>jaxb-core</artifactId>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>com.sun.xml.bind</groupId>-->
<!--<artifactId>jaxb-impl</artifactId>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<scope>test</scope>
</dependency>
<!-- UNIT TEST DEPENDENCIES -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<classifier>jdk15</classifier>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
<exclusion>
<artifactId>commons-lang</artifactId>
<groupId>commons-lang</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<classifier>jdk15-sources</classifier>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>directory-naming</groupId>
<artifactId>naming-java</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<show>public</show>
</configuration>
</plugin>
</plugins>
</reporting>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<classFolders>
<classFolder>${basedir}/target/classes</classFolder>
<classFolder>${basedir}/../hapi-fhir-base/target/classes</classFolder>
<classFolder>${basedir}/../hapi-fhir-client/target/classes</classFolder>
<classFolder>${basedir}/../hapi-fhir-server/target/classes</classFolder>
</classFolders>
<sourceFolders>
<sourceFolder>${basedir}/src/main/java</sourceFolder>
<sourceFolder>${basedir}/../hapi-fhir-base/src/main/java</sourceFolder>
<sourceFolder>${basedir}/../hapi-fhir-client/src/main/java</sourceFolder>
<sourceFolder>${basedir}/../hapi-fhir-server/src/main/java</sourceFolder>
</sourceFolders>
<dumpOnExit>true</dumpOnExit>
</configuration>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<!-- Don't add UHN licenses to RI structures -->
<skipUpdateLicense>true</skipUpdateLicense>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Fragment-Host>
ca.uhn.hapi.fhir.hapi-fhir-base
</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,321 @@
package org.hl7.fhir.r5.hapi.ctx;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.rest.api.Constants;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport {
private static final String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets;
private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set<String> theWantCodes, List<ConceptDefinitionComponent> theConcepts) {
for (ConceptDefinitionComponent next : theConcepts) {
if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) {
theRetVal
.addContains()
.setSystem(theInclude.getSystem())
.setCode(next.getCode())
.setDisplay(next.getDisplay());
}
addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept());
}
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSetExpander.ValueSetExpansionOutcome retVal = new ValueSetExpander.ValueSetExpansionOutcome(new ValueSet());
Set<String> wantCodes = new HashSet<>();
for (ConceptReferenceComponent next : theInclude.getConcept()) {
wantCodes.add(next.getCode());
}
CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem());
if (system != null) {
List<ConceptDefinitionComponent> concepts = system.getConcept();
addConcepts(theInclude, retVal.getValueset().getExpansion(), wantCodes, concepts);
}
for (UriType next : theInclude.getValueSet()) {
ValueSet vs = myValueSets.get(defaultString(next.getValueAsString()));
if (vs != null) {
for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) {
ValueSetExpander.ValueSetExpansionOutcome contents = expandValueSet(theContext, nextInclude);
retVal.getValueset().getExpansion().getContains().addAll(contents.getValueset().getExpansion().getContains());
}
}
}
return retVal;
}
@Override
public List<IBaseResource> fetchAllConformanceResources(FhirContext theContext) {
ArrayList<IBaseResource> retVal = new ArrayList<>();
retVal.addAll(myCodeSystems.values());
retVal.addAll(myStructureDefinitions.values());
retVal.addAll(myValueSets.values());
return retVal;
}
@Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<>(provideStructureDefinitionMap(theContext).values());
}
@Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true);
}
private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) {
synchronized (this) {
Map<String, CodeSystem> codeSystems = myCodeSystems;
Map<String, ValueSet> valueSets = myValueSets;
if (codeSystems == null || valueSets == null) {
codeSystems = new HashMap<>();
valueSets = new HashMap<>();
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r5/model/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r5/model/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/r5/model/valueset/v3-codesystems.xml");
myCodeSystems = codeSystems;
myValueSets = valueSets;
}
// System can take the form "http://url|version"
String system = theSystem;
if (system.contains("|")) {
String version = system.substring(system.indexOf('|') + 1);
if (version.matches("^[0-9.]+$")) {
system = system.substring(0, system.indexOf('|'));
}
}
if (codeSystem) {
return codeSystems.get(system);
} else {
return valueSets.get(system);
}
}
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
Validate.notBlank(theUri, "theUri must not be null or blank");
if (theClass.equals(StructureDefinition.class)) {
return (T) fetchStructureDefinition(theContext, theUri);
}
if (theClass.equals(ValueSet.class) || theUri.startsWith(URL_PREFIX_VALUE_SET)) {
return (T) fetchValueSet(theContext, theUri);
}
return null;
}
@Override
public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) {
String url = theUrl;
if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
// no change
} else if (url.indexOf('/') == -1) {
url = URL_PREFIX_STRUCTURE_DEFINITION + url;
} else if (StringUtils.countMatches(url, '/') == 1) {
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
}
return provideStructureDefinitionMap(theContext).get(url);
}
@Override
public ValueSet fetchValueSet(FhirContext theContext, String uri) {
return (ValueSet) fetchCodeSystemOrValueSet(theContext, uri, false);
}
public void flush() {
myCodeSystems = null;
myStructureDefinitions = null;
}
@Override
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
CodeSystem cs = fetchCodeSystem(theContext, theSystem);
return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT;
}
@Override
public StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName) {
return null;
}
private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) {
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
InputStream inputStream = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
InputStreamReader reader = null;
if (inputStream != null) {
try {
reader = new InputStreamReader(inputStream, Constants.CHARSET_UTF8);
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof CodeSystem) {
CodeSystem nextValueSet = (CodeSystem) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextValueSet);
}
} else if (next.getResource() instanceof ValueSet) {
ValueSet nextValueSet = (ValueSet) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theValueSets.put(system, nextValueSet);
}
}
}
} finally {
try {
if (reader != null) {
reader.close();
}
inputStream.close();
} catch (IOException e) {
ourLog.warn("Failure closing stream", e);
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private void loadStructureDefinitions(FhirContext theContext, Map<String, StructureDefinition> theCodeSystems, String theClasspath) {
ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
if (valuesetText != null) {
InputStreamReader reader = new InputStreamReader(valuesetText, Constants.CHARSET_UTF8);
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) {
if (next.getResource() instanceof StructureDefinition) {
StructureDefinition nextSd = (StructureDefinition) next.getResource();
nextSd.getText().setDivAsString("");
String system = nextSd.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextSd);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) {
Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions;
if (structureDefinitions == null) {
structureDefinitions = new HashMap<>();
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r5/model/profile/profiles-resources.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r5/model/profile/profiles-types.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r5/model/profile/profiles-others.xml");
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/r5/model/extension/extension-definitions.xml");
myStructureDefinitions = structureDefinitions;
}
return structureDefinitions;
}
private CodeValidationResult testIfConceptIsInList(CodeSystem theCodeSystem, String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
String code = theCode;
if (theCaseSensitive == false) {
code = code.toUpperCase();
}
return testIfConceptIsInListInner(theCodeSystem, conceptList, theCaseSensitive, code);
}
private CodeValidationResult testIfConceptIsInListInner(CodeSystem theCodeSystem, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode();
if (theCaseSensitive == false) {
nextCandidate = nextCandidate.toUpperCase();
}
if (nextCandidate.equals(code)) {
retVal = new CodeValidationResult(next);
break;
}
// recurse
retVal = testIfConceptIsInList(theCodeSystem, code, next.getConcept(), theCaseSensitive);
if (retVal != null) {
break;
}
}
if (retVal != null) {
retVal.setCodeSystemName(theCodeSystem.getName());
retVal.setCodeSystemVersion(theCodeSystem.getVersion());
}
return retVal;
}
@Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(cs, theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
@Override
public IContextValidationSupport.LookupCodeResult lookupCode(FhirContext theContext, String theSystem, String theCode) {
return validateCode(theContext, theSystem, theCode, null).asLookupCodeResult(theSystem, theCode);
}
}

View File

@ -0,0 +1,131 @@
package org.hl7.fhir.r5.hapi.ctx;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import ca.uhn.fhir.fluentpath.IFluentPath;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.util.ReflectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r5.hapi.fhirpath.FhirPathR5;
import org.hl7.fhir.r5.hapi.rest.server.R5BundleFactory;
import org.hl7.fhir.r5.model.*;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class FhirR5 implements IFhirVersion {
private String myId;
@Override
public IFluentPath createFluentPathExecutor(FhirContext theFhirContext) {
return new FhirPathR5(theFhirContext);
}
@Override
public IContextValidationSupport<?, ?, ?, ?, ?, ?> createValidationSupport() {
return ReflectionUtil.newInstanceOfFhirProfileValidationSupport("org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport");
}
@Override
public IBaseResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase) {
StructureDefinition retVal = new StructureDefinition();
RuntimeResourceDefinition def = theRuntimeResourceDefinition;
myId = def.getId();
if (StringUtils.isBlank(myId)) {
myId = theRuntimeResourceDefinition.getName().toLowerCase();
}
retVal.setId(new IdDt(myId));
return retVal;
}
@SuppressWarnings("rawtypes")
@Override
public Class<List> getContainedType() {
return List.class;
}
@Override
public InputStream getFhirVersionPropertiesFile() {
String path = "org/hl7/fhir/r5/model/fhirversion.properties";
InputStream str = FhirR5.class.getResourceAsStream("/" + path);
if (str == null) {
str = FhirR5.class.getResourceAsStream(path);
}
if (str == null) {
throw new ConfigurationException("Can not find model property file on classpath: " + path);
}
return str;
}
@Override
public IPrimitiveType<Date> getLastUpdated(IBaseResource theResource) {
return ((Resource) theResource).getMeta().getLastUpdatedElement();
}
@Override
public String getPathToSchemaDefinitions() {
return "/org/hl7/fhir/r5/model/schema";
}
@Override
public Class<? extends IBaseReference> getResourceReferenceType() {
return Reference.class;
}
@Override
public Object getServerVersion() {
return ReflectionUtil.newInstanceOfFhirServerType("org.hl7.fhir.r5.hapi.ctx.FhirServerR5");
}
@Override
public FhirVersionEnum getVersion() {
return FhirVersionEnum.R5;
}
@Override
public IVersionSpecificBundleFactory newBundleFactory(FhirContext theContext) {
return new R5BundleFactory(theContext);
}
@Override
public IBaseCoding newCodingDt() {
return new Coding();
}
@Override
public IIdType newIdType() {
return new IdType();
}
}

View File

@ -0,0 +1,20 @@
package org.hl7.fhir.r5.hapi.ctx;
import ca.uhn.fhir.rest.api.server.IFhirVersionServer;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import org.hl7.fhir.r5.hapi.rest.server.ServerCapabilityStatementProvider;
import org.hl7.fhir.r5.hapi.rest.server.ServerProfileProvider;
public class FhirServerR5 implements IFhirVersionServer {
@Override
public ServerCapabilityStatementProvider createServerConformanceProvider(RestfulServer theServer) {
return new ServerCapabilityStatementProvider(theServer);
}
@Override
public IResourceProvider createServerProfilesProvider(RestfulServer theRestfulServer) {
return new ServerProfileProvider(theRestfulServer);
}
}

View File

@ -0,0 +1,441 @@
package org.hl7.fhir.r5.hapi.ctx;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.CoverageIgnore;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.fhir.ucum.UcumService;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.ParserType;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport.CodeValidationResult;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.utilities.TerminologyServiceOptions;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander, ValueSetExpanderFactory {
private final FhirContext myCtx;
private final Cache<String, Resource> myFetchedResourceCache;
private IValidationSupport myValidationSupport;
private Parameters myExpansionProfile;
private String myOverrideVersionNs;
public HapiWorkerContext(FhirContext theCtx, IValidationSupport theValidationSupport) {
Validate.notNull(theCtx, "theCtx must not be null");
Validate.notNull(theValidationSupport, "theValidationSupport must not be null");
myCtx = theCtx;
myValidationSupport = theValidationSupport;
long timeoutMillis = 10 * DateUtils.MILLIS_PER_SECOND;
if (System.getProperties().containsKey(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) {
timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
}
myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build();
}
@Override
public List<StructureDefinition> allStructures() {
return myValidationSupport.fetchAllStructureDefinitions(myCtx);
}
@Override
public List<StructureDefinition> getStructures() {
return allStructures();
}
@Override
public CodeSystem fetchCodeSystem(String theSystem) {
if (myValidationSupport == null) {
return null;
} else {
return myValidationSupport.fetchCodeSystem(myCtx, theSystem);
}
}
@Override
public List<ConceptMap> findMapsForSource(String theUrl) {
throw new UnsupportedOperationException();
}
@Override
public String getAbbreviation(String theName) {
throw new UnsupportedOperationException();
}
@Override
public ValueSetExpander getExpander() {
ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this);
retVal.setMaxExpansionSize(Integer.MAX_VALUE);
return retVal;
}
@Override
public org.hl7.fhir.r5.utils.INarrativeGenerator getNarrativeGenerator(String thePrefix, String theBasePath) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(ParserType theType) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(String theType) {
throw new UnsupportedOperationException();
}
@Override
public List<String> getResourceNames() {
List<String> result = new ArrayList<>();
for (ResourceType next : ResourceType.values()) {
result.add(next.name());
}
Collections.sort(result);
return result;
}
@Override
public IParser newJsonParser() {
throw new UnsupportedOperationException();
}
@Override
public IResourceValidator newValidator() {
throw new UnsupportedOperationException();
}
@Override
public IParser newXmlParser() {
throw new UnsupportedOperationException();
}
@Override
public String oid2Uri(String theCode) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsSystem(String theSystem) {
if (myValidationSupport == null) {
return false;
} else {
return myValidationSupport.isCodeSystemSupported(myCtx, theSystem);
}
}
@Override
public Set<String> typeTails() {
return new HashSet<>(Arrays.asList("Integer", "UnsignedInt", "PositiveInt", "Decimal", "DateTime", "Date", "Time", "Instant", "String", "Uri", "Oid", "Uuid", "Id", "Boolean", "Code",
"Markdown", "Base64Binary", "Coding", "CodeableConcept", "Attachment", "Identifier", "Quantity", "SampledData", "Range", "Period", "Ratio", "HumanName", "Address", "ContactPoint",
"Timing", "Reference", "Annotation", "Signature", "Meta"));
}
@Override
public ValidationResult validateCode(TerminologyServiceOptions theOptions, CodeableConcept theCode, ValueSet theVs) {
for (Coding next : theCode.getCoding()) {
ValidationResult retVal = validateCode(theOptions, next, theVs);
if (retVal != null && retVal.isOk()) {
return retVal;
}
}
return new ValidationResult(IssueSeverity.ERROR, null);
}
@Override
public ValidationResult validateCode(TerminologyServiceOptions theOptions, Coding theCode, ValueSet theVs) {
String system = theCode.getSystem();
String code = theCode.getCode();
String display = theCode.getDisplay();
return validateCode(theOptions, system, code, display, theVs);
}
@Override
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay) {
CodeValidationResult result = myValidationSupport.validateCode(myCtx, theSystem, theCode, theDisplay);
if (result == null) {
return null;
}
return new ValidationResult(result.getSeverity(), result.getMessage(), result.asConceptDefinition());
}
@Override
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay, ConceptSetComponent theVsi) {
throw new UnsupportedOperationException();
}
@Override
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String theSystem, String theCode, String theDisplay, ValueSet theVs) {
if (theVs != null && isNotBlank(theCode)) {
for (ConceptSetComponent next : theVs.getCompose().getInclude()) {
if (isBlank(theSystem) || theSystem.equals(next.getSystem())) {
for (ConceptReferenceComponent nextCode : next.getConcept()) {
if (theCode.equals(nextCode.getCode())) {
CodeType code = new CodeType(theCode);
return new ValidationResult(new ConceptDefinitionComponent(code));
}
}
}
}
}
boolean caseSensitive = true;
if (isNotBlank(theSystem)) {
CodeSystem system = fetchCodeSystem(theSystem);
if (system == null) {
return new ValidationResult(IssueSeverity.INFORMATION, "Code " + theSystem + "/" + theCode + " was not validated because the code system is not present");
}
if (system.hasCaseSensitive()) {
caseSensitive = system.getCaseSensitive();
}
}
String wantCode = theCode;
if (!caseSensitive) {
wantCode = wantCode.toUpperCase();
}
ValueSetExpansionOutcome expandedValueSet = null;
/*
* The following valueset is a special case, since the BCP codesystem is very difficult to expand
*/
if (theVs != null && "http://hl7.org/fhir/ValueSet/languages".equals(theVs.getId())) {
ValueSet expansion = new ValueSet();
for (ConceptSetComponent nextInclude : theVs.getCompose().getInclude()) {
for (ConceptReferenceComponent nextConcept : nextInclude.getConcept()) {
expansion.getExpansion().addContains().setCode(nextConcept.getCode()).setDisplay(nextConcept.getDisplay());
}
}
expandedValueSet = new ValueSetExpansionOutcome(expansion);
}
/*
* The following valueset is a special case, since the mime types codesystem is very difficult to expand
*/
if (theVs != null && "http://hl7.org/fhir/ValueSet/mimetypes".equals(theVs.getId())) {
ValueSet expansion = new ValueSet();
expansion.getExpansion().addContains().setCode(theCode).setSystem(theSystem).setDisplay(theDisplay);
expandedValueSet = new ValueSetExpansionOutcome(expansion);
}
if (expandedValueSet == null) {
expandedValueSet = expand(theVs, null);
}
for (ValueSetExpansionContainsComponent next : expandedValueSet.getValueset().getExpansion().getContains()) {
String nextCode = next.getCode();
if (!caseSensitive) {
nextCode = nextCode.toUpperCase();
}
if (nextCode.equals(wantCode)) {
if (theSystem == null || next.getSystem().equals(theSystem)) {
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
definition.setCode(next.getCode());
definition.setDisplay(next.getDisplay());
ValidationResult retVal = new ValidationResult(definition);
return retVal;
}
}
}
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
}
@Override
public ValidationResult validateCode(TerminologyServiceOptions theOptions, String code, ValueSet vs) {
return validateCode(theOptions, null, code, null, vs);
}
@Override
@CoverageIgnore
public List<MetadataResource> allConformanceResources() {
throw new UnsupportedOperationException();
}
@Override
public void generateSnapshot(StructureDefinition p) throws FHIRException {
}
@Override
public Parameters getExpansionParameters() {
return myExpansionProfile;
}
@Override
public void setExpansionProfile(Parameters theExpParameters) {
myExpansionProfile = theExpParameters;
}
@Override
@CoverageIgnore
public boolean hasCache() {
throw new UnsupportedOperationException();
}
@Override
public ValueSetExpansionOutcome expand(ValueSet theSource, Parameters theProfile) {
ValueSetExpansionOutcome vso;
try {
vso = getExpander().expand(theSource, theProfile);
} catch (InvalidRequestException e) {
throw e;
} catch (Exception e) {
throw new InternalErrorException(e);
}
if (vso.getError() != null) {
throw new InvalidRequestException(vso.getError());
} else {
return vso;
}
}
@Override
public ValueSetExpansionOutcome expandVS(ValueSet theSource, boolean theCacheOk, boolean theHeiarchical) {
throw new UnsupportedOperationException();
}
@Override
public ValueSetExpansionOutcome expandVS(ConceptSetComponent theInc, boolean theHeiarchical) throws TerminologyServiceException {
return myValidationSupport.expandValueSet(myCtx, theInc);
}
@Override
public ILoggingService getLogger() {
throw new UnsupportedOperationException();
}
@Override
public void setLogger(ILoggingService theLogger) {
throw new UnsupportedOperationException();
}
@Override
public String getVersion() {
return myCtx.getVersion().getVersion().getFhirVersionString();
}
@Override
public UcumService getUcumService() {
throw new UnsupportedOperationException();
}
@Override
public void setUcumService(UcumService ucumService) {
throw new UnsupportedOperationException();
}
@Override
public boolean isNoTerminologyServer() {
return false;
}
@Override
public TranslationServices translator() {
throw new UnsupportedOperationException();
}
@Override
public List<StructureMap> listTransforms() {
throw new UnsupportedOperationException();
}
@Override
public StructureMap getTransform(String url) {
throw new UnsupportedOperationException();
}
@Override
public String getOverrideVersionNs() {
return myOverrideVersionNs;
}
@Override
public void setOverrideVersionNs(String value) {
myOverrideVersionNs = value;
}
@Override
public StructureDefinition fetchTypeDefinition(String typeName) {
return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName);
}
@Override
public List<String> getTypeNames() {
throw new UnsupportedOperationException();
}
@Override
public <T extends org.hl7.fhir.r5.model.Resource> T fetchResource(Class<T> theClass, String theUri) {
if (myValidationSupport == null) {
return null;
} else {
@SuppressWarnings("unchecked")
T retVal = (T) myFetchedResourceCache.get(theUri, t -> myValidationSupport.fetchResource(myCtx, theClass, theUri));
return retVal;
}
}
@Override
public <T extends org.hl7.fhir.r5.model.Resource> T fetchResourceWithException(Class<T> theClass, String theUri) throws FHIRException {
T retVal = fetchResource(theClass, theUri);
if (retVal == null) {
throw new FHIRException("Could not find resource: " + theUri);
}
return retVal;
}
@Override
public org.hl7.fhir.r5.model.Resource fetchResourceById(String theType, String theUri) {
throw new UnsupportedOperationException();
}
@Override
public <T extends org.hl7.fhir.r5.model.Resource> boolean hasResource(Class<T> theClass_, String theUri) {
throw new UnsupportedOperationException();
}
@Override
public void cacheResource(org.hl7.fhir.r5.model.Resource theRes) throws FHIRException {
throw new UnsupportedOperationException();
}
@Override
public Set<String> getResourceNamesAsSet() {
return myCtx.getResourceNames();
}
@Override
public ValueSetExpansionOutcome expandVS(ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHeiarchical) throws FHIRException {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,127 @@
package org.hl7.fhir.r5.hapi.ctx;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IContextValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.util.List;
public interface IValidationSupport
extends IContextValidationSupport<ConceptSetComponent, ValueSetExpander.ValueSetExpansionOutcome, StructureDefinition, CodeSystem, ConceptDefinitionComponent, IssueSeverity> {
/**
* Expands the given portion of a ValueSet
*
* @param theInclude The portion to include
* @return The expansion
*/
@Override
ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude);
/**
* Load and return all possible structure definitions
*/
@Override
List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext);
/**
* Fetch a code system by Uri
*
* @param uri Canonical Uri of the code system
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
@Override
CodeSystem fetchCodeSystem(FhirContext theContext, String uri);
/**
* Fetch a valueset by Uri
*
* @param uri Canonical Uri of the ValueSet
* @return The valueset (must not be null, but can be an empty ValueSet)
*/
ValueSet fetchValueSet(FhirContext theContext, String uri);
/**
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
* @param theContext The HAPI FHIR Context object current in use by the validator
* @param theClass The type of the resource to load
* @param theUri The resource URI
* @return Returns the resource, or <code>null</code> if no resource with the
* given URI can be found
*/
@Override
<T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri);
@Override
StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl);
/**
* Returns <code>true</code> if codes in the given code system can be expanded
* or validated
*
* @param theSystem The URI for the code system, e.g. <code>"http://loinc.org"</code>
* @return Returns <code>true</code> if codes in the given code system can be
* validated
*/
@Override
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
/**
* Generate a snapshot from the given differential profile.
*
* @param theInput
* @param theUrl
* @param theWebUrl
* @param theProfileName
* @return Returns null if this module does not know how to handle this request
*/
StructureDefinition generateSnapshot(StructureDefinition theInput, String theUrl, String theWebUrl, String theProfileName);
/**
* Validates that the given code exists and if possible returns a display
* name. This method is called to check codes which are found in "example"
* binding fields (e.g. <code>Observation.code</code> in the default profile.
*
* @param theCodeSystem The code system, e.g. "<code>http://loinc.org</code>"
* @param theCode The code, e.g. "<code>1234-5</code>"
* @param theDisplay The display name, if it should also be validated
* @return Returns a validation result object
*/
@Override
CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> {
public CodeValidationResult(ConceptDefinitionComponent theNext) {
super(theNext);
}
public CodeValidationResult(IssueSeverity theSeverity, String theMessage) {
super(theSeverity, theMessage);
}
public CodeValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
super(severity, message, definition);
}
@Override
protected String getDisplay() {
String retVal = null;
if (isOk()) {
retVal = asConceptDefinition().getDisplay();
}
return retVal;
}
}
}

View File

@ -0,0 +1,53 @@
package org.hl7.fhir.r5.hapi.fhirpath;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.fluentpath.FluentPathExecutionException;
import ca.uhn.fhir.fluentpath.IFluentPath;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import java.util.List;
import java.util.Optional;
public class FhirPathR5 implements IFluentPath {
private FHIRPathEngine myEngine;
public FhirPathR5(FhirContext theCtx) {
if (!(theCtx.getValidationSupport() instanceof IValidationSupport)) {
throw new IllegalStateException("Validation support module configured on context appears to be for the wrong FHIR version- Does not extend " + IValidationSupport.class.getName());
}
IValidationSupport validationSupport = (IValidationSupport) theCtx.getValidationSupport();
myEngine = new FHIRPathEngine(new HapiWorkerContext(theCtx, validationSupport));
}
@SuppressWarnings("unchecked")
@Override
public <T extends IBase> List<T> evaluate(IBase theInput, String thePath, Class<T> theReturnType) {
List<Base> result;
try {
result = myEngine.evaluate((Base) theInput, thePath);
} catch (FHIRException e) {
throw new FluentPathExecutionException(e);
}
for (Base next : result) {
if (!theReturnType.isAssignableFrom(next.getClass())) {
throw new FluentPathExecutionException("FluentPath expression \"" + thePath + "\" returned unexpected type " + next.getClass().getSimpleName() + " - Expected " + theReturnType.getName());
}
}
return (List<T>) result;
}
@Override
public <T extends IBase> Optional<T> evaluateFirst(IBase theInput, String thePath, Class<T> theReturnType) {
return evaluate(theInput, thePath, theReturnType).stream().findFirst();
}
}

View File

@ -0,0 +1,107 @@
package org.hl7.fhir.r5.hapi.rest.server;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.annotation.GraphQL;
import ca.uhn.fhir.rest.annotation.GraphQLQuery;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Initialize;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport;
import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.utils.GraphQLEngine;
import org.hl7.fhir.utilities.graphql.ObjectValue;
import org.hl7.fhir.utilities.graphql.Parser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GraphQLProvider {
private final IWorkerContext myWorkerContext;
private Logger ourLog = LoggerFactory.getLogger(GraphQLProvider.class);
private GraphQLEngine.IGraphQLStorageServices myStorageServices;
/**
* Constructor which uses a default context and validation support object
*
* @param theStorageServices The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
*/
public GraphQLProvider(GraphQLEngine.IGraphQLStorageServices theStorageServices) {
this(FhirContext.forR5(), new DefaultProfileValidationSupport(), theStorageServices);
}
/**
* Constructor which uses the given worker context
*
* @param theFhirContext The HAPI FHIR Context object
* @param theValidationSupport The HAPI Validation Support object
* @param theStorageServices The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
*/
public GraphQLProvider(FhirContext theFhirContext, IValidationSupport theValidationSupport, GraphQLEngine.IGraphQLStorageServices theStorageServices) {
myWorkerContext = new HapiWorkerContext(theFhirContext, theValidationSupport);
myStorageServices = theStorageServices;
}
@GraphQL
public String graphql(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
GraphQLEngine engine = new GraphQLEngine(myWorkerContext);
engine.setAppInfo(theRequestDetails);
engine.setServices(myStorageServices);
try {
engine.setGraphQL(Parser.parse(theQuery));
} catch (Exception theE) {
throw new InvalidRequestException("Unable to parse GraphQL Expression: " + theE.toString());
}
try {
if (theId != null) {
Resource focus = myStorageServices.lookup(theRequestDetails, theId.getResourceType(), theId.getIdPart());
engine.setFocus(focus);
}
engine.execute();
StringBuilder outputBuilder = new StringBuilder();
ObjectValue output = engine.getOutput();
output.write(outputBuilder, 0, "\n");
return outputBuilder.toString();
} catch (Exception e) {
StringBuilder b = new StringBuilder();
b.append("Unable to execute GraphQL Expression: ");
int statusCode = 500;
if (e instanceof BaseServerResponseException) {
b.append("HTTP ");
statusCode = ((BaseServerResponseException) e).getStatusCode();
b.append(statusCode);
b.append(" ");
} else {
// This means it's a bug, so let's log
ourLog.error("Failure during GraphQL processing", e);
}
b.append(e.getMessage());
throw new UnclassifiedServerFailureException(statusCode, b.toString());
}
}
@Initialize
public void initialize(RestfulServer theServer) {
ourLog.trace("Initializing GraphQL provider");
if (theServer.getFhirContext().getVersion().getVersion() != FhirVersionEnum.R5) {
throw new ConfigurationException("Can not use " + getClass().getName() + " provider on server with FHIR " + theServer.getFhirContext().getVersion().getVersion().name() + " context");
}
}
}

View File

@ -0,0 +1,396 @@
package org.hl7.fhir.r5.hapi.rest.server;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.BundleInclusionRule;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.r5.model.Bundle.HTTPVerb;
import org.hl7.fhir.r5.model.Bundle.SearchEntryMode;
import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Resource;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("Duplicates")
public class R5BundleFactory implements IVersionSpecificBundleFactory {
private String myBase;
private Bundle myBundle;
private FhirContext myContext;
public R5BundleFactory(FhirContext theContext) {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<>();
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
// Linked resources may themselves have linked resources
references = new ArrayList<>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
includedResources.addAll(addedResourcesThisPass);
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
if ("DELETE".equals(httpVerb)) {
entry.setResource(null);
}
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
ensureBundle();
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource next : theResult) {
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (isNotBlank(nextContained.getId())) {
containedIds.add(nextContained.getId());
}
}
}
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
for (ResourceReferenceInfo nextRefInfo : references) {
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
continue;
}
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
}
}
}
includedResources.addAll(addedResourcesThisPass);
// Linked resources may themselves have linked resources
references = new ArrayList<>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
references.addAll(newReferences);
}
} while (references.isEmpty() == false);
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
Resource nextAsResource = (Resource) next;
IIdType id = populateBundleEntryFullUrl(next, entry);
// Populate Request
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
if (id != null) {
entry.getRequest().setUrl(id.getValue());
}
}
if ("DELETE".equals(httpVerb)) {
entry.setResource(null);
}
// Populate Bundle.entry.response
if (theBundleType != null) {
switch (theBundleType) {
case BATCH_RESPONSE:
case TRANSACTION_RESPONSE:
case HISTORY:
if ("1".equals(id.getVersionIdPart())) {
entry.getResponse().setStatus("201 Created");
} else if (isNotBlank(id.getVersionIdPart())) {
entry.getResponse().setStatus("200 OK");
}
if (isNotBlank(id.getVersionIdPart())) {
entry.getResponse().setEtag(RestfulServerUtils.createEtag(id.getVersionIdPart()));
}
break;
}
}
// Populate Bundle.entry.search
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode);
}
}
/*
* Actually add the resources to the bundle
*/
for (IAnyResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
populateBundleEntryFullUrl(next, entry);
}
}
@Override
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
IPrimitiveType<Date> theLastUpdated) {
ensureBundle();
myBase = theServerBase;
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(theId);
}
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
}
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
}
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
}
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) {
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
}
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
}
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
myBundle.getTotalElement().setValue(theTotalResults);
}
}
private void ensureBundle() {
if (myBundle == null) {
myBundle = new Bundle();
}
}
@Override
public IBaseResource getResourceBundle() {
return myBundle;
}
private boolean hasLink(String theLinkType, Bundle theBundle) {
for (BundleLinkComponent next : theBundle.getLink()) {
if (theLinkType.equals(next.getRelation())) {
return true;
}
}
return false;
}
@Override
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
BundleTypeEnum theBundleType) {
myBundle = new Bundle();
myBundle.setId(UUID.randomUUID().toString());
myBundle.getMeta().setLastUpdated(new Date());
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
for (IBaseResource nextBaseRes : theResources) {
Resource next = (Resource) nextBaseRes;
BundleEntryComponent nextEntry = myBundle.addEntry();
nextEntry.setResource(next);
if (next.getIdElement().isEmpty()) {
nextEntry.getRequest().setMethod(HTTPVerb.POST);
} else {
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
if (next.getIdElement().isAbsolute()) {
nextEntry.getRequest().setUrl(next.getId());
} else {
String resourceType = myContext.getResourceDefinition(next).getName();
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
}
}
}
} else {
addResourcesForSearch(theResources);
}
myBundle.getTotalElement().setValue(theTotalResults);
}
@Override
public void initializeWithBundleResource(IBaseResource theBundle) {
myBundle = (Bundle) theBundle;
}
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
IIdType idElement = null;
if (next.getIdElement().hasBaseUrl()) {
idElement = next.getIdElement();
entry.setFullUrl(idElement.toVersionless().getValue());
} else {
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
idElement = next.getIdElement();
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
entry.setFullUrl(idElement.toVersionless().getValue());
}
}
return idElement;
}
@Override
public List<IBaseResource> toListOfResources() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (BundleEntryComponent next : myBundle.getEntry()) {
if (next.getResource() != null) {
retVal.add(next.getResource());
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
IdType id = new IdType(next.getResponse().getLocation());
String resourceType = id.getResourceType();
if (isNotBlank(resourceType)) {
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
res.setId(id);
retVal.add(res);
}
}
}
return retVal;
}
}

View File

@ -0,0 +1,602 @@
package org.hl7.fhir.r5.hapi.rest.server;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.Bindings;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.method.*;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
import ca.uhn.fhir.rest.server.method.SearchParameter;
import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.CapabilityStatement.*;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.OperationDefinition.OperationDefinitionParameterComponent;
import org.hl7.fhir.r5.model.OperationDefinition.OperationKind;
import org.hl7.fhir.r5.model.OperationDefinition.OperationParameterUse;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
/**
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
*
* <p>
* Note: This class is safe to extend, but it is important to note that the same instance of {@link CapabilityStatement} is always returned unless {@link #setCache(boolean)} is called with a value of
* <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor.
* </p>
*/
public class ServerCapabilityStatementProvider extends BaseServerCapabilityStatementProvider implements IServerConformanceProvider<CapabilityStatement> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
private String myPublisher = "Not provided";
/**
* No-arg constructor and setter so that the ServerConformanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen.
*/
public ServerCapabilityStatementProvider() {
super();
}
/**
* Constructor
*
* @deprecated Use no-args constructor instead. Deprecated in 4.0.0
*/
@Deprecated
public ServerCapabilityStatementProvider(RestfulServer theRestfulServer) {
this();
}
/**
* Constructor - This is intended only for JAX-RS server
*/
public ServerCapabilityStatementProvider(RestfulServerConfiguration theServerConfiguration) {
super(theServerConfiguration);
}
private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) {
String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) {
SystemRestfulInteraction sysOp;
try {
sysOp = SystemRestfulInteraction.fromCode(sysOpCode);
} catch (FHIRException e) {
return;
}
if (sysOp == null) {
return;
}
if (systemOps.contains(sysOp) == false) {
systemOps.add(sysOp);
rest.addInteraction().setCode(sysOp);
}
}
}
}
private DateTimeType conformanceDate(RequestDetails theRequestDetails) {
IPrimitiveType<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate();
if (buildDate != null && buildDate.getValue() != null) {
try {
return new DateTimeType(buildDate.getValueAsString());
} catch (DataFormatException e) {
// fall through
}
}
return DateTimeType.now();
}
/**
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
*/
public String getPublisher() {
return myPublisher;
}
/**
* Sets the value of the "publisher" that will be placed in the generated conformance statement. As this is a mandatory element, the value should not be null (although this is not enforced). The
* value defaults to "Not provided" but may be set to null, which will cause this element to be omitted.
*/
public void setPublisher(String thePublisher) {
myPublisher = thePublisher;
}
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
@Override
@Metadata
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
RestfulServerConfiguration configuration = getServerConfiguration(theRequestDetails);
Bindings bindings = configuration.provideBindings();
CapabilityStatement retVal = new CapabilityStatement();
retVal.setPublisher(myPublisher);
retVal.setDateElement(conformanceDate(theRequestDetails));
retVal.setFhirVersion(Enumerations.FHIRVersion.fromCode(FhirVersionEnum.R5.getFhirVersionString()));
ServletContext servletContext = (ServletContext) (theRequest == null ? null : theRequest.getAttribute(RestfulServer.SERVLET_CONTEXT_ATTRIBUTE));
String serverBase = configuration.getServerAddressStrategy().determineServerBase(servletContext, theRequest);
retVal
.getImplementation()
.setUrl(serverBase)
.setDescription(configuration.getImplementationDescription());
retVal.setKind(CapabilityStatementKind.INSTANCE);
retVal.getSoftware().setName(configuration.getServerName());
retVal.getSoftware().setVersion(configuration.getServerVersion());
retVal.addFormat(Constants.CT_FHIR_XML_NEW);
retVal.addFormat(Constants.CT_FHIR_JSON_NEW);
retVal.setStatus(PublicationStatus.ACTIVE);
CapabilityStatementRestComponent rest = retVal.addRest();
rest.setMode(RestfulCapabilityMode.SERVER);
Set<SystemRestfulInteraction> systemOps = new HashSet<>();
Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
CapabilityStatementRestResourceComponent resource = rest.addResource();
String resourceName = nextEntry.getKey();
RuntimeResourceDefinition def = configuration.getFhirContext().getResourceDefinition(resourceName);
resource.getTypeElement().setValue(def.getName());
resource.getProfileElement().setValue((def.getResourceProfile(serverBase)));
TreeSet<String> includes = new TreeSet<>();
// Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// CapabilityStatement.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) {
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) {
TypeRestfulInteraction resOp;
try {
resOp = TypeRestfulInteraction.fromCode(resOpCode);
} catch (Exception e) {
resOp = null;
}
if (resOp != null) {
if (resourceOps.contains(resOp) == false) {
resourceOps.add(resOp);
resource.addInteraction().setCode(resOp);
}
if ("vread".equals(resOpCode)) {
// vread implies read
resOp = TypeRestfulInteraction.READ;
if (resourceOps.contains(resOp) == false) {
resourceOps.add(resOp);
resource.addInteraction().setCode(resOp);
}
}
if (nextMethodBinding.isSupportsConditional()) {
switch (resOp) {
case CREATE:
resource.setConditionalCreate(true);
break;
case DELETE:
if (nextMethodBinding.isSupportsConditionalMultiple()) {
resource.setConditionalDelete(ConditionalDeleteStatus.MULTIPLE);
} else {
resource.setConditionalDelete(ConditionalDeleteStatus.SINGLE);
}
break;
case UPDATE:
resource.setConditionalUpdate(true);
break;
default:
break;
}
}
}
}
}
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof SearchMethodBinding) {
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
if (methodBinding.getQueryName() != null) {
String queryName = bindings.getNamedSearchMethodBindingToName().get(methodBinding);
if (operationNames.add(queryName)) {
rest.addOperation().setName(methodBinding.getQueryName()).setDefinition(("OperationDefinition/" + queryName));
}
} else {
handleNamelessSearchMethodBinding(resource, def, includes, (SearchMethodBinding) nextMethodBinding, theRequestDetails);
}
} else if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
String opName = bindings.getOperationBindingToName().get(methodBinding);
if (operationNames.add(opName)) {
// Only add each operation (by name) once
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(("OperationDefinition/" + opName));
}
}
resource.getInteraction().sort(new Comparator<ResourceInteractionComponent>() {
@Override
public int compare(ResourceInteractionComponent theO1, ResourceInteractionComponent theO2) {
TypeRestfulInteraction o1 = theO1.getCode();
TypeRestfulInteraction o2 = theO2.getCode();
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return 1;
}
if (o2 == null) {
return -1;
}
return o1.ordinal() - o2.ordinal();
}
});
}
for (String nextInclude : includes) {
resource.addSearchInclude(nextInclude);
}
} else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
String opName = bindings.getOperationBindingToName().get(methodBinding);
if (operationNames.add(opName)) {
ourLog.debug("Found bound operation: {}", opName);
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(("OperationDefinition/" + opName));
}
}
}
}
}
return retVal;
}
private void handleNamelessSearchMethodBinding(CapabilityStatementRestResourceComponent resource, RuntimeResourceDefinition def, TreeSet<String> includes,
SearchMethodBinding searchMethodBinding, RequestDetails theRequestDetails) {
includes.addAll(searchMethodBinding.getIncludes());
List<IParameter> params = searchMethodBinding.getParameters();
List<SearchParameter> searchParameters = new ArrayList<>();
for (IParameter nextParameter : params) {
if ((nextParameter instanceof SearchParameter)) {
searchParameters.add((SearchParameter) nextParameter);
}
}
sortSearchParameters(searchParameters);
if (!searchParameters.isEmpty()) {
// boolean allOptional = searchParameters.get(0).isRequired() == false;
//
// OperationDefinition query = null;
// if (!allOptional) {
// RestOperation operation = rest.addOperation();
// query = new OperationDefinition();
// operation.setDefinition(new ResourceReferenceDt(query));
// query.getDescriptionElement().setValue(searchMethodBinding.getDescription());
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
// for (String nextInclude : searchMethodBinding.getIncludes()) {
// query.addUndeclaredExtension(false, ExtensionConstants.QUERY_ALLOWED_INCLUDE, new StringDt(nextInclude));
// }
// }
for (SearchParameter nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) {
chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
}
CapabilityStatementRestResourceSearchParamComponent param = resource.addSearchParam();
param.setName(nextParamUnchainedName);
// if (StringUtils.isNotBlank(chain)) {
// param.addChain(chain);
// }
//
// if (nextParameter.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
// for (String nextWhitelist : new TreeSet<String>(nextParameter.getQualifierWhitelist())) {
// if (nextWhitelist.startsWith(".")) {
// param.addChain(nextWhitelist.substring(1));
// }
// }
// }
param.setDocumentation(nextParamDescription);
if (nextParameter.getParamType() != null) {
param.getTypeElement().setValueAsString(nextParameter.getParamType().getCode());
}
for (Class<? extends IBaseResource> nextTarget : nextParameter.getDeclaredTypes()) {
RuntimeResourceDefinition targetDef = getServerConfiguration(theRequestDetails).getFhirContext().getResourceDefinition(nextTarget);
if (targetDef != null) {
ResourceType code;
try {
code = ResourceType.fromCode(targetDef.getName());
} catch (FHIRException e) {
code = null;
}
// if (code != null) {
// param.addTarget(targetDef.getName());
// }
}
}
}
}
}
@Read(type = OperationDefinition.class)
public OperationDefinition readOperationDefinition(@IdParam IdType theId, RequestDetails theRequestDetails) {
if (theId == null || theId.hasIdPart() == false) {
throw new ResourceNotFoundException(theId);
}
RestfulServerConfiguration configuration = getServerConfiguration(theRequestDetails);
Bindings bindings = configuration.provideBindings();
List<OperationMethodBinding> operationBindings = bindings.getOperationNameToBindings().get(theId.getIdPart());
if (operationBindings != null && !operationBindings.isEmpty()) {
return readOperationDefinitionForOperation(operationBindings);
}
List<SearchMethodBinding> searchBindings = bindings.getSearchNameToBindings().get(theId.getIdPart());
if (searchBindings != null && !searchBindings.isEmpty()) {
return readOperationDefinitionForNamedSearch(searchBindings);
}
throw new ResourceNotFoundException(theId);
}
private OperationDefinition readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) {
OperationDefinition op = new OperationDefinition();
op.setStatus(PublicationStatus.ACTIVE);
op.setKind(OperationKind.QUERY);
op.setAffectsState(false);
op.setSystem(false);
op.setType(false);
op.setInstance(false);
Set<String> inParams = new HashSet<>();
for (SearchMethodBinding binding : bindings) {
if (isNotBlank(binding.getDescription())) {
op.setDescription(binding.getDescription());
}
if (isBlank(binding.getResourceProviderResourceName())) {
op.setSystem(true);
} else {
op.setType(true);
op.addResourceElement().setValue(binding.getResourceProviderResourceName());
}
op.setCode(binding.getQueryName());
for (IParameter nextParamUntyped : binding.getParameters()) {
if (nextParamUntyped instanceof SearchParameter) {
SearchParameter nextParam = (SearchParameter) nextParamUntyped;
if (!inParams.add(nextParam.getName())) {
continue;
}
OperationDefinitionParameterComponent param = op.addParameter();
param.setUse(OperationParameterUse.IN);
param.setType("string");
param.getSearchTypeElement().setValueAsString(nextParam.getParamType().getCode());
param.setMin(nextParam.isRequired() ? 1 : 0);
param.setMax("1");
param.setName(nextParam.getName());
}
}
if (isBlank(op.getName())) {
if (isNotBlank(op.getDescription())) {
op.setName(op.getDescription());
} else {
op.setName(op.getCode());
}
}
}
return op;
}
private OperationDefinition readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) {
OperationDefinition op = new OperationDefinition();
op.setStatus(PublicationStatus.ACTIVE);
op.setKind(OperationKind.OPERATION);
op.setAffectsState(false);
// We reset these to true below if we find a binding that can handle the level
op.setSystem(false);
op.setType(false);
op.setInstance(false);
Set<String> inParams = new HashSet<>();
Set<String> outParams = new HashSet<>();
for (OperationMethodBinding sharedDescription : bindings) {
if (isNotBlank(sharedDescription.getDescription())) {
op.setDescription(sharedDescription.getDescription());
}
if (sharedDescription.isCanOperateAtInstanceLevel()) {
op.setInstance(true);
}
if (sharedDescription.isCanOperateAtServerLevel()) {
op.setSystem(true);
}
if (sharedDescription.isCanOperateAtTypeLevel()) {
op.setType(true);
}
if (!sharedDescription.isIdempotent()) {
op.setAffectsState(!sharedDescription.isIdempotent());
}
op.setCode(sharedDescription.getName().substring(1));
if (sharedDescription.isCanOperateAtInstanceLevel()) {
op.setInstance(sharedDescription.isCanOperateAtInstanceLevel());
}
if (sharedDescription.isCanOperateAtServerLevel()) {
op.setSystem(sharedDescription.isCanOperateAtServerLevel());
}
if (isNotBlank(sharedDescription.getResourceName())) {
op.addResourceElement().setValue(sharedDescription.getResourceName());
}
for (IParameter nextParamUntyped : sharedDescription.getParameters()) {
if (nextParamUntyped instanceof OperationParameter) {
OperationParameter nextParam = (OperationParameter) nextParamUntyped;
OperationDefinitionParameterComponent param = op.addParameter();
if (!inParams.add(nextParam.getName())) {
continue;
}
param.setUse(OperationParameterUse.IN);
if (nextParam.getParamType() != null) {
param.setType(nextParam.getParamType());
}
if (nextParam.getSearchParamType() != null) {
param.getSearchTypeElement().setValueAsString(nextParam.getSearchParamType());
}
param.setMin(nextParam.getMin());
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
param.setName(nextParam.getName());
}
}
for (ReturnType nextParam : sharedDescription.getReturnParams()) {
if (!outParams.add(nextParam.getName())) {
continue;
}
OperationDefinitionParameterComponent param = op.addParameter();
param.setUse(OperationParameterUse.OUT);
if (nextParam.getType() != null) {
param.setType(nextParam.getType());
}
param.setMin(nextParam.getMin());
param.setMax(nextParam.getMax() == -1 ? "*" : Integer.toString(nextParam.getMax()));
param.setName(nextParam.getName());
}
}
if (isBlank(op.getName())) {
if (isNotBlank(op.getDescription())) {
op.setName(op.getDescription());
} else {
op.setName(op.getCode());
}
}
if (op.hasSystem() == false) {
op.setSystem(false);
}
if (op.hasInstance() == false) {
op.setInstance(false);
}
return op;
}
/**
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
* <p>
* See the class documentation for an important note if you are extending this class
* </p>
*
* @deprecated Since 4.0.0 - This method no longer does anything
*/
@Deprecated
public ServerCapabilityStatementProvider setCache(boolean theCache) {
return this;
}
@Override
public void setRestfulServer(RestfulServer theRestfulServer) {
// ignore
}
private void sortRuntimeSearchParameters(List<RuntimeSearchParam> searchParameters) {
Collections.sort(searchParameters, new Comparator<RuntimeSearchParam>() {
@Override
public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
return theO1.getName().compareTo(theO2.getName());
}
});
}
private void sortSearchParameters(List<SearchParameter> searchParameters) {
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
@Override
public int compare(SearchParameter theO1, SearchParameter theO2) {
if (theO1.isRequired() == theO2.isRequired()) {
return theO1.getName().compareTo(theO2.getName());
}
if (theO1.isRequired()) {
return -1;
}
return 1;
}
});
}
}

View File

@ -0,0 +1,88 @@
package org.hl7.fhir.r5.hapi.rest.server;
/*
* #%L
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
* %%
* Copyright (C) 2014 - 2015 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.StructureDefinition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ServerProfileProvider implements IResourceProvider {
private final FhirContext myContext;
private final RestfulServer myRestfulServer;
public ServerProfileProvider(RestfulServer theServer) {
myContext = theServer.getFhirContext();
myRestfulServer = theServer;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return StructureDefinition.class;
}
@Read()
public StructureDefinition getProfileById(ServletRequestDetails theRequest, @IdParam IdType theId) {
RuntimeResourceDefinition retVal = myContext.getResourceDefinitionById(theId.getIdPart());
if (retVal==null) {
return null;
}
String serverBase = getServerBase(theRequest);
return (StructureDefinition) retVal.toProfile(serverBase);
}
@Search()
public List<StructureDefinition> getAllProfiles(ServletRequestDetails theRequest) {
final String serverBase = getServerBase(theRequest);
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitionsWithExplicitId());
Collections.sort(defs, new Comparator<RuntimeResourceDefinition>() {
@Override
public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
int cmp = theO1.getName().compareTo(theO2.getName());
if (cmp==0) {
cmp=theO1.getResourceProfile(serverBase).compareTo(theO2.getResourceProfile(serverBase));
}
return cmp;
}});
ArrayList<StructureDefinition> retVal = new ArrayList<StructureDefinition>();
for (RuntimeResourceDefinition next : defs) {
retVal.add((StructureDefinition) next.toProfile(serverBase));
}
return retVal;
}
private String getServerBase(ServletRequestDetails theHttpRequest) {
return myRestfulServer.getServerBaseForRequest(theHttpRequest);
}
}

View File

@ -0,0 +1,212 @@
# This file contains version definitions
# Generated: 2019-08-05T14:22:40.297-04:00
resource.Account=org.hl7.fhir.r5.model.Account
resource.ActivityDefinition=org.hl7.fhir.r5.model.ActivityDefinition
resource.AdverseEvent=org.hl7.fhir.r5.model.AdverseEvent
resource.AllergyIntolerance=org.hl7.fhir.r5.model.AllergyIntolerance
resource.Appointment=org.hl7.fhir.r5.model.Appointment
resource.AppointmentResponse=org.hl7.fhir.r5.model.AppointmentResponse
resource.AuditEvent=org.hl7.fhir.r5.model.AuditEvent
resource.Basic=org.hl7.fhir.r5.model.Basic
resource.Binary=org.hl7.fhir.r5.model.Binary
resource.BiologicallyDerivedProduct=org.hl7.fhir.r5.model.BiologicallyDerivedProduct
resource.BodyStructure=org.hl7.fhir.r5.model.BodyStructure
resource.Bundle=org.hl7.fhir.r5.model.Bundle
resource.CapabilityStatement=org.hl7.fhir.r5.model.CapabilityStatement
resource.CarePlan=org.hl7.fhir.r5.model.CarePlan
resource.CareTeam=org.hl7.fhir.r5.model.CareTeam
resource.CatalogEntry=org.hl7.fhir.r5.model.CatalogEntry
resource.ChargeItem=org.hl7.fhir.r5.model.ChargeItem
resource.ChargeItemDefinition=org.hl7.fhir.r5.model.ChargeItemDefinition
resource.Claim=org.hl7.fhir.r5.model.Claim
resource.ClaimResponse=org.hl7.fhir.r5.model.ClaimResponse
resource.ClinicalImpression=org.hl7.fhir.r5.model.ClinicalImpression
resource.CodeSystem=org.hl7.fhir.r5.model.CodeSystem
resource.Communication=org.hl7.fhir.r5.model.Communication
resource.CommunicationRequest=org.hl7.fhir.r5.model.CommunicationRequest
resource.CompartmentDefinition=org.hl7.fhir.r5.model.CompartmentDefinition
resource.Composition=org.hl7.fhir.r5.model.Composition
resource.ConceptMap=org.hl7.fhir.r5.model.ConceptMap
resource.Condition=org.hl7.fhir.r5.model.Condition
resource.Consent=org.hl7.fhir.r5.model.Consent
resource.Contract=org.hl7.fhir.r5.model.Contract
resource.Coverage=org.hl7.fhir.r5.model.Coverage
resource.CoverageEligibilityRequest=org.hl7.fhir.r5.model.CoverageEligibilityRequest
resource.CoverageEligibilityResponse=org.hl7.fhir.r5.model.CoverageEligibilityResponse
resource.DetectedIssue=org.hl7.fhir.r5.model.DetectedIssue
resource.Device=org.hl7.fhir.r5.model.Device
resource.DeviceDefinition=org.hl7.fhir.r5.model.DeviceDefinition
resource.DeviceMetric=org.hl7.fhir.r5.model.DeviceMetric
resource.DeviceRequest=org.hl7.fhir.r5.model.DeviceRequest
resource.DeviceUseStatement=org.hl7.fhir.r5.model.DeviceUseStatement
resource.DiagnosticReport=org.hl7.fhir.r5.model.DiagnosticReport
resource.DocumentManifest=org.hl7.fhir.r5.model.DocumentManifest
resource.DocumentReference=org.hl7.fhir.r5.model.DocumentReference
resource.EffectEvidenceSynthesis=org.hl7.fhir.r5.model.EffectEvidenceSynthesis
resource.Encounter=org.hl7.fhir.r5.model.Encounter
resource.Endpoint=org.hl7.fhir.r5.model.Endpoint
resource.EnrollmentRequest=org.hl7.fhir.r5.model.EnrollmentRequest
resource.EnrollmentResponse=org.hl7.fhir.r5.model.EnrollmentResponse
resource.EpisodeOfCare=org.hl7.fhir.r5.model.EpisodeOfCare
resource.EventDefinition=org.hl7.fhir.r5.model.EventDefinition
resource.Evidence=org.hl7.fhir.r5.model.Evidence
resource.EvidenceVariable=org.hl7.fhir.r5.model.EvidenceVariable
resource.ExampleScenario=org.hl7.fhir.r5.model.ExampleScenario
resource.ExplanationOfBenefit=org.hl7.fhir.r5.model.ExplanationOfBenefit
resource.FamilyMemberHistory=org.hl7.fhir.r5.model.FamilyMemberHistory
resource.Flag=org.hl7.fhir.r5.model.Flag
resource.Goal=org.hl7.fhir.r5.model.Goal
resource.GraphDefinition=org.hl7.fhir.r5.model.GraphDefinition
resource.Group=org.hl7.fhir.r5.model.Group
resource.GuidanceResponse=org.hl7.fhir.r5.model.GuidanceResponse
resource.HealthcareService=org.hl7.fhir.r5.model.HealthcareService
resource.ImagingStudy=org.hl7.fhir.r5.model.ImagingStudy
resource.Immunization=org.hl7.fhir.r5.model.Immunization
resource.ImmunizationEvaluation=org.hl7.fhir.r5.model.ImmunizationEvaluation
resource.ImmunizationRecommendation=org.hl7.fhir.r5.model.ImmunizationRecommendation
resource.ImplementationGuide=org.hl7.fhir.r5.model.ImplementationGuide
resource.InsurancePlan=org.hl7.fhir.r5.model.InsurancePlan
resource.Invoice=org.hl7.fhir.r5.model.Invoice
resource.Library=org.hl7.fhir.r5.model.Library
resource.Linkage=org.hl7.fhir.r5.model.Linkage
resource.List=org.hl7.fhir.r5.model.ListResource
resource.Location=org.hl7.fhir.r5.model.Location
resource.Measure=org.hl7.fhir.r5.model.Measure
resource.MeasureReport=org.hl7.fhir.r5.model.MeasureReport
resource.Media=org.hl7.fhir.r5.model.Media
resource.Medication=org.hl7.fhir.r5.model.Medication
resource.MedicationAdministration=org.hl7.fhir.r5.model.MedicationAdministration
resource.MedicationDispense=org.hl7.fhir.r5.model.MedicationDispense
resource.MedicationKnowledge=org.hl7.fhir.r5.model.MedicationKnowledge
resource.MedicationRequest=org.hl7.fhir.r5.model.MedicationRequest
resource.MedicationStatement=org.hl7.fhir.r5.model.MedicationStatement
resource.MedicinalProduct=org.hl7.fhir.r5.model.MedicinalProduct
resource.MedicinalProductAuthorization=org.hl7.fhir.r5.model.MedicinalProductAuthorization
resource.MedicinalProductContraindication=org.hl7.fhir.r5.model.MedicinalProductContraindication
resource.MedicinalProductIndication=org.hl7.fhir.r5.model.MedicinalProductIndication
resource.MedicinalProductIngredient=org.hl7.fhir.r5.model.MedicinalProductIngredient
resource.MedicinalProductInteraction=org.hl7.fhir.r5.model.MedicinalProductInteraction
resource.MedicinalProductManufactured=org.hl7.fhir.r5.model.MedicinalProductManufactured
resource.MedicinalProductPackaged=org.hl7.fhir.r5.model.MedicinalProductPackaged
resource.MedicinalProductPharmaceutical=org.hl7.fhir.r5.model.MedicinalProductPharmaceutical
resource.MedicinalProductUndesirableEffect=org.hl7.fhir.r5.model.MedicinalProductUndesirableEffect
resource.MessageDefinition=org.hl7.fhir.r5.model.MessageDefinition
resource.MessageHeader=org.hl7.fhir.r5.model.MessageHeader
resource.MolecularSequence=org.hl7.fhir.r5.model.MolecularSequence
resource.NamingSystem=org.hl7.fhir.r5.model.NamingSystem
resource.NutritionOrder=org.hl7.fhir.r5.model.NutritionOrder
resource.Observation=org.hl7.fhir.r5.model.Observation
resource.ObservationDefinition=org.hl7.fhir.r5.model.ObservationDefinition
resource.OperationDefinition=org.hl7.fhir.r5.model.OperationDefinition
resource.OperationOutcome=org.hl7.fhir.r5.model.OperationOutcome
resource.Organization=org.hl7.fhir.r5.model.Organization
resource.OrganizationAffiliation=org.hl7.fhir.r5.model.OrganizationAffiliation
resource.Parameters=org.hl7.fhir.r5.model.Parameters
resource.Patient=org.hl7.fhir.r5.model.Patient
resource.PaymentNotice=org.hl7.fhir.r5.model.PaymentNotice
resource.PaymentReconciliation=org.hl7.fhir.r5.model.PaymentReconciliation
resource.Person=org.hl7.fhir.r5.model.Person
resource.PlanDefinition=org.hl7.fhir.r5.model.PlanDefinition
resource.Practitioner=org.hl7.fhir.r5.model.Practitioner
resource.PractitionerRole=org.hl7.fhir.r5.model.PractitionerRole
resource.Procedure=org.hl7.fhir.r5.model.Procedure
resource.Provenance=org.hl7.fhir.r5.model.Provenance
resource.Questionnaire=org.hl7.fhir.r5.model.Questionnaire
resource.QuestionnaireResponse=org.hl7.fhir.r5.model.QuestionnaireResponse
resource.RelatedPerson=org.hl7.fhir.r5.model.RelatedPerson
resource.RequestGroup=org.hl7.fhir.r5.model.RequestGroup
resource.ResearchDefinition=org.hl7.fhir.r5.model.ResearchDefinition
resource.ResearchElementDefinition=org.hl7.fhir.r5.model.ResearchElementDefinition
resource.ResearchStudy=org.hl7.fhir.r5.model.ResearchStudy
resource.ResearchSubject=org.hl7.fhir.r5.model.ResearchSubject
resource.RiskAssessment=org.hl7.fhir.r5.model.RiskAssessment
resource.RiskEvidenceSynthesis=org.hl7.fhir.r5.model.RiskEvidenceSynthesis
resource.Schedule=org.hl7.fhir.r5.model.Schedule
resource.SearchParameter=org.hl7.fhir.r5.model.SearchParameter
resource.ServiceRequest=org.hl7.fhir.r5.model.ServiceRequest
resource.Slot=org.hl7.fhir.r5.model.Slot
resource.Specimen=org.hl7.fhir.r5.model.Specimen
resource.SpecimenDefinition=org.hl7.fhir.r5.model.SpecimenDefinition
resource.StructureDefinition=org.hl7.fhir.r5.model.StructureDefinition
resource.StructureMap=org.hl7.fhir.r5.model.StructureMap
resource.Subscription=org.hl7.fhir.r5.model.Subscription
resource.Substance=org.hl7.fhir.r5.model.Substance
resource.SubstanceNucleicAcid=org.hl7.fhir.r5.model.SubstanceNucleicAcid
resource.SubstancePolymer=org.hl7.fhir.r5.model.SubstancePolymer
resource.SubstanceProtein=org.hl7.fhir.r5.model.SubstanceProtein
resource.SubstanceReferenceInformation=org.hl7.fhir.r5.model.SubstanceReferenceInformation
resource.SubstanceSourceMaterial=org.hl7.fhir.r5.model.SubstanceSourceMaterial
resource.SubstanceSpecification=org.hl7.fhir.r5.model.SubstanceSpecification
resource.SupplyDelivery=org.hl7.fhir.r5.model.SupplyDelivery
resource.SupplyRequest=org.hl7.fhir.r5.model.SupplyRequest
resource.Task=org.hl7.fhir.r5.model.Task
resource.TerminologyCapabilities=org.hl7.fhir.r5.model.TerminologyCapabilities
resource.TestReport=org.hl7.fhir.r5.model.TestReport
resource.TestScript=org.hl7.fhir.r5.model.TestScript
resource.ValueSet=org.hl7.fhir.r5.model.ValueSet
resource.VerificationResult=org.hl7.fhir.r5.model.VerificationResult
resource.VisionPrescription=org.hl7.fhir.r5.model.VisionPrescription
datatype.Address=org.hl7.fhir.r5.model.Address
datatype.Age=org.hl7.fhir.r5.model.Age
datatype.Annotation=org.hl7.fhir.r5.model.Annotation
datatype.Attachment=org.hl7.fhir.r5.model.Attachment
datatype.BackboneElement=org.hl7.fhir.r5.model.BackboneType
datatype.CodeableConcept=org.hl7.fhir.r5.model.CodeableConcept
datatype.Coding=org.hl7.fhir.r5.model.Coding
datatype.ContactDetail=org.hl7.fhir.r5.model.ContactDetail
datatype.ContactPoint=org.hl7.fhir.r5.model.ContactPoint
datatype.Contributor=org.hl7.fhir.r5.model.Contributor
datatype.Count=org.hl7.fhir.r5.model.Count
datatype.DataRequirement=org.hl7.fhir.r5.model.DataRequirement
datatype.Distance=org.hl7.fhir.r5.model.Distance
datatype.Dosage=org.hl7.fhir.r5.model.Dosage
datatype.Duration=org.hl7.fhir.r5.model.Duration
datatype.ElementDefinition=org.hl7.fhir.r5.model.ElementDefinition
datatype.Expression=org.hl7.fhir.r5.model.Expression
datatype.Extension=org.hl7.fhir.r5.model.Extension
datatype.HumanName=org.hl7.fhir.r5.model.HumanName
datatype.Identifier=org.hl7.fhir.r5.model.Identifier
datatype.MarketingStatus=org.hl7.fhir.r5.model.MarketingStatus
datatype.Meta=org.hl7.fhir.r5.model.Meta
datatype.Money=org.hl7.fhir.r5.model.Money
datatype.MoneyQuantity=org.hl7.fhir.r5.model.MoneyQuantity
datatype.Narrative=org.hl7.fhir.r5.model.Narrative
datatype.ParameterDefinition=org.hl7.fhir.r5.model.ParameterDefinition
datatype.Period=org.hl7.fhir.r5.model.Period
datatype.Population=org.hl7.fhir.r5.model.Population
datatype.ProdCharacteristic=org.hl7.fhir.r5.model.ProdCharacteristic
datatype.ProductShelfLife=org.hl7.fhir.r5.model.ProductShelfLife
datatype.Quantity=org.hl7.fhir.r5.model.Quantity
datatype.Range=org.hl7.fhir.r5.model.Range
datatype.Ratio=org.hl7.fhir.r5.model.Ratio
datatype.Reference=org.hl7.fhir.r5.model.Reference
datatype.RelatedArtifact=org.hl7.fhir.r5.model.RelatedArtifact
datatype.SampledData=org.hl7.fhir.r5.model.SampledData
datatype.Signature=org.hl7.fhir.r5.model.Signature
datatype.SimpleQuantity=org.hl7.fhir.r5.model.SimpleQuantity
datatype.SubstanceAmount=org.hl7.fhir.r5.model.SubstanceAmount
datatype.Timing=org.hl7.fhir.r5.model.Timing
datatype.TriggerDefinition=org.hl7.fhir.r5.model.TriggerDefinition
datatype.UsageContext=org.hl7.fhir.r5.model.UsageContext
datatype.base64Binary=org.hl7.fhir.r5.model.Base64BinaryType
datatype.boolean=org.hl7.fhir.r5.model.BooleanType
datatype.canonical=org.hl7.fhir.r5.model.CanonicalType
datatype.code=org.hl7.fhir.r5.model.CodeType
datatype.code.2=org.hl7.fhir.r5.model.Enumeration
datatype.date=org.hl7.fhir.r5.model.DateType
datatype.dateTime=org.hl7.fhir.r5.model.DateTimeType
datatype.decimal=org.hl7.fhir.r5.model.DecimalType
datatype.id=org.hl7.fhir.r5.model.IdType
datatype.instant=org.hl7.fhir.r5.model.InstantType
datatype.integer=org.hl7.fhir.r5.model.IntegerType
datatype.markdown=org.hl7.fhir.r5.model.MarkdownType
datatype.oid=org.hl7.fhir.r5.model.OidType
datatype.positiveInt=org.hl7.fhir.r5.model.PositiveIntType
datatype.string=org.hl7.fhir.r5.model.StringType
datatype.time=org.hl7.fhir.r5.model.TimeType
datatype.unsignedInt=org.hl7.fhir.r5.model.UnsignedIntType
datatype.uri=org.hl7.fhir.r5.model.UriType
datatype.url=org.hl7.fhir.r5.model.UrlType
datatype.xhtml=org.hl7.fhir.utilities.xhtml.XhtmlNode

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.rest.client;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
@ -30,9 +30,10 @@ import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Bundle.BundleType;
import org.hl7.fhir.r5.model.Bundle.HTTPVerb;
import org.junit.*;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
@ -43,10 +44,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -101,40 +102,11 @@ public class GenericClientTest {
}
private String extractBody(ArgumentCaptor<HttpUriRequest> capt, int count) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(count)).getEntity().getContent(), "UTF-8");
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(count)).getEntity().getContent(), StandardCharsets.UTF_8);
return body;
}
private String getPatientFeedWithOneResult() {
return ClientR4Test.getPatientFeedWithOneResult(ourCtx);
// //@formatter:off
// String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
// "<title/>\n" +
// "<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" +
// "<os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults>\n" +
// "<author>\n" +
// "<name>ca.uhn.fhir.rest.server.DummyRestfulServer</name>\n" +
// "</author>\n" +
// "<entry>\n" +
// "<content type=\"text/xml\">"
// + "<Patient xmlns=\"http://hl7.org/fhir\">"
// + "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
// + "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
// + "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
// + "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
// + "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
// + "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
// + "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
// + "</Patient>"
// + "</content>\n"
// + " </entry>\n"
// + "</feed>";
// //@formatter:on
// return msg;
}
private String getResourceResult() {
//@formatter:off
String msg =
"<Patient xmlns=\"http://hl7.org/fhir\">"
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
@ -145,7 +117,6 @@ public class GenericClientTest {
+ "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
+ "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
+ "</Patient>";
//@formatter:on
return msg;
}
@ -159,7 +130,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -184,7 +155,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -209,7 +180,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -231,9 +202,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -258,9 +229,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -296,7 +267,6 @@ public class GenericClientTest {
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
assertThat(extractBody(capt, count), containsString("value=\"John\""));
count++;
}
@ -309,9 +279,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -331,7 +301,7 @@ public class GenericClientTest {
/*
* Try fluent options
*/
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
client.create().resource(p1).execute();
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(1).getURI().toString());
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
@ -339,7 +309,7 @@ public class GenericClientTest {
count++;
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
client.create().resource(resourceText).execute();
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(2).getURI().toString());
assertEquals(resourceText, IOUtils.toString(((HttpPost) capt.getAllValues().get(2)).getEntity().getContent()));
@ -357,9 +327,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -386,9 +356,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -410,9 +380,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ooStr), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ooStr), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -421,16 +391,16 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient/123", capt.getValue().getURI().toString());
assertEquals("DELETE", capt.getValue().getMethod());
assertEquals("testDelete01", outcome.getIssueFirstRep().getLocation().get(0).getValue());
Assert.assertEquals("testDelete01", outcome.getIssueFirstRep().getLocation().get(0).getValue());
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("LKJHLKJGLKJKLL"), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("LKJHLKJGLKJKLL"), StandardCharsets.UTF_8));
outcome = (OperationOutcome) client.delete().resourceById(new IdType("Location", "123", "456")).prettyPrint().encodedJson().execute();
assertEquals("http://example.com/fhir/Location/123?_pretty=true", capt.getAllValues().get(1).getURI().toString());
assertEquals("DELETE", capt.getValue().getMethod());
assertEquals(null, outcome);
Assert.assertEquals(null, outcome);
}
@ -446,7 +416,7 @@ public class GenericClientTest {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
return new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8);
}
});
@ -463,7 +433,7 @@ public class GenericClientTest {
.execute();
assertEquals("http://example.com/fhir/_history", capt.getAllValues().get(idx).getURI().toString());
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
assertEquals(1, response.getEntry().size());
Assert.assertEquals(1, response.getEntry().size());
idx++;
response = client
@ -474,7 +444,7 @@ public class GenericClientTest {
.withAdditionalHeader("myHeaderName", "myHeaderValue2")
.execute();
assertEquals("http://example.com/fhir/Patient/_history", capt.getAllValues().get(idx).getURI().toString());
assertEquals(1, response.getEntry().size());
Assert.assertEquals(1, response.getEntry().size());
assertEquals("myHeaderValue1", capt.getValue().getHeaders("myHeaderName")[0].getValue());
assertEquals("myHeaderValue2", capt.getValue().getHeaders("myHeaderName")[1].getValue());
idx++;
@ -485,7 +455,7 @@ public class GenericClientTest {
.andReturnBundle(Bundle.class)
.execute();
assertEquals("http://example.com/fhir/Patient/123/_history", capt.getAllValues().get(idx).getURI().toString());
assertEquals(1, response.getEntry().size());
Assert.assertEquals(1, response.getEntry().size());
idx++;
}
@ -517,7 +487,7 @@ public class GenericClientTest {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
@ -536,12 +506,12 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return (new ReaderInputStream(new StringReader(getPatientFeedWithOneResult()), Charset.forName("UTF-8")));
return (new ReaderInputStream(new StringReader(getPatientFeedWithOneResult()), StandardCharsets.UTF_8));
}
});
@ -566,7 +536,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(respString), StandardCharsets.UTF_8));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[0]);
Bundle bundle = new Bundle();
@ -596,8 +566,8 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] {
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
Header[] headers = new Header[]{
new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
};
@ -613,11 +583,11 @@ public class GenericClientTest {
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://foo.com/Patient/123/_history/2333", response.getIdElement().getValue());
Assert.assertEquals("http://foo.com/Patient/123/_history/2333", response.getIdElement().getValue());
InstantType lm = response.getMeta().getLastUpdatedElement();
lm.setTimeZoneZulu(true);
assertEquals("1995-11-15T04:58:08.000Z", lm.getValueAsString());
Assert.assertEquals("1995-11-15T04:58:08.000Z", lm.getValueAsString());
}
@ -630,8 +600,8 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] {
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
Header[] headers = new Header[]{
new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
};
@ -645,22 +615,22 @@ public class GenericClientTest {
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://example.com/fhir/Patient/1234", capt.getAllValues().get(count++).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = (Patient) client.read().resource("Patient").withId("1234").execute();
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://example.com/fhir/Patient/1234", capt.getAllValues().get(count++).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = (Patient) client.read().resource("Patient").withId(567L).execute();
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://example.com/fhir/Patient/567", capt.getAllValues().get(count++).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = client.read().resource(Patient.class).withIdAndVersion("1234", "22").execute();
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://example.com/fhir/Patient/1234/_history/22", capt.getAllValues().get(count++).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = client.read().resource(Patient.class).withUrl("http://foo/Patient/22").execute();
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://foo/Patient/22", capt.getAllValues().get(count++).getURI().toString());
@ -676,8 +646,8 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] {new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
Header[] headers = new Header[]{new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
};
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
@ -692,7 +662,7 @@ public class GenericClientTest {
assertThat(response.getNameFirstRep().getFamily(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234", capt.getAllValues().get(0).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = client
.read()
.resource(Patient.class)
@ -713,7 +683,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -737,7 +707,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -767,7 +737,7 @@ public class GenericClientTest {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
@ -784,7 +754,7 @@ public class GenericClientTest {
ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals("PRP1660", BundleUtil.toListOfResourcesOfType(ourCtx, response, Patient.class).get(0).getIdentifier().get(0).getValue());
Assert.assertEquals("PRP1660", BundleUtil.toListOfResourcesOfType(ourCtx, response, Patient.class).get(0).getIdentifier().get(0).getValue());
try {
client
@ -811,7 +781,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://foo");
@ -823,7 +793,7 @@ public class GenericClientTest {
.returnBundle(Bundle.class)
.execute();
assertEquals("http://foo/Observation?" + Observation.SP_CODE_VALUE_DATE + "=" + UrlUtil.escapeUrlParam("FOO\\$BAR$2001-01-01"), capt.getValue().getURI().toString());
Assert.assertEquals("http://foo/Observation?" + Observation.SP_CODE_VALUE_DATE + "=" + UrlUtil.escapeUrlParam("FOO\\$BAR$2001-01-01"), capt.getValue().getURI().toString());
}
@ -840,7 +810,7 @@ public class GenericClientTest {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
return new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8);
}
});
@ -902,7 +872,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -925,7 +895,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -950,7 +920,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -974,7 +944,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -998,7 +968,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1022,7 +992,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1047,7 +1017,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1059,7 +1029,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?name=james", capt.getValue().getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().values("AAA", "BBB", "C,C"))
@ -1080,7 +1050,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(t-> new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenAnswer(t -> new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1123,7 +1093,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1147,7 +1117,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1174,7 +1144,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1188,7 +1158,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?identifier=http%3A%2F%2Fexample.com%2Ffhir%7CZZZ", capt.getValue().getURI().toString());
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = client.search()
.forResource("Patient")
.where(Patient.IDENTIFIER.exactly().code("ZZZ"))
@ -1197,7 +1167,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?identifier=ZZZ", capt.getAllValues().get(1).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
response = client.search()
.forResource("Patient")
.where(Patient.IDENTIFIER.exactly().codings(new Coding("A", "B", "ZZZ"), new Coding("C", "D", "ZZZ")))
@ -1223,7 +1193,7 @@ public class GenericClientTest {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
return new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8);
}
});
@ -1269,7 +1239,7 @@ public class GenericClientTest {
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
return new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8);
}
});
@ -1309,7 +1279,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1317,7 +1287,7 @@ public class GenericClientTest {
.forResource(Patient.class)
.include(Patient.INCLUDE_ORGANIZATION)
.include(Patient.INCLUDE_LINK.asRecursive())
.include(Patient.INCLUDE_ALL.asNonRecursive())
.include(IBaseResource.INCLUDE_ALL.asNonRecursive())
.returnBundle(Bundle.class)
.execute();
@ -1338,7 +1308,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1362,7 +1332,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1391,7 +1361,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1401,7 +1371,7 @@ public class GenericClientTest {
.returnBundle(Bundle.class)
.execute();
assertEquals(1, response.getEntry().size());
Assert.assertEquals(1, response.getEntry().size());
}
@SuppressWarnings("unused")
@ -1414,7 +1384,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
client.setPrettyPrint(true);
@ -1439,7 +1409,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1466,7 +1436,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, "INTERNAL ERRORS"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Server Issues!"), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Server Issues!"), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1494,7 +1464,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Server Issues!"), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Server Issues!"), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1517,7 +1487,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1541,7 +1511,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
// Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08
// GMT"),
// new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
@ -1575,7 +1545,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1584,7 +1554,7 @@ public class GenericClientTest {
.execute();
assertEquals("http://example.com/fhir", capt.getValue().getURI().toString());
assertEquals(input.getEntry().get(0).getResource().getId(), response.getEntry().get(0).getResource().getId());
Assert.assertEquals(input.getEntry().get(0).getResource().getId(), response.getEntry().get(0).getResource().getId());
assertEquals(EncodingEnum.JSON.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
}
@ -1600,7 +1570,7 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1610,7 +1580,7 @@ public class GenericClientTest {
.execute();
assertEquals("http://example.com/fhir", capt.getValue().getURI().toString());
assertEquals(input.getEntry().get(0).getResource().getId(), response.getEntry().get(0).getResource().getId());
Assert.assertEquals(input.getEntry().get(0).getResource().getId(), response.getEntry().get(0).getResource().getId());
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
}
@ -1625,9 +1595,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1662,13 +1632,13 @@ public class GenericClientTest {
/*
* Try fluent options
*/
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
client.update().resource(p1).withId("123").execute();
assertEquals(3, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(2).getURI().toString());
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
client.update().resource(resourceText).withId("123").execute();
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(3).getURI().toString());
assertEquals(resourceText, IOUtils.toString(((HttpPut) capt.getAllValues().get(3)).getEntity().getContent()));
@ -1686,9 +1656,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), StandardCharsets.UTF_8));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1731,8 +1701,8 @@ public class GenericClientTest {
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] {
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), StandardCharsets.UTF_8));
Header[] headers = new Header[]{
new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
};
@ -1759,9 +1729,9 @@ public class GenericClientTest {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] {});
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ourCtx.newXmlParser().encodeResourceToString(oo)), Charset.forName("UTF-8")));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ourCtx.newXmlParser().encodeResourceToString(oo)), StandardCharsets.UTF_8));
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@ -1773,10 +1743,22 @@ public class GenericClientTest {
MethodOutcome resp = client.validate(p1);
assertEquals("http://example.com/fhir/Patient/$validate", capt.getValue().getURI().toString());
oo = (OperationOutcome) resp.getOperationOutcome();
assertEquals("OOOK", oo.getIssueFirstRep().getDiagnostics());
Assert.assertEquals("OOOK", oo.getIssueFirstRep().getDiagnostics());
}
private String getPatientFeedWithOneResult() {
Bundle retVal = new Bundle();
Patient p = new Patient();
p.addName().setFamily("Cardinal").addGiven("John");
p.addIdentifier().setValue("PRP1660");
retVal.addEntry().setResource(p);
return ourCtx.newXmlParser().encodeResourceToString(retVal);
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
@ -1784,7 +1766,7 @@ public class GenericClientTest {
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forR4();
ourCtx = FhirContext.forR5();
}
}

View File

@ -0,0 +1,144 @@
package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
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.r5.model.HumanName;
import org.hl7.fhir.r5.model.Patient;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class SearchR5Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchR5Test.class);
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forR5();
private static TokenAndListParam ourIdentifiers;
private static String ourLastMethod;
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastMethod = null;
ourIdentifiers = null;
}
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_pretty=true");
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
// validate(ourCtx.newJsonParser().parseResource(responseContent));
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("search", ourLastMethod);
assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem());
assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue());
}
}
private void validate(IBaseResource theResource) {
FhirValidator validatorModule = ourCtx.newValidator();
ValidationResult result = validatorModule.validateWithResult(theResource);
if (!result.isSuccessful()) {
fail(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()));
}
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@SuppressWarnings("rawtypes")
@Search()
public List search(
@RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
ourLastMethod = "search";
ourIdentifiers = theIdentifiers;
ArrayList<Patient> retVal = new ArrayList<>();
for (int i = 0; i < 200; i++) {
Patient patient = new Patient();
patient.getIdElement().setValue("Patient/" + i + "/_history/222");
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(patient, BundleEntrySearchModeEnum.INCLUDE.getCode());
patient.addName(new HumanName().setFamily("FAMILY"));
patient.setActive(true);
retVal.add(patient);
}
return retVal;
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
JettyUtil.closeServer(ourServer);
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourServer = new Server(0);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setDefaultResponseEncoding(EncodingEnum.JSON);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
JettyUtil.startServer(ourServer);
ourPort = JettyUtil.getPortForStartedServer(ourServer);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
}

View File

@ -0,0 +1,30 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.eclipse" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.apache" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.thymeleaf" additivity="false" level="warn">
<appender-ref ref="STDOUT" />
</logger>
<!--
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
<appender-ref ref="STDOUT" />
</logger>
-->
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,53 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-validation-resources-r5</artifactId>
<packaging>bundle</packaging>
<name>HAPI FHIR - Validation Resources (FHIR R5)</name>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Fragment-Host>
ca.uhn.hapi.fhir.hapi-fhir-base
</Fragment-Host>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>

Some files were not shown because too many files have changed in this diff Show More