Support GraphQL for R3/4/5 (#1424)
* Work on grpahql enhanbcements * Add some more chars to the sanitizer function * Add changelog
This commit is contained in:
parent
2999a292e6
commit
0c9e5ec1ea
|
@ -163,7 +163,16 @@ public class UrlUtil {
|
||||||
if (theString != null) {
|
if (theString != null) {
|
||||||
for (int i = 0; i < theString.length(); i++) {
|
for (int i = 0; i < theString.length(); i++) {
|
||||||
char nextChar = theString.charAt(i);
|
char nextChar = theString.charAt(i);
|
||||||
if (nextChar == '<' || nextChar == '"') {
|
switch (nextChar) {
|
||||||
|
case '\'':
|
||||||
|
case '"':
|
||||||
|
case '<':
|
||||||
|
case '>':
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (nextChar < ' ') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,7 +357,17 @@ public class UrlUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method specifically HTML-encodes the " and
|
* This method specifically HTML-encodes the " and
|
||||||
* < characters in order to prevent injection attacks
|
* < characters in order to prevent injection attacks.
|
||||||
|
*
|
||||||
|
* The following characters are escaped:
|
||||||
|
* <ul>
|
||||||
|
* <li>'</li>
|
||||||
|
* <li>"</li>
|
||||||
|
* <li><</li>
|
||||||
|
* <li>></li>
|
||||||
|
* <li>\n (newline)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public static String sanitizeUrlPart(CharSequence theString) {
|
public static String sanitizeUrlPart(CharSequence theString) {
|
||||||
if (theString == null) {
|
if (theString == null) {
|
||||||
|
@ -364,6 +383,10 @@ public class UrlUtil {
|
||||||
|
|
||||||
char nextChar = theString.charAt(j);
|
char nextChar = theString.charAt(j);
|
||||||
switch (nextChar) {
|
switch (nextChar) {
|
||||||
|
/*
|
||||||
|
* NB: If you add a constant here, you also need to add it
|
||||||
|
* to isNeedsSanitization()!!
|
||||||
|
*/
|
||||||
case '\'':
|
case '\'':
|
||||||
buffer.append("'");
|
buffer.append("'");
|
||||||
break;
|
break;
|
||||||
|
@ -373,8 +396,19 @@ public class UrlUtil {
|
||||||
case '<':
|
case '<':
|
||||||
buffer.append("<");
|
buffer.append("<");
|
||||||
break;
|
break;
|
||||||
|
case '>':
|
||||||
|
buffer.append(">");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
buffer.append(" ");
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
buffer.append(" ");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
buffer.append(nextChar);
|
if (nextChar >= ' ') {
|
||||||
|
buffer.append(nextChar);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,4 +59,15 @@ public class UrlUtilTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSanitize() {
|
||||||
|
assertEquals(" ' ", UrlUtil.sanitizeUrlPart(" ' "));
|
||||||
|
assertEquals(" < ", UrlUtil.sanitizeUrlPart(" < "));
|
||||||
|
assertEquals(" > ", UrlUtil.sanitizeUrlPart(" > "));
|
||||||
|
assertEquals(" " ", UrlUtil.sanitizeUrlPart(" \" "));
|
||||||
|
assertEquals(" ", UrlUtil.sanitizeUrlPart(" \n "));
|
||||||
|
assertEquals(" ", UrlUtil.sanitizeUrlPart(" \r "));
|
||||||
|
assertEquals(" ", UrlUtil.sanitizeUrlPart(" \0 "));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,8 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
||||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.executor.InterceptorService;
|
||||||
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
|
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
|
||||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||||
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
||||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
|
@ -23,6 +24,7 @@ import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
||||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||||
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.*;
|
import org.springframework.context.annotation.*;
|
||||||
|
@ -109,6 +111,12 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
||||||
|
|
||||||
public abstract FhirContext fhirContext();
|
public abstract FhirContext fhirContext();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Lazy
|
||||||
|
public IGraphQLStorageServices graphqlStorageServices() {
|
||||||
|
return new JpaStorageServices();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ScheduledExecutorFactoryBean scheduledExecutorService() {
|
public ScheduledExecutorFactoryBean scheduledExecutorService() {
|
||||||
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
|
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
|
||||||
|
|
|
@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3;
|
import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3;
|
||||||
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
|
@ -73,6 +74,12 @@ public class BaseDstu3Config extends BaseConfig {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = GRAPHQL_PROVIDER_NAME)
|
||||||
|
@Lazy
|
||||||
|
public GraphQLProvider graphQLProvider() {
|
||||||
|
return new GraphQLProvider(fhirContextDstu3(), validationSupportChainDstu3(), graphqlStorageServices());
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public TransactionProcessor.ITransactionProcessorVersionAdapter transactionProcessorVersionFacade() {
|
public TransactionProcessor.ITransactionProcessorVersionAdapter transactionProcessorVersionFacade() {
|
||||||
return new TransactionProcessorVersionAdapterDstu3();
|
return new TransactionProcessorVersionAdapterDstu3();
|
||||||
|
|
|
@ -21,12 +21,13 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import org.hl7.fhir.r4.hapi.validation.CachingValidationSupport;
|
import org.hl7.fhir.r4.hapi.validation.CachingValidationSupport;
|
||||||
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
|
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
||||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||||
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -91,12 +92,6 @@ public class BaseR4Config extends BaseConfig {
|
||||||
return new GraphQLProvider(fhirContextR4(), validationSupportChainR4(), graphqlStorageServices());
|
return new GraphQLProvider(fhirContextR4(), validationSupportChainR4(), graphqlStorageServices());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Lazy
|
|
||||||
public GraphQLEngine.IGraphQLStorageServices graphqlStorageServices() {
|
|
||||||
return new JpaStorageServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "myInstanceValidatorR4")
|
@Bean(name = "myInstanceValidatorR4")
|
||||||
@Lazy
|
@Lazy
|
||||||
public IValidatorModule instanceValidatorR4() {
|
public IValidatorModule instanceValidatorR4() {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
||||||
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
|
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
|
||||||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR5;
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR5;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR5;
|
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryR5;
|
||||||
|
@ -21,11 +21,9 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR5;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hl7.fhir.r5.hapi.ctx.IValidationSupport;
|
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.CachingValidationSupport;
|
||||||
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
|
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.r5.model.Bundle;
|
import org.hl7.fhir.r5.model.Bundle;
|
||||||
import org.hl7.fhir.r5.utils.GraphQLEngine;
|
|
||||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
@ -85,17 +83,11 @@ public class BaseR5Config extends BaseConfig {
|
||||||
return new TransactionProcessor<>();
|
return new TransactionProcessor<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Bean(name = GRAPHQL_PROVIDER_NAME)
|
@Bean(name = GRAPHQL_PROVIDER_NAME)
|
||||||
// @Lazy
|
@Lazy
|
||||||
// public GraphQLProvider graphQLProvider() {
|
public GraphQLProvider graphQLProvider() {
|
||||||
// return new GraphQLProvider(fhirContextR5(), validationSupportChainR5(), graphqlStorageServices());
|
return new GraphQLProvider(fhirContextR5(), validationSupportChainR5(), graphqlStorageServices());
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// @Bean
|
|
||||||
// @Lazy
|
|
||||||
// public GraphQLEngine.IGraphQLStorageServices graphqlStorageServices() {
|
|
||||||
// return new JpaStorageServices();
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Bean(name = "myInstanceValidatorR5")
|
@Bean(name = "myInstanceValidatorR5")
|
||||||
@Lazy
|
@Lazy
|
||||||
|
|
|
@ -24,7 +24,6 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
@ -33,22 +32,21 @@ import ca.uhn.fhir.rest.param.*;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
|
||||||
import org.hl7.fhir.r4.model.Resource;
|
|
||||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
|
||||||
import org.hl7.fhir.utilities.graphql.Argument;
|
import org.hl7.fhir.utilities.graphql.Argument;
|
||||||
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
import org.hl7.fhir.utilities.graphql.Value;
|
import org.hl7.fhir.utilities.graphql.Value;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implements GraphQLEngine.IGraphQLStorageServices {
|
public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implements IGraphQLStorageServices {
|
||||||
|
|
||||||
|
private static final int MAX_SEARCH_SIZE = 500;
|
||||||
|
|
||||||
private IFhirResourceDao<? extends IBaseResource> getDao(String theResourceType) {
|
private IFhirResourceDao<? extends IBaseResource> getDao(String theResourceType) {
|
||||||
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theResourceType);
|
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theResourceType);
|
||||||
|
@ -57,13 +55,13 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.NEVER)
|
@Transactional(propagation = Propagation.NEVER)
|
||||||
@Override
|
@Override
|
||||||
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<Resource> theMatches) throws FHIRException {
|
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<IBaseResource> theMatches) throws FHIRException {
|
||||||
|
|
||||||
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theType);
|
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theType);
|
||||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(typeDef.getImplementingClass());
|
IFhirResourceDao<? extends IBaseResource> dao = getDao(typeDef.getImplementingClass());
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
params.setLoadSynchronousUpTo(500);
|
params.setLoadSynchronousUpTo(MAX_SEARCH_SIZE);
|
||||||
|
|
||||||
for (Argument nextArgument : theSearchParams) {
|
for (Argument nextArgument : theSearchParams) {
|
||||||
|
|
||||||
|
@ -114,40 +112,37 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
||||||
size = response.preferredPageSize();
|
size = response.preferredPageSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IBaseResource next : response.getResources(0, size)) {
|
theMatches.addAll(response.getResources(0, size));
|
||||||
theMatches.add((Resource) next);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
@Override
|
@Override
|
||||||
public Resource lookup(Object theAppInfo, String theType, String theId) throws FHIRException {
|
public IBaseResource lookup(Object theAppInfo, String theType, String theId) throws FHIRException {
|
||||||
IIdType refId = getContext().getVersion().newIdType();
|
IIdType refId = getContext().getVersion().newIdType();
|
||||||
refId.setValue(theType + "/" + theId);
|
refId.setValue(theType + "/" + theId);
|
||||||
return lookup(theAppInfo, refId);
|
return lookup(theAppInfo, refId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Resource lookup(Object theAppInfo, IIdType theRefId) {
|
private IBaseResource lookup(Object theAppInfo, IIdType theRefId) {
|
||||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(theRefId.getResourceType());
|
IFhirResourceDao<? extends IBaseResource> dao = getDao(theRefId.getResourceType());
|
||||||
RequestDetails requestDetails = (RequestDetails) theAppInfo;
|
RequestDetails requestDetails = (RequestDetails) theAppInfo;
|
||||||
return (Resource) dao.read(theRefId, requestDetails, false);
|
return dao.read(theRefId, requestDetails, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRED)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
@Override
|
@Override
|
||||||
public ReferenceResolution lookup(Object theAppInfo, Resource theContext, Reference theReference) throws FHIRException {
|
public ReferenceResolution lookup(Object theAppInfo, IBaseResource theContext, IBaseReference theReference) throws FHIRException {
|
||||||
IdType refId = new IdType(theReference.getReference());
|
IBaseResource outcome = lookup(theAppInfo, theReference.getReferenceElement());
|
||||||
Resource outcome = lookup(theAppInfo, refId);
|
|
||||||
if (outcome == null) {
|
if (outcome == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new ReferenceResolution(theContext, outcome);
|
return new ReferenceResolution(theContext, outcome);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.NEVER)
|
@Transactional(propagation = Propagation.NEVER)
|
||||||
@Override
|
@Override
|
||||||
public Bundle search(Object theAppInfo, String theType, List<Argument> theSearchParams) throws FHIRException {
|
public IBaseBundle search(Object theAppInfo, String theType, List<Argument> theSearchParams) throws FHIRException {
|
||||||
throw new NotImplementedOperationException("Not yet able to handle this GraphQL request");
|
throw new NotImplementedOperationException("Not yet able to handle this GraphQL request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%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.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||||
|
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.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport;
|
||||||
|
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.utilities.graphql.IGraphQLEngine;
|
||||||
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
|
import org.hl7.fhir.utilities.graphql.ObjectValue;
|
||||||
|
import org.hl7.fhir.utilities.graphql.Parser;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class GraphQLProvider {
|
||||||
|
private final Supplier<IGraphQLEngine> engineFactory;
|
||||||
|
private final IGraphQLStorageServices myStorageServices;
|
||||||
|
private Logger ourLog = LoggerFactory.getLogger(GraphQLProvider.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(IGraphQLStorageServices theStorageServices) {
|
||||||
|
this(FhirContext.forR4(), null, theStorageServices);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor which uses the given worker context
|
||||||
|
*
|
||||||
|
* @param theFhirContext The HAPI FHIR Context object
|
||||||
|
* @param theValidationSupport The HAPI Validation Support object, or null
|
||||||
|
* @param theStorageServices The storage services (this object will be used to retrieve various resources as required by the GraphQL engine)
|
||||||
|
*/
|
||||||
|
public GraphQLProvider(@Nonnull FhirContext theFhirContext, @Nullable IContextValidationSupport theValidationSupport, @Nonnull IGraphQLStorageServices theStorageServices) {
|
||||||
|
Validate.notNull(theFhirContext, "theFhirContext must not be null");
|
||||||
|
Validate.notNull(theStorageServices, "theStorageServices must not be null");
|
||||||
|
|
||||||
|
switch (theFhirContext.getVersion().getVersion()) {
|
||||||
|
case DSTU3: {
|
||||||
|
IValidationSupport validationSupport = (IValidationSupport) theValidationSupport;
|
||||||
|
validationSupport = ObjectUtils.defaultIfNull(validationSupport, new org.hl7.fhir.dstu3.hapi.ctx.DefaultProfileValidationSupport());
|
||||||
|
org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport);
|
||||||
|
engineFactory = () -> new org.hl7.fhir.dstu3.utils.GraphQLEngine(workerContext);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case R4: {
|
||||||
|
org.hl7.fhir.r4.hapi.ctx.IValidationSupport validationSupport = (org.hl7.fhir.r4.hapi.ctx.IValidationSupport) theValidationSupport;
|
||||||
|
validationSupport = ObjectUtils.defaultIfNull(validationSupport, new org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport());
|
||||||
|
org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport);
|
||||||
|
engineFactory = () -> new org.hl7.fhir.r4.utils.GraphQLEngine(workerContext);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case R5: {
|
||||||
|
org.hl7.fhir.r5.hapi.ctx.IValidationSupport validationSupport = (org.hl7.fhir.r5.hapi.ctx.IValidationSupport) theValidationSupport;
|
||||||
|
validationSupport = ObjectUtils.defaultIfNull(validationSupport, new org.hl7.fhir.r5.hapi.ctx.DefaultProfileValidationSupport());
|
||||||
|
org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext workerContext = new org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext(theFhirContext, validationSupport);
|
||||||
|
engineFactory = () -> new org.hl7.fhir.r5.utils.GraphQLEngine(workerContext);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DSTU2:
|
||||||
|
case DSTU2_HL7ORG:
|
||||||
|
case DSTU2_1:
|
||||||
|
default: {
|
||||||
|
throw new UnsupportedOperationException("GraphQL not supported for version: " + theFhirContext.getVersion().getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
myStorageServices = theStorageServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GraphQL
|
||||||
|
public String processGraphQlRequet(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
|
||||||
|
|
||||||
|
IGraphQLEngine engine = engineFactory.get();
|
||||||
|
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) {
|
||||||
|
IBaseResource 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().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||||
|
throw new ConfigurationException("Can not use " + getClass().getName() + " provider on server with FHIR " + theServer.getFhirContext().getVersion().getVersion().name() + " context");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.rest.server;
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
|
@ -6,6 +6,10 @@ import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@ -18,11 +22,14 @@ import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.HumanName;
|
||||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
import org.hl7.fhir.utilities.graphql.Argument;
|
import org.hl7.fhir.utilities.graphql.Argument;
|
||||||
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
@ -34,9 +41,8 @@ import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
|
||||||
|
|
||||||
public class GraphQLR4ProviderTest {
|
public class GraphQLR4ProviderTest {
|
||||||
|
|
||||||
|
@ -216,9 +222,9 @@ public class GraphQLR4ProviderTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MyStorageServices implements GraphQLEngine.IGraphQLStorageServices {
|
private static class MyStorageServices implements IGraphQLStorageServices {
|
||||||
@Override
|
@Override
|
||||||
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<Resource> theMatches) throws FHIRException {
|
public void listResources(Object theAppInfo, String theType, List<Argument> theSearchParams, List<IBaseResource> theMatches) throws FHIRException {
|
||||||
ourLog.info("listResources of {} - {}", theType, theSearchParams);
|
ourLog.info("listResources of {} - {}", theType, theSearchParams);
|
||||||
|
|
||||||
if (theSearchParams.size() == 1) {
|
if (theSearchParams.size() == 1) {
|
||||||
|
@ -264,8 +270,8 @@ public class GraphQLR4ProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReferenceResolution lookup(Object theAppInfo, Resource theContext, Reference theReference) throws FHIRException {
|
public IGraphQLStorageServices.ReferenceResolution lookup(Object theAppInfo, IBaseResource theContext, IBaseReference theReference) throws FHIRException {
|
||||||
ourLog.info("lookup from {} to {}", theContext.getIdElement().getValue(), theReference.getReference());
|
ourLog.info("lookup from {} to {}", theContext.getIdElement().getValue(), theReference.getReferenceElement().getValue());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.provider.dstu3;
|
||||||
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||||
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
@ -97,6 +98,8 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
SubscriptionTriggeringProvider subscriptionTriggeringProvider = myAppCtx.getBean(SubscriptionTriggeringProvider.class);
|
SubscriptionTriggeringProvider subscriptionTriggeringProvider = myAppCtx.getBean(SubscriptionTriggeringProvider.class);
|
||||||
ourRestServer.registerProvider(subscriptionTriggeringProvider);
|
ourRestServer.registerProvider(subscriptionTriggeringProvider);
|
||||||
|
|
||||||
|
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||||
|
|
||||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(ourRestServer, mySystemDao, myDaoConfig);
|
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(ourRestServer, mySystemDao, myDaoConfig);
|
||||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||||
ourRestServer.setServerConformanceProvider(confProvider);
|
ourRestServer.setServerConformanceProvider(confProvider);
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class GraphQLProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
private Logger ourLog = LoggerFactory.getLogger(GraphQLProviderDstu3Test.class);
|
||||||
|
private IIdType myPatientId0;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInstanceSimpleRead() throws IOException {
|
||||||
|
initTestPatients();
|
||||||
|
|
||||||
|
String query = "{name{family,given}}";
|
||||||
|
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||||
|
|
||||||
|
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||||
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
ourLog.info(resp);
|
||||||
|
assertEquals(TestUtil.stripReturns("{\n" +
|
||||||
|
" \"name\":[{\n" +
|
||||||
|
" \"family\":\"FAM\",\n" +
|
||||||
|
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||||
|
" },{\n" +
|
||||||
|
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||||
|
" }]\n" +
|
||||||
|
"}"), TestUtil.stripReturns(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSystemSimpleSearch() throws IOException {
|
||||||
|
initTestPatients();
|
||||||
|
|
||||||
|
String query = "{PatientList(given:\"given\"){name{family,given}}}";
|
||||||
|
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||||
|
|
||||||
|
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||||
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
|
ourLog.info(resp);
|
||||||
|
assertEquals(TestUtil.stripReturns("{\n" +
|
||||||
|
" \"PatientList\":[{\n" +
|
||||||
|
" \"name\":[{\n" +
|
||||||
|
" \"family\":\"FAM\",\n" +
|
||||||
|
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||||
|
" },{\n" +
|
||||||
|
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||||
|
" }]\n" +
|
||||||
|
" },{\n" +
|
||||||
|
" \"name\":[{\n" +
|
||||||
|
" \"given\":[\"GivenOnlyB1\",\"GivenOnlyB2\"]\n" +
|
||||||
|
" }]\n" +
|
||||||
|
" }]\n" +
|
||||||
|
"}"), TestUtil.stripReturns(resp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTestPatients() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.addName()
|
||||||
|
.setFamily("FAM")
|
||||||
|
.addGiven("GIVEN1")
|
||||||
|
.addGiven("GIVEN2");
|
||||||
|
p.addName()
|
||||||
|
.addGiven("GivenOnly1")
|
||||||
|
.addGiven("GivenOnly2");
|
||||||
|
myPatientId0 = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
p = new Patient();
|
||||||
|
p.addName()
|
||||||
|
.addGiven("GivenOnlyB1")
|
||||||
|
.addGiven("GivenOnlyB2");
|
||||||
|
ourClient.create().resource(p).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
|
@ -71,7 +72,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
protected IGenericClient ourClient;
|
protected IGenericClient ourClient;
|
||||||
ResourceCountCache ourResourceCountsCache;
|
ResourceCountCache ourResourceCountsCache;
|
||||||
private TerminologyUploaderProvider myTerminologyUploaderProvider;
|
private TerminologyUploaderProvider myTerminologyUploaderProvider;
|
||||||
private Object ourGraphQLProvider;
|
|
||||||
private boolean ourRestHookSubscriptionInterceptorRequested;
|
private boolean ourRestHookSubscriptionInterceptorRequested;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -105,10 +105,10 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
||||||
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||||
|
|
||||||
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
|
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
|
||||||
ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
|
|
||||||
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
||||||
|
|
||||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider, ourGraphQLProvider);
|
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||||
|
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||||
|
|
||||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(ourRestServer, mySystemDao, myDaoConfig);
|
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(ourRestServer, mySystemDao, myDaoConfig);
|
||||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||||
|
|
|
@ -27,20 +27,17 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
String query = "{name{family,given}}";
|
String query = "{name{family,given}}";
|
||||||
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||||
|
|
||||||
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
|
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||||
try {
|
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
assertEquals(TestUtil.stripReturns("{\n" +
|
||||||
" \"name\":[{\n" +
|
" \"name\":[{\n" +
|
||||||
" \"family\":\"FAM\",\n" +
|
" \"family\":\"FAM\",\n" +
|
||||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||||
" },{\n" +
|
" },{\n" +
|
||||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||||
" }]\n" +
|
" }]\n" +
|
||||||
"}"));
|
"}"), TestUtil.stripReturns(resp));
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,8 +49,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
String query = "{PatientList(given:\"given\"){name{family,given}}}";
|
String query = "{PatientList(given:\"given\"){name{family,given}}}";
|
||||||
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||||
|
|
||||||
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
|
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||||
try {
|
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
assertEquals(TestUtil.stripReturns("{\n" +
|
assertEquals(TestUtil.stripReturns("{\n" +
|
||||||
|
@ -70,10 +66,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
" }]\n" +
|
" }]\n" +
|
||||||
" }]\n" +
|
" }]\n" +
|
||||||
"}"), TestUtil.stripReturns(resp));
|
"}"), TestUtil.stripReturns(resp));
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTestPatients() {
|
private void initTestPatients() {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
|
import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
|
||||||
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
|
@ -108,8 +109,7 @@ public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
|
||||||
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
||||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||||
|
|
||||||
// ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
|
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||||
// ourRestServer.registerProvider(ourGraphQLProvider);
|
|
||||||
|
|
||||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(ourRestServer, mySystemDao, myDaoConfig);
|
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(ourRestServer, mySystemDao, myDaoConfig);
|
||||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||||
|
|
|
@ -34,7 +34,7 @@ import ca.uhn.fhirtest.config.TestR4Config;
|
||||||
import ca.uhn.fhirtest.config.TestR5Config;
|
import ca.uhn.fhirtest.config.TestR5Config;
|
||||||
import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor;
|
import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
|
@ -127,6 +127,7 @@ public class TestRestfulServer extends RestfulServer {
|
||||||
confProvider.setImplementationDescription(implDesc);
|
confProvider.setImplementationDescription(implDesc);
|
||||||
setServerConformanceProvider(confProvider);
|
setServerConformanceProvider(confProvider);
|
||||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||||
|
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "R4": {
|
case "R4": {
|
||||||
|
@ -164,7 +165,7 @@ public class TestRestfulServer extends RestfulServer {
|
||||||
confProvider.setImplementationDescription(implDesc);
|
confProvider.setImplementationDescription(implDesc);
|
||||||
setServerConformanceProvider(confProvider);
|
setServerConformanceProvider(confProvider);
|
||||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||||
// providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -19,6 +19,11 @@
|
||||||
<artifactId>hapi-fhir-base</artifactId>
|
<artifactId>hapi-fhir-base</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>org.hl7.fhir.utilities</artifactId>
|
||||||
|
<version>${fhir_core_version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Server -->
|
<!-- Server -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
package org.hl7.fhir.r4.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.r4.context.IWorkerContext;
|
|
||||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
|
||||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
|
||||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
|
||||||
import org.hl7.fhir.r4.model.Resource;
|
|
||||||
import org.hl7.fhir.r4.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.forR4(), 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.R4) {
|
|
||||||
throw new ConfigurationException("Can not use " + getClass().getName() + " provider on server with FHIR " + theServer.getFhirContext().getVersion().getVersion().name() + " context");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import java.io.IOException;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
import static org.mockito.Matchers.any;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -33,8 +32,8 @@ public class GraphQLEngineTest {
|
||||||
return obs;
|
return obs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GraphQLEngine.IGraphQLStorageServices createStorageServices() throws FHIRException {
|
private IGraphQLStorageServices createStorageServices() throws FHIRException {
|
||||||
GraphQLEngine.IGraphQLStorageServices retVal = mock(GraphQLEngine.IGraphQLStorageServices.class);
|
IGraphQLStorageServices retVal = mock(IGraphQLStorageServices.class);
|
||||||
when(retVal.lookup(nullable(Object.class), nullable(Resource.class), nullable(Reference.class))).thenAnswer(new Answer<Object>() {
|
when(retVal.lookup(nullable(Object.class), nullable(Resource.class), nullable(Reference.class))).thenAnswer(new Answer<Object>() {
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) {
|
public Object answer(InvocationOnMock invocation) {
|
||||||
|
@ -46,7 +45,7 @@ public class GraphQLEngineTest {
|
||||||
if (reference.getReference().equalsIgnoreCase("Patient/123")) {
|
if (reference.getReference().equalsIgnoreCase("Patient/123")) {
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
p.getBirthDateElement().setValueAsString("2011-02-22");
|
p.getBirthDateElement().setValueAsString("2011-02-22");
|
||||||
return new GraphQLEngine.IGraphQLStorageServices.ReferenceResolution(context, p);
|
return new IGraphQLStorageServices.ReferenceResolution(context, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Not found!");
|
ourLog.info("Not found!");
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -88,16 +88,28 @@
|
||||||
</action>
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
Support for the new R5 draft resources has been added. This support includes the client,
|
Support for the new R5 draft resources has been added. This support includes the client,
|
||||||
|
<![CDATA[
|
||||||
|
<b>New Feature</b>:
|
||||||
server, and JPA server. Note that these definitions will change as the R5 standard is
|
server, and JPA server. Note that these definitions will change as the R5 standard is
|
||||||
modified until it is released, so use with caution!
|
modified until it is released, so use with caution!
|
||||||
|
]]>
|
||||||
</action>
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
<![CDATA[
|
||||||
|
<b>New Feature</b>:
|
||||||
A new interceptor called
|
A new interceptor called
|
||||||
<![CDATA[<code>ConsentInterceptor</code>]]> has been added. This interceptor allows
|
<code>ConsentInterceptor</code> has been added. This interceptor allows
|
||||||
JPA based servers to make appropriate consent decisions related to resources that
|
JPA based servers to make appropriate consent decisions related to resources that
|
||||||
and operations that are being returned. See
|
and operations that are being returned. See
|
||||||
<![CDATA[<a href="http://hapifhir.io/doc_rest_server_security.html">Server Security</a>]]>
|
<a href="http://hapifhir.io/doc_rest_server_security.html">Server Security</a>
|
||||||
for more information.
|
for more information.
|
||||||
|
]]>
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[
|
||||||
|
<b>New Feature</b>:
|
||||||
|
The JPA server now supports GraphQL for DSTU3 / R4 / R5 servers.
|
||||||
|
]]>
|
||||||
</action>
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
Several enhancements have been made to the <![CDATA[<code>AuthorizationInterceptor</code>]]>:
|
Several enhancements have been made to the <![CDATA[<code>AuthorizationInterceptor</code>]]>:
|
||||||
|
@ -105,7 +117,8 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li>The interceptor now registers against the <code>STORAGE_PRESHOW_RESOURCES</code> interceptor hook,
|
<li>The interceptor now registers against the <code>STORAGE_PRESHOW_RESOURCES</code> interceptor hook,
|
||||||
which allows it to successfully authorize JPA operations that don't actually return resource content,
|
which allows it to successfully authorize JPA operations that don't actually return resource content,
|
||||||
such as GraphQL responses.</li>
|
such as GraphQL responses, and resources that have been filtered using the <code>_elements</code>
|
||||||
|
parameter.</li>
|
||||||
<li>
|
<li>
|
||||||
</li>The rule list is now cached on a per-request basis, which should improve performance</ul>
|
</li>The rule list is now cached on a per-request basis, which should improve performance</ul>
|
||||||
]]>
|
]]>
|
||||||
|
|
Loading…
Reference in New Issue