Fix responsehighlighterinterceptor to work with graphql

This commit is contained in:
James Agnew 2019-08-13 18:00:30 -04:00
parent 33767b5399
commit b127867ef7
5 changed files with 128 additions and 51 deletions

View File

@ -400,6 +400,12 @@ public enum Pointcut {
* <li> * <li>
* java.lang.String - The GraphQL response * java.lang.String - The GraphQL response
* </li> * </li>
* <li>
* javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment
* </li>
* <li>
* javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment
* </li>
* </ul> * </ul>
* </p> * </p>
* <p> * <p>
@ -417,7 +423,9 @@ public enum Pointcut {
"ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
"java.lang.String", "java.lang.String",
"java.lang.String" "java.lang.String",
"javax.servlet.http.HttpServletRequest",
"javax.servlet.http.HttpServletResponse"
), ),

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.server.interceptor;
public class InterceptorOrders { public class InterceptorOrders {
public static final int SERVE_MEDIA_RESOURCE_RAW_INTERCEPTOR = 1000; public static final int SERVE_MEDIA_RESOURCE_RAW_INTERCEPTOR = 1000;
public static final int RESPONSE_HIGHLIGHTER_INTERCEPTOR = 10000; public static final int RESPONSE_HIGHLIGHTER_INTERCEPTOR = 10000;
/** Non instantiable */ /** Non instantiable */

View File

@ -268,7 +268,7 @@ public class ResponseHighlighterInterceptor {
responseDetails.setResponseCode(theException.getStatusCode()); responseDetails.setResponseCode(theException.getStatusCode());
BaseResourceReturningMethodBinding.callOutgoingFailureOperationOutcomeHook(theRequestDetails, oo); BaseResourceReturningMethodBinding.callOutgoingFailureOperationOutcomeHook(theRequestDetails, oo);
streamResponse(theRequestDetails, theServletResponse, responseDetails.getResponseResource(), theServletRequest, responseDetails.getResponseCode()); streamResponse(theRequestDetails, theServletResponse, responseDetails.getResponseResource(), null, theServletRequest, responseDetails.getResponseCode());
return false; return false;
} }
@ -313,10 +313,39 @@ public class ResponseHighlighterInterceptor {
return this; return this;
} }
@Hook(value = Pointcut.SERVER_OUTGOING_GRAPHQL_RESPONSE, order = InterceptorOrders.RESPONSE_HIGHLIGHTER_INTERCEPTOR)
public boolean outgoingGraphqlResponse(RequestDetails theRequestDetails, String theRequest, String theResponse, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws AuthenticationException {
/*
* Return true here so that we still fire SERVER_OUTGOING_GRAPHQL_RESPONSE!
*/
if (handleOutgoingResponse(theRequestDetails, null, theServletRequest, theServletResponse, theResponse, null)) {
return true;
}
theRequestDetails.setAttribute("ResponseHighlighterInterceptorHandled", Boolean.TRUE);
return true;
}
@Hook(value = Pointcut.SERVER_OUTGOING_RESPONSE, order = InterceptorOrders.RESPONSE_HIGHLIGHTER_INTERCEPTOR) @Hook(value = Pointcut.SERVER_OUTGOING_RESPONSE, order = InterceptorOrders.RESPONSE_HIGHLIGHTER_INTERCEPTOR)
public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws AuthenticationException { throws AuthenticationException {
if (!Boolean.TRUE.equals(theRequestDetails.getAttribute("ResponseHighlighterInterceptorHandled"))) {
String graphqlResponse = null;
IBaseResource resourceResponse = theResponseObject.getResponseResource();
if (handleOutgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse, graphqlResponse, resourceResponse)) {
return true;
}
}
return false;
}
private boolean handleOutgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse, String theGraphqlResponse, IBaseResource theResourceResponse) {
/* /*
* Request for _raw * Request for _raw
*/ */
@ -380,12 +409,11 @@ public class ResponseHighlighterInterceptor {
/* /*
* Not binary * Not binary
*/ */
if (!force && (theResponseObject.getResponseResource() instanceof IBaseBinary)) { if (!force && theResponseObject != null && (theResponseObject.getResponseResource() instanceof IBaseBinary)) {
return true; return true;
} }
streamResponse(theRequestDetails, theServletResponse, theResponseObject.getResponseResource(), theServletRequest, 200); streamResponse(theRequestDetails, theServletResponse, theResourceResponse, theGraphqlResponse, theServletRequest, 200);
return false; return false;
} }
@ -407,40 +435,51 @@ public class ResponseHighlighterInterceptor {
} }
} }
private void streamResponse(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, IBaseResource theResource, ServletRequest theServletRequest, int theStatusCode) { private void streamResponse(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, IBaseResource theResource, String theGraphqlResponse, ServletRequest theServletRequest, int theStatusCode) {
EncodingEnum encoding;
String encoded;
Map<String, String[]> parameters = theRequestDetails.getParameters();
if (isNotBlank(theGraphqlResponse)) {
encoded = theGraphqlResponse;
encoding = EncodingEnum.JSON;
} else {
IParser p;
if (parameters.containsKey(Constants.PARAM_FORMAT)) {
FhirVersionEnum forVersion = theResource.getStructureFhirVersionEnum();
p = RestfulServerUtils.getNewParser(theRequestDetails.getServer().getFhirContext(), forVersion, theRequestDetails);
} else {
EncodingEnum defaultResponseEncoding = theRequestDetails.getServer().getDefaultResponseEncoding();
p = defaultResponseEncoding.newParser(theRequestDetails.getServer().getFhirContext());
RestfulServerUtils.configureResponseParser(theRequestDetails, p);
}
// This interceptor defaults to pretty printing unless the user
// has specifically requested us not to
boolean prettyPrintResponse = true;
String[] prettyParams = parameters.get(Constants.PARAM_PRETTY);
if (prettyParams != null && prettyParams.length > 0) {
if (Constants.PARAM_PRETTY_VALUE_FALSE.equals(prettyParams[0])) {
prettyPrintResponse = false;
}
}
if (prettyPrintResponse) {
p.setPrettyPrint(true);
}
encoding = p.getEncoding();
encoded = p.encodeResourceToString(theResource);
}
if (theRequestDetails.getServer() instanceof RestfulServer) { if (theRequestDetails.getServer() instanceof RestfulServer) {
RestfulServer rs = (RestfulServer) theRequestDetails.getServer(); RestfulServer rs = (RestfulServer) theRequestDetails.getServer();
rs.addHeadersToResponse(theServletResponse); rs.addHeadersToResponse(theServletResponse);
} }
IParser p;
Map<String, String[]> parameters = theRequestDetails.getParameters();
if (parameters.containsKey(Constants.PARAM_FORMAT)) {
FhirVersionEnum forVersion = theResource.getStructureFhirVersionEnum();
p = RestfulServerUtils.getNewParser(theRequestDetails.getServer().getFhirContext(), forVersion, theRequestDetails);
} else {
EncodingEnum defaultResponseEncoding = theRequestDetails.getServer().getDefaultResponseEncoding();
p = defaultResponseEncoding.newParser(theRequestDetails.getServer().getFhirContext());
RestfulServerUtils.configureResponseParser(theRequestDetails, p);
}
// This interceptor defaults to pretty printing unless the user
// has specifically requested us not to
boolean prettyPrintResponse = true;
String[] prettyParams = parameters.get(Constants.PARAM_PRETTY);
if (prettyParams != null && prettyParams.length > 0) {
if (Constants.PARAM_PRETTY_VALUE_FALSE.equals(prettyParams[0])) {
prettyPrintResponse = false;
}
}
if (prettyPrintResponse) {
p.setPrettyPrint(true);
}
EncodingEnum encoding = p.getEncoding();
String encoded = p.encodeResourceToString(theResource);
try { try {
if (theStatusCode > 299) { if (theStatusCode > 299) {
@ -545,27 +584,30 @@ public class ResponseHighlighterInterceptor {
outputBuffer.append(" <body>"); outputBuffer.append(" <body>");
outputBuffer.append("<p>"); outputBuffer.append("<p>");
outputBuffer.append("This result is being rendered in HTML for easy viewing. ");
outputBuffer.append("You may access this content as ");
outputBuffer.append("<a href=\""); if (isBlank(theGraphqlResponse)) {
outputBuffer.append(createLinkHref(parameters, Constants.FORMAT_JSON)); outputBuffer.append("This result is being rendered in HTML for easy viewing. ");
outputBuffer.append("\">Raw JSON</a> or "); outputBuffer.append("You may access this content as ");
outputBuffer.append("<a href=\""); outputBuffer.append("<a href=\"");
outputBuffer.append(createLinkHref(parameters, Constants.FORMAT_XML)); outputBuffer.append(createLinkHref(parameters, Constants.FORMAT_JSON));
outputBuffer.append("\">Raw XML</a>, "); outputBuffer.append("\">Raw JSON</a> or ");
outputBuffer.append(" or view this content in "); outputBuffer.append("<a href=\"");
outputBuffer.append(createLinkHref(parameters, Constants.FORMAT_XML));
outputBuffer.append("\">Raw XML</a>, ");
outputBuffer.append("<a href=\""); outputBuffer.append(" or view this content in ");
outputBuffer.append(createLinkHref(parameters, Constants.FORMATS_HTML_JSON));
outputBuffer.append("\">HTML JSON</a> ");
outputBuffer.append("or "); outputBuffer.append("<a href=\"");
outputBuffer.append("<a href=\""); outputBuffer.append(createLinkHref(parameters, Constants.FORMATS_HTML_JSON));
outputBuffer.append(createLinkHref(parameters, Constants.FORMATS_HTML_XML)); outputBuffer.append("\">HTML JSON</a> ");
outputBuffer.append("\">HTML XML</a>.");
outputBuffer.append("or ");
outputBuffer.append("<a href=\"");
outputBuffer.append(createLinkHref(parameters, Constants.FORMATS_HTML_XML));
outputBuffer.append("\">HTML XML</a>.");
}
Date startTime = (Date) theServletRequest.getAttribute(RestfulServer.REQUEST_START_TIME); Date startTime = (Date) theServletRequest.getAttribute(RestfulServer.REQUEST_START_TIME);
if (startTime != null) { if (startTime != null) {

View File

@ -106,7 +106,9 @@ public class GraphQLMethodBinding extends BaseMethodBinding<String> {
.add(RequestDetails.class, theRequest) .add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(String.class, theRequest.getParameters().get(Constants.PARAM_GRAPHQL_QUERY)[0]) .add(String.class, theRequest.getParameters().get(Constants.PARAM_GRAPHQL_QUERY)[0])
.add(String.class, responseString); .add(String.class, responseString)
.add(HttpServletRequest.class, servletRequest)
.add(HttpServletResponse.class, servletResponse);
if (!theRequest.getInterceptorBroadcaster().callHooks(Pointcut.SERVER_OUTGOING_GRAPHQL_RESPONSE, params)) { if (!theRequest.getInterceptorBroadcaster().callHooks(Pointcut.SERVER_OUTGOING_GRAPHQL_RESPONSE, params)) {
return null; return null;
} }

View File

@ -30,6 +30,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
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.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
@ -387,6 +388,21 @@ public class ResponseHighlightingInterceptorTest {
} }
@Test
public void testHighlightGraphQLResponse() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/A/$graphql?query=" + UrlUtil.escapeUrlParam("{name}"));
httpGet.addHeader("Accept", "text/html");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
status.close();
ourLog.info("Resp: {}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(responseContent, stringContainsInOrder("&quot;foo&quot;"));
}
@Test @Test
public void testHighlightException() throws Exception { public void testHighlightException() throws Exception {
ResponseHighlighterInterceptor ic = ourInterceptor; ResponseHighlighterInterceptor ic = ourInterceptor;
@ -799,6 +815,14 @@ public class ResponseHighlightingInterceptorTest {
TestUtil.clearAllStaticFieldsForUnitTest(); TestUtil.clearAllStaticFieldsForUnitTest();
} }
public static class GraphQLProvider {
@GraphQL
public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQuery String theQuery) {
return "{\"foo\":\"bar\"}";
}
}
@BeforeClass @BeforeClass
public static void beforeClass() throws Exception { public static void beforeClass() throws Exception {
ourServer = new Server(0); ourServer = new Server(0);
@ -827,7 +851,7 @@ public class ResponseHighlightingInterceptorTest {
ourServlet.registerInterceptor(corsInterceptor); ourServlet.registerInterceptor(corsInterceptor);
ourServlet.registerInterceptor(ourInterceptor); ourServlet.registerInterceptor(ourInterceptor);
ourServlet.setResourceProviders(patientProvider, new DummyBinaryResourceProvider()); ourServlet.registerProviders(patientProvider, new DummyBinaryResourceProvider(), new GraphQLProvider());
ourServlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); ourServlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
ServletHolder servletHolder = new ServletHolder(ourServlet); ServletHolder servletHolder = new ServletHolder(ourServlet);
proxyHandler.addServletWithMapping(servletHolder, "/*"); proxyHandler.addServletWithMapping(servletHolder, "/*");