Render narrative in HTML resource view (#4702)
* Working * Add changelog
This commit is contained in:
parent
e69fe05e96
commit
ee8b5b39d4
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 4702
|
||||
title: "The ResponseHighlightingInterceptor (which renders a stylized HTML view
|
||||
of resources) will now include the resource narrative in the rendered view
|
||||
if one is present."
|
|
@ -19,8 +19,9 @@
|
|||
*/
|
||||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
|
@ -38,24 +39,31 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
@ -63,6 +71,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.util.UrlUtil.sanitizeUrlPart;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -89,6 +98,7 @@ public class ResponseHighlighterInterceptor {
|
|||
private static final String[] PARAM_FORMAT_VALUE_TTL = new String[]{Constants.FORMAT_TURTLE};
|
||||
private boolean myShowRequestHeaders = false;
|
||||
private boolean myShowResponseHeaders = true;
|
||||
private boolean myShowNarrative = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -427,7 +437,6 @@ public class ResponseHighlighterInterceptor {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean handleOutgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse, String theGraphqlResponse, IBaseResource theResourceResponse) {
|
||||
if (theResourceResponse == null && theGraphqlResponse == null) {
|
||||
// this will happen during, for example, a bulk export polling request
|
||||
|
@ -582,90 +591,7 @@ public class ResponseHighlighterInterceptor {
|
|||
outputBuffer.append(" <head>\n");
|
||||
outputBuffer.append(" <meta charset=\"utf-8\" />\n");
|
||||
outputBuffer.append(" <style>\n");
|
||||
outputBuffer.append(".httpStatusDiv {");
|
||||
outputBuffer.append(" font-size: 1.2em;");
|
||||
outputBuffer.append(" font-weight: bold;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".hlQuot { color: #88F; }\n");
|
||||
outputBuffer.append(".hlQuot a { text-decoration: underline; text-decoration-color: #CCC; }\n");
|
||||
outputBuffer.append(".hlQuot a:HOVER { text-decoration: underline; text-decoration-color: #008; }\n");
|
||||
outputBuffer.append(".hlQuot .uuid, .hlQuot .dateTime {\n");
|
||||
outputBuffer.append(" user-select: all;\n");
|
||||
outputBuffer.append(" -moz-user-select: all;\n");
|
||||
outputBuffer.append(" -webkit-user-select: all;\n");
|
||||
outputBuffer.append(" -ms-user-select: element;\n");
|
||||
outputBuffer.append("}\n");
|
||||
outputBuffer.append(".hlAttr {\n");
|
||||
outputBuffer.append(" color: #888;\n");
|
||||
outputBuffer.append("}\n");
|
||||
outputBuffer.append(".hlTagName {\n");
|
||||
outputBuffer.append(" color: #006699;\n");
|
||||
outputBuffer.append("}\n");
|
||||
outputBuffer.append(".hlControl {\n");
|
||||
outputBuffer.append(" color: #660000;\n");
|
||||
outputBuffer.append("}\n");
|
||||
outputBuffer.append(".hlText {\n");
|
||||
outputBuffer.append(" color: #000000;\n");
|
||||
outputBuffer.append("}\n");
|
||||
outputBuffer.append(".hlUrlBase {\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".headersDiv {\n");
|
||||
outputBuffer.append(" padding: 10px;");
|
||||
outputBuffer.append(" margin-left: 10px;");
|
||||
outputBuffer.append(" border: 1px solid #CCC;");
|
||||
outputBuffer.append(" border-radius: 10px;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".headersRow {\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".headerName {\n");
|
||||
outputBuffer.append(" color: #888;\n");
|
||||
outputBuffer.append(" font-family: monospace;\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".headerValue {\n");
|
||||
outputBuffer.append(" color: #88F;\n");
|
||||
outputBuffer.append(" font-family: monospace;\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".responseBodyTable {");
|
||||
outputBuffer.append(" width: 100%;\n");
|
||||
outputBuffer.append(" margin-left: 0px;\n");
|
||||
outputBuffer.append(" margin-top: -10px;\n");
|
||||
outputBuffer.append(" position: relative;\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".responseBodyTableFirstColumn {");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".responseBodyTableSecondColumn {");
|
||||
outputBuffer.append(" position: absolute;\n");
|
||||
outputBuffer.append(" margin-left: 70px;\n");
|
||||
outputBuffer.append(" vertical-align: top;\n");
|
||||
outputBuffer.append(" left: 0px;\n");
|
||||
outputBuffer.append(" right: 0px;\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".responseBodyTableSecondColumn PRE {");
|
||||
outputBuffer.append(" margin: 0px;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".sizeInfo {");
|
||||
outputBuffer.append(" margin-top: 20px;");
|
||||
outputBuffer.append(" font-size: 0.8em;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".lineAnchor A {");
|
||||
outputBuffer.append(" text-decoration: none;");
|
||||
outputBuffer.append(" padding-left: 20px;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".lineAnchor {");
|
||||
outputBuffer.append(" display: block;");
|
||||
outputBuffer.append(" padding-right: 20px;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(".selectedLine {");
|
||||
outputBuffer.append(" background-color: #EEF;");
|
||||
outputBuffer.append(" font-weight: bold;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append("H1 {");
|
||||
outputBuffer.append(" font-size: 1.1em;");
|
||||
outputBuffer.append(" color: #666;");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append("BODY {\n");
|
||||
outputBuffer.append(" font-family: Arial;\n");
|
||||
outputBuffer.append("}");
|
||||
outputBuffer.append(ClasspathUtil.loadResource("ca/uhn/fhir/rest/server/interceptor/ResponseHighlighter.css"));
|
||||
outputBuffer.append(" </style>\n");
|
||||
outputBuffer.append(" </head>\n");
|
||||
outputBuffer.append("\n");
|
||||
|
@ -756,6 +682,16 @@ public class ResponseHighlighterInterceptor {
|
|||
// ignore (this will hit if we're running in a servlet 2.5 environment)
|
||||
}
|
||||
|
||||
if (myShowNarrative) {
|
||||
String narrativeHtml = extractNarrativeHtml(theRequestDetails, theResource);
|
||||
if (isNotBlank(narrativeHtml)) {
|
||||
outputBuffer.append("<h1>Narrative</h1>");
|
||||
outputBuffer.append("<div class=\"narrativeBody\">");
|
||||
outputBuffer.append(narrativeHtml);
|
||||
outputBuffer.append("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
outputBuffer.append("<h1>Response Body</h1>");
|
||||
|
||||
outputBuffer.append("<div class=\"responseBodyTable\">");
|
||||
|
@ -827,6 +763,71 @@ public class ResponseHighlighterInterceptor {
|
|||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
String extractNarrativeHtml(@Nonnull RequestDetails theRequestDetails, @Nullable IBaseResource theResource) {
|
||||
if (theResource == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FhirContext ctx = theRequestDetails.getFhirContext();
|
||||
|
||||
// Try to extract the narrative from the resource. First, just see if there
|
||||
// is a narrative in the normal spot.
|
||||
XhtmlNode xhtmlNode = extractNarrativeFromDomainResource(theResource, ctx);
|
||||
|
||||
// If the resource is a document, see if the Composition has a narrative
|
||||
if (xhtmlNode == null && "Bundle".equals(ctx.getResourceType(theResource))) {
|
||||
if ("document".equals(ctx.newTerser().getSinglePrimitiveValueOrNull(theResource, "type"))) {
|
||||
IBaseResource firstResource = ctx.newTerser().getSingleValueOrNull(theResource, "entry.resource", IBaseResource.class);
|
||||
if (firstResource != null && "Composition".equals(ctx.getResourceType(firstResource))) {
|
||||
xhtmlNode = extractNarrativeFromDomainResource(firstResource, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the resource is a Parameters, see if it has a narrative in the first
|
||||
// parameter
|
||||
if (xhtmlNode == null && "Parameters".equals(ctx.getResourceType(theResource))) {
|
||||
String firstParameterName = ctx.newTerser().getSinglePrimitiveValueOrNull(theResource, "parameter.name");
|
||||
if ("Narrative".equals(firstParameterName)) {
|
||||
String firstParameterValue = ctx.newTerser().getSinglePrimitiveValueOrNull(theResource, "parameter.value[x]");
|
||||
if (defaultString(firstParameterValue).startsWith("<div")) {
|
||||
xhtmlNode = new XhtmlNode();
|
||||
xhtmlNode.setValueAsString(firstParameterValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FHIR only allows a pretty restricted set of HTML tags and attributes, in order
|
||||
* to avoid any risk of injection attacks. If anything that isn't explicitly allowed
|
||||
* by FHIR is present in the narrative we won't render it and instead we'll explain
|
||||
* what validation problems we found.
|
||||
*/
|
||||
if (xhtmlNode != null) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
Validate.isTrue(xhtmlNode.getName() == null);
|
||||
xhtmlNode.getFirstElement().validate(errors, "", true, false, false);
|
||||
if (errors.size() > 0) {
|
||||
StringBuilder errorNarrative = new StringBuilder();
|
||||
errorNarrative.append("Can not render narrative due to validation errors:");
|
||||
errorNarrative.append("<ul>");
|
||||
errors.forEach(next -> {
|
||||
errorNarrative.append("<li>");
|
||||
errorNarrative.append(sanitizeUrlPart(next));
|
||||
errorNarrative.append("</li>");
|
||||
});
|
||||
errorNarrative.append("</ul>");
|
||||
return errorNarrative.toString();
|
||||
}
|
||||
|
||||
return xhtmlNode.getValueAsString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeLength(HttpServletResponse theServletResponse, int theLength) throws IOException {
|
||||
double kb = ((double) theLength) / FileUtils.ONE_KB;
|
||||
if (kb <= 1000) {
|
||||
|
@ -876,4 +877,75 @@ public class ResponseHighlighterInterceptor {
|
|||
theBuilder.append("</div>");
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is {@literal true}), if the response is a FHIR
|
||||
* resource, and that resource includes a <a href="http://hl7.org/fhir/narrative.html">Narrative</div>,
|
||||
* the narrative will be rendered in the HTML response page as actual rendered HTML.
|
||||
* <p>
|
||||
* The narrative to be rendered will be sourced from one of 3 possible locations,
|
||||
* depending on the resource being returned by the server:
|
||||
* <ul>
|
||||
* <li>if the resource is a DomainResource, the narrative in Resource.text will be rendered.</li>
|
||||
* <li>If the resource is a document bundle, the narrative in the document Composition will be rendered.</li>
|
||||
* <li>If the resource is a Parameters resource, and the first parameter has the name "Narrative" and a value consisting of a string starting with "<div", that will be rendered.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* In all cases, the narrative is scanned to ensure that it does not contain any tags
|
||||
* or attributes that are not explicitly allowed by the FHIR specification in order
|
||||
* to <a href="http://hl7.org/fhir/narrative.html#xhtml">prevent active content</a>.
|
||||
* If any such tags or attributes are found, the narrative is not rendered and
|
||||
* instead a warning is displayed. Note that while this scanning is helpful, it does
|
||||
* not completely mitigate the security risks associated with narratives. See
|
||||
* <a href="http://hl7.org/fhir/security.html#narrative">FHIR Security: Narrative</a>
|
||||
* for more information.
|
||||
* </p>
|
||||
*
|
||||
* @return Should the narrative be rendered?
|
||||
* @since 6.6.0
|
||||
*/
|
||||
|
||||
public boolean isShowNarrative() {
|
||||
return myShowNarrative;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is {@literal true}), if the response is a FHIR
|
||||
* resource, and that resource includes a <a href="http://hl7.org/fhir/narrative.html">Narrative</div>,
|
||||
* the narrative will be rendered in the HTML response page as actual rendered HTML.
|
||||
* <p>
|
||||
* The narrative to be rendered will be sourced from one of 3 possible locations,
|
||||
* depending on the resource being returned by the server:
|
||||
* <ul>
|
||||
* <li>if the resource is a DomainResource, the narrative in Resource.text will be rendered.</li>
|
||||
* <li>If the resource is a document bundle, the narrative in the document Composition will be rendered.</li>
|
||||
* <li>If the resource is a Parameters resource, and the first parameter has the name "Narrative" and a value consisting of a string starting with "<div", that will be rendered.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* In all cases, the narrative is scanned to ensure that it does not contain any tags
|
||||
* or attributes that are not explicitly allowed by the FHIR specification in order
|
||||
* to <a href="http://hl7.org/fhir/narrative.html#xhtml">prevent active content</a>.
|
||||
* If any such tags or attributes are found, the narrative is not rendered and
|
||||
* instead a warning is displayed. Note that while this scanning is helpful, it does
|
||||
* not completely mitigate the security risks associated with narratives. See
|
||||
* <a href="http://hl7.org/fhir/security.html#narrative">FHIR Security: Narrative</a>
|
||||
* for more information.
|
||||
* </p>
|
||||
*
|
||||
* @param theShowNarrative Should the narrative be rendered?
|
||||
* @since 6.6.0
|
||||
*/
|
||||
public void setShowNarrative(boolean theShowNarrative) {
|
||||
myShowNarrative = theShowNarrative;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static XhtmlNode extractNarrativeFromDomainResource(@Nonnull IBaseResource theResource, FhirContext ctx) {
|
||||
if (ctx.getResourceDefinition(theResource).getChildByName("text") != null) {
|
||||
return ctx.newTerser().getSingleValue(theResource, "text.div", XhtmlNode.class).orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
.httpStatusDiv {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hlQuot {
|
||||
color: #88F;
|
||||
}
|
||||
|
||||
.hlQuot a {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #CCC;
|
||||
}
|
||||
|
||||
.hlQuot a:HOVER {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: #008;
|
||||
}
|
||||
|
||||
.hlQuot .uuid, .hlQuot .dateTime {
|
||||
user-select: all;
|
||||
-moz-user-select: all;
|
||||
-webkit-user-select: all;
|
||||
-ms-user-select: element;
|
||||
}
|
||||
|
||||
.hlAttr {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.hlTagName {
|
||||
color: #006699;
|
||||
}
|
||||
|
||||
.hlControl {
|
||||
color: #660000;
|
||||
}
|
||||
|
||||
.hlText {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.hlUrlBase {
|
||||
}
|
||||
|
||||
.headersDiv {
|
||||
padding: 10px;
|
||||
margin-left: 10px;
|
||||
border: 1px solid #CCC;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.headersRow {
|
||||
}
|
||||
|
||||
.headerName {
|
||||
color: #888;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.headerValue {
|
||||
color: #88F;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.narrativeBody {
|
||||
padding: 10px;
|
||||
margin-left: 10px;
|
||||
border: 1px solid #CCC;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.narrativeBody DIV,
|
||||
.narrativeBody TABLE {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.narrativeBody TABLE > THEAD {
|
||||
background: #AAA;
|
||||
}
|
||||
|
||||
.narrativeBody TABLE > TBODY > TR:nth-child(odd) > TD {
|
||||
background: #CCC;
|
||||
}
|
||||
|
||||
.narrativeBody TABLE > TBODY > TR:nth-child(even) > TD {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
.narrativeBody TABLE TD, .narrativeBody TABLE TH {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.responseBodyTable {
|
||||
width: 100%;
|
||||
margin-left: 0px;
|
||||
margin-top: -10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.responseBodyTableFirstColumn {
|
||||
}
|
||||
|
||||
.responseBodyTableSecondColumn {
|
||||
position: absolute;
|
||||
margin-left: 70px;
|
||||
vertical-align: top;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.responseBodyTableSecondColumn PRE {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.sizeInfo {
|
||||
margin-top: 20px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.lineAnchor A {
|
||||
text-decoration: none;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.lineAnchor {
|
||||
display: block;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.selectedLine {
|
||||
background-color: #EEF;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
H1 {
|
||||
font-size: 1.1em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
BODY {
|
||||
font-family: Arial;
|
||||
}
|
|
@ -17,6 +17,7 @@ import ca.uhn.fhir.rest.api.EncodingEnum;
|
|||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
@ -40,20 +41,28 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Binary;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Composition;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Quantity;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.Type;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
@ -82,17 +91,20 @@ import static org.mockito.Mockito.when;
|
|||
public class ResponseHighlightingInterceptorTest {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseHighlightingInterceptorTest.class);
|
||||
private static ResponseHighlighterInterceptor ourInterceptor = new ResponseHighlighterInterceptor();
|
||||
private static Server ourServer;
|
||||
private static final ResponseHighlighterInterceptor ourInterceptor = new ResponseHighlighterInterceptor();
|
||||
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||
private static Server ourServer;
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private static int ourPort;
|
||||
private static RestfulServer ourServlet;
|
||||
private static DummyPatientResourceProvider ourPatientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
ourInterceptor.setShowRequestHeaders(new ResponseHighlighterInterceptor().isShowRequestHeaders());
|
||||
ourInterceptor.setShowResponseHeaders(new ResponseHighlighterInterceptor().isShowResponseHeaders());
|
||||
ResponseHighlighterInterceptor defaults = new ResponseHighlighterInterceptor();
|
||||
ourInterceptor.setShowRequestHeaders(defaults.isShowRequestHeaders());
|
||||
ourInterceptor.setShowResponseHeaders(defaults.isShowResponseHeaders());
|
||||
ourInterceptor.setShowNarrative(defaults.isShowNarrative());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,8 +201,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testDontHighlightWhenOriginHeaderPresent() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9"));
|
||||
when(req.getHeader(Constants.HEADER_ORIGIN)).thenAnswer(theInvocation -> "http://example.com");
|
||||
|
@ -210,10 +220,86 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// true means it decided to not handle the request..
|
||||
assertTrue(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertTrue(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_DomainResource() {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("Simpson");
|
||||
patient.getText().setDivAsString("<div>HELLO</div>");
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), patient);
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">HELLO</div>", outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_NonDomainResource() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), bundle);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_DocumentWithCompositionNarrative() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setType(Bundle.BundleType.DOCUMENT);
|
||||
Composition composition = new Composition();
|
||||
composition.getText().setDivAsString("<div>HELLO</div>");
|
||||
bundle.addEntry().setResource(composition);
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), bundle);
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">HELLO</div>", outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_ParametersWithNarrativeAsFirstParameter() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("Narrative", new StringType("<div>HELLO</div>"));
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), parameters);
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">HELLO</div>", outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_Parameters() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("Foo", new StringType("<div>HELLO</div>"));
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), parameters);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_ParametersWithNonNarrativeFirstParameter_1() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("Narrative", new Quantity(123L));
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), parameters);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_ParametersWithNonNarrativeFirstParameter_2() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("Narrative", (Type)null);
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), parameters);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractNarrativeHtml_ParametersWithNonNarrativeFirstParameter_3() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("Narrative", new StringType("hello"));
|
||||
|
||||
String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), parameters);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceApplicationJson() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=application/json");
|
||||
|
@ -467,8 +553,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightException() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9"));
|
||||
|
||||
|
@ -492,7 +576,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
ResourceNotFoundException exception = new ResourceNotFoundException("Not found");
|
||||
exception.setOperationOutcome(new OperationOutcome().addIssue(new OperationOutcome.OperationOutcomeIssueComponent().setDiagnostics("Hello")));
|
||||
|
||||
assertFalse(ic.handleException(reqDetails, exception, req, resp));
|
||||
assertFalse(ourInterceptor.handleException(reqDetails, exception, req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -526,14 +610,11 @@ public class ResponseHighlightingInterceptorTest {
|
|||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* See #346
|
||||
*/
|
||||
@Test
|
||||
public void testHighlightForceHtmlCt() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("application/xml+fhir"));
|
||||
|
||||
|
@ -553,7 +634,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// false means it decided to handle the request..
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertFalse(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,7 +642,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
*/
|
||||
@Test
|
||||
public void testHighlightForceHtmlFormat() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("application/xml+fhir"));
|
||||
|
@ -582,13 +662,11 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// false means it decided to handle the request..
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertFalse(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightForceRaw() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9"));
|
||||
|
||||
|
@ -610,13 +688,12 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// true means it decided to not handle the request..
|
||||
assertTrue(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertTrue(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightNormalResponse() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9"));
|
||||
|
@ -636,7 +713,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServer(server);
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertFalse(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -647,8 +724,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightNormalResponseForcePrettyPrint() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9"));
|
||||
|
||||
|
@ -669,7 +744,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServer(server);
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertFalse(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -682,8 +757,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
*/
|
||||
@Test
|
||||
public void testHighlightProducesDefaultJsonWithBrowserRequest() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html,application/xhtml+xml,application/xml;q=0.9"));
|
||||
|
@ -703,7 +776,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServer(server);
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertFalse(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -712,8 +785,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightProducesDefaultJsonWithBrowserRequest2() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(theInvocation -> new ArrayEnumeration<>("text/html;q=0.8,application/xhtml+xml,application/xml;q=0.9"));
|
||||
|
@ -734,7 +805,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// True here means the interceptor didn't handle the request, because HTML wasn't the top ranked accept header
|
||||
assertTrue(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
assertTrue(ourInterceptor.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -871,6 +942,60 @@ public class ResponseHighlightingInterceptorTest {
|
|||
assertThat(responseContent, (containsStringIgnoringCase("Content-Type")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNarrative() throws IOException {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("Simpson");
|
||||
patient.getText().setDivAsString("<div><table><thead><tr><th>Header1</th><th>Header2</th></tr></thead><tr><td>A cell</td><td>A cell</td></tr><tr><td>A cell 2</td><td>A cell 2</td></tr></table></div>");
|
||||
ourPatientProvider.myNextPatientOpResponse = patient;
|
||||
|
||||
String url = "http://localhost:" + ourPort + "/Patient/1/$patientOp?_format=html/json";
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try (CloseableHttpResponse response = ourClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8);
|
||||
assertThat(resp, containsString("<h1>Narrative</h1>"));
|
||||
assertThat(resp, containsString("<thead><tr><th>Header1</th><th>Header2</th></tr></thead>"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNarrative_Disabled() throws IOException {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("Simpson");
|
||||
patient.getText().setDivAsString("<div><table><thead><tr><th>Header1</th><th>Header2</th></tr></thead><tr><td>A cell</td><td>A cell</td></tr><tr><td>A cell 2</td><td>A cell 2</td></tr></table></div>");
|
||||
ourPatientProvider.myNextPatientOpResponse = patient;
|
||||
|
||||
ourInterceptor.setShowNarrative(false);
|
||||
|
||||
String url = "http://localhost:" + ourPort + "/Patient/1/$patientOp?_format=html/json";
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try (CloseableHttpResponse response = ourClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8);
|
||||
assertThat(resp, not(containsString("<h1>Narrative</h1>")));
|
||||
assertThat(resp, not(containsString("<thead><tr><th>Header1</th><th>Header2</th></tr></thead>")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNarrative_SketchyTagBlocked() throws IOException {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("Simpson");
|
||||
patient.getText().setDivAsString("<div><table onclick=\"foo();\"><thead><tr><th>Header1</th><th>Header2</th></tr></thead><tr><td>A cell</td><td>A cell</td></tr><tr><td>A cell 2</td><td>A cell 2</td></tr></table></div>");
|
||||
ourPatientProvider.myNextPatientOpResponse = patient;
|
||||
|
||||
String url = "http://localhost:" + ourPort + "/Patient/1/$patientOp?_format=html/json";
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
try (CloseableHttpResponse response = ourClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8);
|
||||
assertThat(resp, not(containsString("<thead><tr><th>Header1</th><th>Header2</th></tr></thead>")));
|
||||
assertThat(resp, containsString("Error at div/table: Found attribute table.onclick in a resource"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullResponseResource() {
|
||||
ourInterceptor.setShowResponseHeaders(true);
|
||||
|
@ -895,64 +1020,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
assertTrue(ourInterceptor.outgoingResponse(requestDetails, responseObject, servletRequest, servletResponse));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
JettyUtil.closeServer(ourServer);
|
||||
TestUtil.randomizeLocaleAndTimezone();
|
||||
}
|
||||
|
||||
|
||||
public static class GraphQLProvider {
|
||||
@GraphQL
|
||||
public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryUrl String theQuery) {
|
||||
return "{\"foo\":\"bar\"}";
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeClass() throws Exception {
|
||||
ourServer = new Server(0);
|
||||
|
||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
ourServlet = new RestfulServer(ourCtx);
|
||||
ourServlet.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
|
||||
/*
|
||||
* Enable CORS
|
||||
*/
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
CorsInterceptor corsInterceptor = new CorsInterceptor(config);
|
||||
config.addAllowedHeader("Origin");
|
||||
config.addAllowedHeader("Accept");
|
||||
config.addAllowedHeader("X-Requested-With");
|
||||
config.addAllowedHeader("Content-Type");
|
||||
config.addAllowedHeader("Access-Control-Request-Method");
|
||||
config.addAllowedHeader("Access-Control-Request-Headers");
|
||||
config.addAllowedOrigin("*");
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
ourServlet.registerInterceptor(corsInterceptor);
|
||||
|
||||
ourServlet.registerInterceptor(ourInterceptor);
|
||||
ourServlet.registerProviders(patientProvider, new DummyBinaryResourceProvider(), new GraphQLProvider());
|
||||
ourServlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
|
||||
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
JettyUtil.startServer(ourServer);
|
||||
ourPort = JettyUtil.getPortForStartedServer(ourServer);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
class TestServletRequestDetails extends ServletRequestDetails {
|
||||
TestServletRequestDetails(IInterceptorBroadcaster theInterceptorBroadcaster) {
|
||||
super(theInterceptorBroadcaster);
|
||||
|
@ -964,6 +1031,13 @@ public class ResponseHighlightingInterceptorTest {
|
|||
}
|
||||
}
|
||||
|
||||
public static class GraphQLProvider {
|
||||
@GraphQL
|
||||
public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryUrl String theQuery) {
|
||||
return "{\"foo\":\"bar\"}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class DummyBinaryResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
|
@ -978,7 +1052,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
if (theId.getIdPart().equals("html")) {
|
||||
retVal.setContent("<html>DATA</html>".getBytes(Charsets.UTF_8));
|
||||
retVal.setContentType("text/html");
|
||||
}else {
|
||||
} else {
|
||||
retVal.setContent(new byte[]{1, 2, 3, 4});
|
||||
retVal.setContentType(theId.getIdPart());
|
||||
}
|
||||
|
@ -998,6 +1072,8 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
private Patient myNextPatientOpResponse;
|
||||
|
||||
private Patient createPatient1() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
|
@ -1031,14 +1107,14 @@ public class ResponseHighlightingInterceptorTest {
|
|||
return Collections.singletonList(p);
|
||||
}
|
||||
|
||||
@Operation(name="binaryOp", idempotent = true)
|
||||
@Operation(name = "binaryOp", idempotent = true)
|
||||
public Binary binaryOp(@IdParam IdType theId) {
|
||||
Binary retVal = new Binary();
|
||||
retVal.setId(theId);
|
||||
if (theId.getIdPart().equals("html")) {
|
||||
retVal.setContent("<html>DATA</html>".getBytes(Charsets.UTF_8));
|
||||
retVal.setContentType("text/html");
|
||||
}else {
|
||||
} else {
|
||||
retVal.setContent(new byte[]{1, 2, 3, 4});
|
||||
retVal.setContentType(theId.getIdPart());
|
||||
}
|
||||
|
@ -1108,6 +1184,67 @@ public class ResponseHighlightingInterceptorTest {
|
|||
return Collections.singletonList(p);
|
||||
}
|
||||
|
||||
@Operation(name = "patientOp", idempotent = true)
|
||||
public Patient patientOp(@IdParam IIdType theId) {
|
||||
return myNextPatientOpResponse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
JettyUtil.closeServer(ourServer);
|
||||
TestUtil.randomizeLocaleAndTimezone();
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeClass() throws Exception {
|
||||
ourServer = new Server(0);
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
ourServlet = new RestfulServer(ourCtx);
|
||||
ourServlet.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
|
||||
/*
|
||||
* Enable CORS
|
||||
*/
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
CorsInterceptor corsInterceptor = new CorsInterceptor(config);
|
||||
config.addAllowedHeader("Origin");
|
||||
config.addAllowedHeader("Accept");
|
||||
config.addAllowedHeader("X-Requested-With");
|
||||
config.addAllowedHeader("Content-Type");
|
||||
config.addAllowedHeader("Access-Control-Request-Method");
|
||||
config.addAllowedHeader("Access-Control-Request-Headers");
|
||||
config.addAllowedOrigin("*");
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
ourServlet.registerInterceptor(corsInterceptor);
|
||||
|
||||
ourServlet.registerInterceptor(ourInterceptor);
|
||||
ourServlet.registerProviders(ourPatientProvider, new DummyBinaryResourceProvider(), new GraphQLProvider());
|
||||
ourServlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
|
||||
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
JettyUtil.startServer(ourServer);
|
||||
ourPort = JettyUtil.getPortForStartedServer(ourServer);
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static SystemRequestDetails newRequest() {
|
||||
SystemRequestDetails retVal = new SystemRequestDetails();
|
||||
retVal.setFhirContext(ourCtx);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue