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) {
|
||||
for (int i = 0; i < theString.length(); 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;
|
||||
}
|
||||
}
|
||||
|
@ -348,7 +357,17 @@ public class UrlUtil {
|
|||
|
||||
/**
|
||||
* 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) {
|
||||
if (theString == null) {
|
||||
|
@ -364,6 +383,10 @@ public class UrlUtil {
|
|||
|
||||
char nextChar = theString.charAt(j);
|
||||
switch (nextChar) {
|
||||
/*
|
||||
* NB: If you add a constant here, you also need to add it
|
||||
* to isNeedsSanitization()!!
|
||||
*/
|
||||
case '\'':
|
||||
buffer.append("'");
|
||||
break;
|
||||
|
@ -373,8 +396,19 @@ public class UrlUtil {
|
|||
case '<':
|
||||
buffer.append("<");
|
||||
break;
|
||||
case '>':
|
||||
buffer.append(">");
|
||||
break;
|
||||
case '\n':
|
||||
buffer.append(" ");
|
||||
break;
|
||||
case '\r':
|
||||
buffer.append(" ");
|
||||
break;
|
||||
default:
|
||||
buffer.append(nextChar);
|
||||
if (nextChar >= ' ') {
|
||||
buffer.append(nextChar);
|
||||
}
|
||||
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.server.ETagSupportEnum;
|
||||
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.interceptor.CorsInterceptor;
|
||||
import org.hl7.fhir.r4.hapi.rest.server.GraphQLProvider;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
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.BinaryStorageInterceptor;
|
||||
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.provider.SubscriptionTriggeringProvider;
|
||||
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.rest.server.interceptor.consent.IConsentContextServices;
|
||||
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.Autowired;
|
||||
import org.springframework.context.annotation.*;
|
||||
|
@ -109,6 +111,12 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
|||
|
||||
public abstract FhirContext fhirContext();
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public IGraphQLStorageServices graphqlStorageServices() {
|
||||
return new JpaStorageServices();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ScheduledExecutorFactoryBean scheduledExecutorService() {
|
||||
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.TransactionProcessor;
|
||||
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.searchparam.extractor.SearchParamExtractorDstu3;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
|
@ -73,6 +74,12 @@ public class BaseDstu3Config extends BaseConfig {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Bean(name = GRAPHQL_PROVIDER_NAME)
|
||||
@Lazy
|
||||
public GraphQLProvider graphQLProvider() {
|
||||
return new GraphQLProvider(fhirContextDstu3(), validationSupportChainDstu3(), graphqlStorageServices());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionProcessor.ITransactionProcessorVersionAdapter transactionProcessorVersionFacade() {
|
||||
return new TransactionProcessorVersionAdapterDstu3();
|
||||
|
|
|
@ -21,12 +21,13 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainR4;
|
|||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
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.FhirInstanceValidator;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -91,12 +92,6 @@ public class BaseR4Config extends BaseConfig {
|
|||
return new GraphQLProvider(fhirContextR4(), validationSupportChainR4(), graphqlStorageServices());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public GraphQLEngine.IGraphQLStorageServices graphqlStorageServices() {
|
||||
return new JpaStorageServices();
|
||||
}
|
||||
|
||||
@Bean(name = "myInstanceValidatorR4")
|
||||
@Lazy
|
||||
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.TransactionProcessor;
|
||||
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.registry.ISearchParamRegistry;
|
||||
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 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;
|
||||
|
@ -85,17 +83,11 @@ public class BaseR5Config extends BaseConfig {
|
|||
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 = GRAPHQL_PROVIDER_NAME)
|
||||
@Lazy
|
||||
public GraphQLProvider graphQLProvider() {
|
||||
return new GraphQLProvider(fhirContextR5(), validationSupportChainR5(), graphqlStorageServices());
|
||||
}
|
||||
|
||||
@Bean(name = "myInstanceValidatorR5")
|
||||
@Lazy
|
||||
|
|
|
@ -24,7 +24,6 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
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.model.api.IQueryParameterType;
|
||||
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.NotImplementedOperationException;
|
||||
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.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.IGraphQLStorageServices;
|
||||
import org.hl7.fhir.utilities.graphql.Value;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
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) {
|
||||
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(theResourceType);
|
||||
|
@ -57,13 +55,13 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
|||
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
@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);
|
||||
IFhirResourceDao<? extends IBaseResource> dao = getDao(typeDef.getImplementingClass());
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(500);
|
||||
params.setLoadSynchronousUpTo(MAX_SEARCH_SIZE);
|
||||
|
||||
for (Argument nextArgument : theSearchParams) {
|
||||
|
||||
|
@ -114,40 +112,37 @@ public class JpaStorageServices extends BaseHapiFhirDao<IBaseResource> implement
|
|||
size = response.preferredPageSize();
|
||||
}
|
||||
|
||||
for (IBaseResource next : response.getResources(0, size)) {
|
||||
theMatches.add((Resource) next);
|
||||
}
|
||||
theMatches.addAll(response.getResources(0, size));
|
||||
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@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();
|
||||
refId.setValue(theType + "/" + theId);
|
||||
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());
|
||||
RequestDetails requestDetails = (RequestDetails) theAppInfo;
|
||||
return (Resource) dao.read(theRefId, requestDetails, false);
|
||||
return dao.read(theRefId, requestDetails, false);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public ReferenceResolution lookup(Object theAppInfo, Resource theContext, Reference theReference) throws FHIRException {
|
||||
IdType refId = new IdType(theReference.getReference());
|
||||
Resource outcome = lookup(theAppInfo, refId);
|
||||
@Override
|
||||
public ReferenceResolution lookup(Object theAppInfo, IBaseResource theContext, IBaseReference theReference) throws FHIRException {
|
||||
IBaseResource outcome = lookup(theAppInfo, theReference.getReferenceElement());
|
||||
if (outcome == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return new ReferenceResolution(theContext, outcome);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
@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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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.EncodingEnum;
|
||||
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.UrlUtil;
|
||||
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.ServletHolder;
|
||||
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.r4.hapi.rest.server.GraphQLProvider;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
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.IGraphQLStorageServices;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -34,9 +41,8 @@ import java.util.List;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class GraphQLR4ProviderTest {
|
||||
|
||||
|
@ -216,9 +222,9 @@ public class GraphQLR4ProviderTest {
|
|||
|
||||
}
|
||||
|
||||
private static class MyStorageServices implements GraphQLEngine.IGraphQLStorageServices {
|
||||
private static class MyStorageServices implements IGraphQLStorageServices {
|
||||
@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);
|
||||
|
||||
if (theSearchParams.size() == 1) {
|
||||
|
@ -264,8 +270,8 @@ public class GraphQLR4ProviderTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ReferenceResolution lookup(Object theAppInfo, Resource theContext, Reference theReference) throws FHIRException {
|
||||
ourLog.info("lookup from {} to {}", theContext.getIdElement().getValue(), theReference.getReference());
|
||||
public IGraphQLStorageServices.ReferenceResolution lookup(Object theAppInfo, IBaseResource theContext, IBaseReference theReference) throws FHIRException {
|
||||
ourLog.info("lookup from {} to {}", theContext.getIdElement().getValue(), theReference.getReferenceElement().getValue());
|
||||
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.dao.data.ISearchDao;
|
||||
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.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
|
@ -97,6 +98,8 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
SubscriptionTriggeringProvider subscriptionTriggeringProvider = myAppCtx.getBean(SubscriptionTriggeringProvider.class);
|
||||
ourRestServer.registerProvider(subscriptionTriggeringProvider);
|
||||
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(ourRestServer, mySystemDao, myDaoConfig);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
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.data.ISearchDao;
|
||||
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.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
|
@ -71,7 +72,6 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
protected IGenericClient ourClient;
|
||||
ResourceCountCache ourResourceCountsCache;
|
||||
private TerminologyUploaderProvider myTerminologyUploaderProvider;
|
||||
private Object ourGraphQLProvider;
|
||||
private boolean ourRestHookSubscriptionInterceptorRequested;
|
||||
|
||||
@Autowired
|
||||
|
@ -105,10 +105,10 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
ourRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
|
||||
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class);
|
||||
ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
|
||||
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);
|
||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
|
|
|
@ -27,20 +27,17 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
String query = "{name{family,given}}";
|
||||
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(TestUtil.stripReturns(resp), TestUtil.stripReturns("{\n" +
|
||||
assertEquals(TestUtil.stripReturns("{\n" +
|
||||
" \"name\":[{\n" +
|
||||
" \"family\":\"FAM\",\n" +
|
||||
" \"given\":[\"GIVEN1\",\"GIVEN2\"]\n" +
|
||||
" },{\n" +
|
||||
" \"given\":[\"GivenOnly1\",\"GivenOnly2\"]\n" +
|
||||
" }]\n" +
|
||||
"}"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
"}"), TestUtil.stripReturns(resp));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,8 +49,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
String query = "{PatientList(given:\"given\"){name{family,given}}}";
|
||||
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
|
||||
try {
|
||||
try (CloseableHttpResponse response = ourHttpClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(resp);
|
||||
assertEquals(TestUtil.stripReturns("{\n" +
|
||||
|
@ -70,10 +66,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
|
|||
" }]\n" +
|
||||
" }]\n" +
|
||||
"}"), TestUtil.stripReturns(resp));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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.data.ISearchDao;
|
||||
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.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
|
@ -108,8 +109,7 @@ public abstract class BaseResourceProviderR5Test extends BaseJpaR5Test {
|
|||
myDaoRegistry = myAppCtx.getBean(DaoRegistry.class);
|
||||
ourRestServer.registerProviders(mySystemProvider, myTerminologyUploaderProvider);
|
||||
|
||||
// ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider");
|
||||
// ourRestServer.registerProvider(ourGraphQLProvider);
|
||||
ourRestServer.registerProvider(myAppCtx.getBean(GraphQLProvider.class));
|
||||
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(ourRestServer, mySystemDao, myDaoConfig);
|
||||
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.hapi.converters.server.VersionedApiConverterInterceptor;
|
||||
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.WebApplicationContext;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
@ -127,6 +127,7 @@ public class TestRestfulServer extends RestfulServer {
|
|||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||
break;
|
||||
}
|
||||
case "R4": {
|
||||
|
@ -164,7 +165,7 @@ public class TestRestfulServer extends RestfulServer {
|
|||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
providers.add(myAppCtx.getBean(TerminologyUploaderProvider.class));
|
||||
// providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||
providers.add(myAppCtx.getBean(GraphQLProvider.class));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
<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>
|
||||
|
||||
<!-- Server -->
|
||||
<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.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
@ -33,8 +32,8 @@ public class GraphQLEngineTest {
|
|||
return obs;
|
||||
}
|
||||
|
||||
private GraphQLEngine.IGraphQLStorageServices createStorageServices() throws FHIRException {
|
||||
GraphQLEngine.IGraphQLStorageServices retVal = mock(GraphQLEngine.IGraphQLStorageServices.class);
|
||||
private IGraphQLStorageServices createStorageServices() throws FHIRException {
|
||||
IGraphQLStorageServices retVal = mock(IGraphQLStorageServices.class);
|
||||
when(retVal.lookup(nullable(Object.class), nullable(Resource.class), nullable(Reference.class))).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) {
|
||||
|
@ -46,7 +45,7 @@ public class GraphQLEngineTest {
|
|||
if (reference.getReference().equalsIgnoreCase("Patient/123")) {
|
||||
Patient p = new Patient();
|
||||
p.getBirthDateElement().setValueAsString("2011-02-22");
|
||||
return new GraphQLEngine.IGraphQLStorageServices.ReferenceResolution(context, p);
|
||||
return new IGraphQLStorageServices.ReferenceResolution(context, p);
|
||||
}
|
||||
|
||||
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 type="add">
|
||||
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
|
||||
modified until it is released, so use with caution!
|
||||
]]>
|
||||
</action>
|
||||
<action type="add">
|
||||
<![CDATA[
|
||||
<b>New Feature</b>:
|
||||
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
|
||||
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.
|
||||
]]>
|
||||
</action>
|
||||
<action type="add">
|
||||
<![CDATA[
|
||||
<b>New Feature</b>:
|
||||
The JPA server now supports GraphQL for DSTU3 / R4 / R5 servers.
|
||||
]]>
|
||||
</action>
|
||||
<action type="add">
|
||||
Several enhancements have been made to the <![CDATA[<code>AuthorizationInterceptor</code>]]>:
|
||||
|
@ -105,7 +117,8 @@
|
|||
<ul>
|
||||
<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,
|
||||
such as GraphQL responses.</li>
|
||||
such as GraphQL responses, and resources that have been filtered using the <code>_elements</code>
|
||||
parameter.</li>
|
||||
<li>
|
||||
</li>The rule list is now cached on a per-request basis, which should improve performance</ul>
|
||||
]]>
|
||||
|
|
Loading…
Reference in New Issue