Add interceptor hook for graphql calls

This commit is contained in:
James Agnew 2019-08-13 14:46:31 -04:00
parent a1e1cf4e56
commit b4fece0ae9
7 changed files with 89 additions and 14 deletions

View File

@ -631,7 +631,10 @@ public class FhirContext {
* Performance Note: <b>This method is cheap</b> to call, and may be called once for every message being processed
* without incurring any performance penalty
* </p>
*
* @deprecated THIS FEATURE IS NOT YET COMPLETE
*/
@Deprecated
public IParser newRDFParser() {
return new RDFParser(this, myParserErrorHandler, Lang.TURTLE);
}

View File

@ -374,6 +374,57 @@ public enum Pointcut {
),
/**
* <b>Server Hook:</b>
* This method is called after the server implementation method has been called, but before any attempt
* to stream the response back to the client, specifically for GraphQL requests (as these do not fit
* cleanly into the model provided by {@link #SERVER_OUTGOING_RESPONSE}).
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request.
* </li>
* <li>
* ca.uhn.fhir.rest.server.servlet.ServletRequestDetails - A bean containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
* </li>
* <li>
* java.lang.String - The GraphQL query
* </li>
* <li>
* java.lang.String - The GraphQL response
* </li>
* <li>
* ca.uhn.fhir.rest.api.server.ResponseDetails - This object contains details about the response, including the contents. Hook methods may modify this object to change or replace the response.
* </li>
* </ul>
* </p>
* <p>
* Hook methods may return <code>true</code> or <code>void</code> if processing should continue normally.
* This is generally the right thing to do. If your interceptor is providing a response rather than
* letting HAPI handle the response normally, you must return <code>false</code>. In this case,
* no further processing will occur and no further interceptors will be called.
* </p>
* <p>
* Hook methods may also throw {@link AuthenticationException} to indicate that the interceptor
* has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401
* will be returned to the client.
*/
SERVER_OUTGOING_GRAPHQL_RESPONSE(boolean.class,
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"java.lang.String",
"java.lang.String",
"ca.uhn.fhir.rest.api.server.ResponseDetails"
),
/**
* <b>Server Hook:</b>
* This method is called when an OperationOutcome is being returned in response to a failure.

View File

@ -219,6 +219,7 @@ public class Constants {
public static final int MAX_RESOURCE_NAME_LENGTH = 100;
public static final String CACHE_CONTROL_PRIVATE = "private";
public static final int STATUS_HTTP_412_PAYLOAD_TOO_LARGE = 413;
public static final String OPERATION_NAME_GRAPHQL = "$graphql";
static {
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);

View File

@ -49,7 +49,9 @@ public enum EncodingEnum {
public IParser newParser(FhirContext theContext) {
return theContext.newRDFParser();
}
};
},
;
/**
* "json"

View File

@ -109,7 +109,7 @@ public class GraphQLProvider {
}
@GraphQL
public String processGraphQlRequet(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
IGraphQLEngine engine = engineFactory.get();
engine.setAppInfo(theRequestDetails);

View File

@ -21,16 +21,19 @@ package ca.uhn.fhir.rest.server.method;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IRestfulServer;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import javax.annotation.Nonnull;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
@ -63,7 +66,7 @@ public class GraphQLMethodBinding extends BaseMethodBinding<String> {
@Override
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
if ("$graphql".equals(theRequest.getOperation())) {
if (Constants.OPERATION_NAME_GRAPHQL.equals(theRequest.getOperation())) {
return true;
}
@ -85,11 +88,22 @@ public class GraphQLMethodBinding extends BaseMethodBinding<String> {
String charset = Constants.CHARSET_NAME_UTF8;
boolean respondGzip = theRequest.isRespondGzip();
Writer writer = theRequest.getResponse().getResponseWriter(statusCode, statusMessage, contentType, charset, respondGzip);
String responseString = (String) response;
writer.write(responseString);
writer.close();
// Interceptor call: SERVER_OUTGOING_GRAPHQL_RESPONSE
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(String.class, theRequest.getParameters().get(Constants.PARAM_GRAPHQL_QUERY)[0])
.add(String.class, responseString);
if (theRequest.getInterceptorBroadcaster().callHooks(Pointcut.SERVER_OUTGOING_GRAPHQL_RESPONSE, params)) {
// Write the response
Writer writer = theRequest.getResponse().getResponseWriter(statusCode, statusMessage, contentType, charset, respondGzip);
writer.write(responseString);
writer.close();
}
return null;
}

View File

@ -6,6 +6,15 @@
<title>HAPI FHIR Changelog</title>
</properties>
<body>
<!--
<release version="4.1.0" date="TBD" description="Igloo">
<action type="add" issue="1321">
Support has been added for RDF encoding and parsing in the
<![CDATA[<a href="https://www.hl7.org/fhir/rdf.html#instance">Turtle</a>]]>
format. Thanks to Raul Estrada for the pull request!
</action>
</release>
-->
<release version="4.0.0" date="TBD" description="Igloo">
<action type="add">
The version of a few dependencies have been bumped to the
@ -135,11 +144,6 @@
</li>The rule list is now cached on a per-request basis, which should improve performance</ul>
]]>
</action>
<action type="add" issue="1321">
Support has been added for RDF encoding and parsing in the
<![CDATA[<a href="https://www.hl7.org/fhir/rdf.html#instance">Turtle</a>]]>
format. Thanks to Raul Estrada for the pull request!
</action>
<action type="add">
The $expunge global everything operation has been refactored to do deletes
in small batches. This change will likely reduce performance, but does allow