Merge branch 'master' into hapi3_refactor
This commit is contained in:
commit
a92ace2e0d
|
@ -0,0 +1,30 @@
|
|||
package example;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
|
||||
|
||||
public class PatchExamples {
|
||||
|
||||
//START SNIPPET: patch
|
||||
@Patch
|
||||
public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) {
|
||||
|
||||
if (thePatchType == PatchTypeEnum.JSON_PATCH) {
|
||||
// do something
|
||||
}
|
||||
if (thePatchType == PatchTypeEnum.XML_PATCH) {
|
||||
// do something
|
||||
}
|
||||
|
||||
OperationOutcome retVal = new OperationOutcome();
|
||||
retVal.getText().setDivAsString("<div>OK</div>");
|
||||
return retVal;
|
||||
}
|
||||
//END SNIPPET: patch
|
||||
|
||||
|
||||
}
|
|
@ -141,7 +141,7 @@ public class ServletExamples {
|
|||
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
|
||||
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
|
||||
|
||||
// Create the interceptor and register it
|
||||
CorsInterceptor interceptor = new CorsInterceptor(config);
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
var selectedLines = new Array();
|
||||
function updateHighlightedLine() {
|
||||
updateHighlightedLineTo(window.location.hash);
|
||||
}
|
||||
|
||||
function updateHighlightedLineTo(theNewHash) {
|
||||
|
||||
for (var next in selectedLines) {
|
||||
document.getElementById('line' + selectedLines[next]).className = '';
|
||||
document.getElementById('anchor' + selectedLines[next]).className = 'lineAnchor';
|
||||
}
|
||||
selectedLines = new Array();
|
||||
|
||||
var line = -1;
|
||||
if (theNewHash && theNewHash.match('L[0-9]+-L[0-9]+')) {
|
||||
var dashIndex = theNewHash.indexOf('-');
|
||||
var start = parseInt(theNewHash.substring(2, dashIndex));
|
||||
var end = parseInt(theNewHash.substring(dashIndex+2));
|
||||
for (var i = start; i <= end; i++) {
|
||||
selectedLines.push(i);
|
||||
}
|
||||
} else if (theNewHash && theNewHash.match('L[0-9]+')) {
|
||||
var line = parseInt(theNewHash.substring(2));
|
||||
selectedLines.push(line);
|
||||
}
|
||||
|
||||
|
||||
for (var next in selectedLines) {
|
||||
// Prevent us from scrolling to the selected line
|
||||
document.getElementById('L' + selectedLines[next]).name = '';
|
||||
// Select the line number column
|
||||
document.getElementById('line' + selectedLines[next]).className = 'selectedLine';
|
||||
// Select the response body column
|
||||
document.getElementById('anchor' + selectedLines[next]).className = 'lineAnchor selectedLine';
|
||||
}
|
||||
|
||||
selectedLine = line;
|
||||
}
|
||||
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
updateHighlightedLine();
|
||||
window.onhashchange = updateHighlightedLine;
|
||||
|
||||
/* bail out if user is testing a version of this script via Greasemonkey or Tampermonkey */
|
||||
if (window.HAPI_ResponseHighlighter_userscript) {
|
||||
console.log("HAPI ResponseHighlighter: userscript detected - not executing embedded script");
|
||||
return;
|
||||
}
|
||||
|
||||
/* adds hyperlinks and CSS styles to dates and UUIDs (e.g. to enable user-select: all) */
|
||||
const logicalReferenceRegex = /^[A-Z][A-Za-z]+\/[0-9]+$/;
|
||||
const dateTimeRegex = /^-?[0-9]{4}(-(0[1-9]|1[0-2])(-(0[0-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00)))?)?)?$/; // from the spec - https://www.hl7.org/fhir/datatypes.html#datetime
|
||||
const uuidRegex = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
|
||||
|
||||
const allQuotes = document.querySelectorAll(".hlQuot");
|
||||
for (var i = 0; i < allQuotes.length; i++) {
|
||||
const quote = allQuotes[i];
|
||||
const text = quote.textContent.substr(1, quote.textContent.length - 2 /* remove quotes */);
|
||||
|
||||
const absHyperlink = text.startsWith("http://") || text.startsWith("https://");
|
||||
const relHyperlink = text.match(logicalReferenceRegex);
|
||||
const uuid = text.match(uuidRegex);
|
||||
const dateTime = text.match(dateTimeRegex);
|
||||
|
||||
if (absHyperlink || relHyperlink) {
|
||||
const link = document.createElement("a");
|
||||
const href = absHyperlink ? text : "FHIR_BASE/" + text;
|
||||
link.setAttribute("href", href);
|
||||
link.textContent = '"' + text + '"';
|
||||
quote.textContent = "";
|
||||
quote.appendChild(link);
|
||||
}
|
||||
|
||||
if (uuid || dateTime) {
|
||||
const span = document.createElement("span");
|
||||
span.setAttribute("class", uuid ? "uuid" : "dateTime");
|
||||
span.textContent = text;
|
||||
quote.textContent = "";
|
||||
quote.appendChild(document.createTextNode('"'));
|
||||
quote.appendChild(span);
|
||||
quote.appendChild(document.createTextNode('"'));
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -155,14 +155,18 @@ public class TestRestfulServer extends RestfulServer {
|
|||
config.addAllowedOrigin("*");
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
|
||||
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"));
|
||||
registerInterceptor(corsInterceptor);
|
||||
|
||||
/*
|
||||
* We want to format the response using nice HTML if it's a browser, since this
|
||||
* makes things a little easier for testers.
|
||||
*/
|
||||
registerInterceptor(new ResponseHighlighterInterceptor());
|
||||
ResponseHighlighterInterceptor responseHighlighterInterceptor = new ResponseHighlighterInterceptor();
|
||||
responseHighlighterInterceptor.setShowRequestHeaders(false);
|
||||
responseHighlighterInterceptor.setShowResponseHeaders(true);
|
||||
registerInterceptor(responseHighlighterInterceptor);
|
||||
|
||||
registerInterceptor(new BanUnsupportedHttpMethodsInterceptor());
|
||||
|
||||
/*
|
||||
|
|
|
@ -44,6 +44,7 @@ public class BanUnsupportedHttpMethodsInterceptor extends InterceptorAdapter {
|
|||
myAllowedMethods.add(RequestTypeEnum.DELETE);
|
||||
myAllowedMethods.add(RequestTypeEnum.PUT);
|
||||
myAllowedMethods.add(RequestTypeEnum.POST);
|
||||
myAllowedMethods.add(RequestTypeEnum.PATCH);
|
||||
myAllowedMethods.add(RequestTypeEnum.HEAD);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -24,25 +25,24 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.*;
|
||||
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.RestfulServerUtils.ResponseEncoding;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
/**
|
||||
|
@ -62,23 +62,64 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
* requesting _format=json or xml so eventually this parameter should be removed
|
||||
*/
|
||||
public static final String PARAM_RAW = "_raw";
|
||||
|
||||
public static final String PARAM_RAW_TRUE = "true";
|
||||
|
||||
public static final String PARAM_TRUE = "true";
|
||||
|
||||
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
|
||||
String str = StringEscapeUtils.escapeHtml4(theResultBody);
|
||||
if (str == null || theEncodingEnum == null) {
|
||||
return str;
|
||||
private boolean myShowRequestHeaders = false;
|
||||
private boolean myShowResponseHeaders = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResponseHighlighterInterceptor() {
|
||||
super();
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
private String createLinkHref(Map<String, String[]> parameters, String formatValue) {
|
||||
StringBuilder rawB = new StringBuilder();
|
||||
for (String next : parameters.keySet()) {
|
||||
if (Constants.PARAM_FORMAT.equals(next)) {
|
||||
continue;
|
||||
}
|
||||
for (String nextValue : parameters.get(next)) {
|
||||
if (isBlank(nextValue)) {
|
||||
continue;
|
||||
}
|
||||
if (rawB.length() == 0) {
|
||||
rawB.append('?');
|
||||
} else {
|
||||
rawB.append('&');
|
||||
}
|
||||
rawB.append(UrlUtil.escape(next));
|
||||
rawB.append('=');
|
||||
rawB.append(UrlUtil.escape(nextValue));
|
||||
}
|
||||
}
|
||||
if (rawB.length() == 0) {
|
||||
rawB.append('?');
|
||||
} else {
|
||||
rawB.append('&');
|
||||
}
|
||||
rawB.append(Constants.PARAM_FORMAT).append('=').append(formatValue);
|
||||
|
||||
if (theEncodingEnum == EncodingEnum.JSON) {
|
||||
String link = rawB.toString();
|
||||
return link;
|
||||
}
|
||||
|
||||
private int format(String theResultBody, StringBuilder theTarget, EncodingEnum theEncodingEnum) {
|
||||
String str = StringEscapeUtils.escapeHtml4(theResultBody);
|
||||
if (str == null || theEncodingEnum == null) {
|
||||
theTarget.append(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
theTarget.append("<div id=\"line1\">");
|
||||
|
||||
boolean inValue = false;
|
||||
boolean inQuote = false;
|
||||
boolean inTag = false;
|
||||
int lineCount = 1;
|
||||
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char prevChar = (i > 0) ? str.charAt(i - 1) : ' ';
|
||||
char nextChar = str.charAt(i);
|
||||
|
@ -87,95 +128,99 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
|
||||
if (nextChar == '\n') {
|
||||
lineCount++;
|
||||
theTarget.append("</div><div id=\"line");
|
||||
theTarget.append(lineCount);
|
||||
theTarget.append("\" onclick=\"updateHighlightedLineTo('#L");
|
||||
theTarget.append(lineCount);
|
||||
theTarget.append("');\">");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (theEncodingEnum == EncodingEnum.JSON) {
|
||||
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
theTarget.append(nextChar);
|
||||
if (prevChar != '\\' && nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
theTarget.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
} else if (nextChar == '\\' && nextChar2 == '"') {
|
||||
b.append("quot;</span>");
|
||||
theTarget.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else {
|
||||
if (nextChar == ':') {
|
||||
inValue = true;
|
||||
b.append(nextChar);
|
||||
theTarget.append(nextChar);
|
||||
} else if (nextChar == '[' || nextChar == '{') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
theTarget.append("<span class='hlControl'>");
|
||||
theTarget.append(nextChar);
|
||||
theTarget.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '{' || nextChar == '}' || nextChar == ',') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
theTarget.append("<span class='hlControl'>");
|
||||
theTarget.append(nextChar);
|
||||
theTarget.append("</span>");
|
||||
inValue = false;
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
if (inValue) {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
theTarget.append("<span class='hlQuot'>"");
|
||||
} else {
|
||||
b.append("<span class='hlTagName'>"");
|
||||
theTarget.append("<span class='hlTagName'>"");
|
||||
}
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else if (nextChar == ':') {
|
||||
b.append("<span class='hlControl'>");
|
||||
b.append(nextChar);
|
||||
b.append("</span>");
|
||||
theTarget.append("<span class='hlControl'>");
|
||||
theTarget.append(nextChar);
|
||||
theTarget.append("</span>");
|
||||
inValue = true;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
theTarget.append(nextChar);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
boolean inQuote = false;
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char nextChar = str.charAt(i);
|
||||
char nextChar2 = (i + 1) < str.length() ? str.charAt(i + 1) : ' ';
|
||||
char nextChar3 = (i + 2) < str.length() ? str.charAt(i + 2) : ' ';
|
||||
char nextChar4 = (i + 3) < str.length() ? str.charAt(i + 3) : ' ';
|
||||
char nextChar5 = (i + 4) < str.length() ? str.charAt(i + 4) : ' ';
|
||||
char nextChar6 = (i + 5) < str.length() ? str.charAt(i + 5) : ' ';
|
||||
if (inQuote) {
|
||||
b.append(nextChar);
|
||||
theTarget.append(nextChar);
|
||||
if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("quot;</span>");
|
||||
theTarget.append("quot;</span>");
|
||||
i += 5;
|
||||
inQuote = false;
|
||||
}
|
||||
} else if (inTag) {
|
||||
if (nextChar == '&' && nextChar2 == 'g' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("</span><span class='hlControl'>></span>");
|
||||
theTarget.append("</span><span class='hlControl'>></span>");
|
||||
inTag = false;
|
||||
i += 3;
|
||||
} else if (nextChar == ' ') {
|
||||
b.append("</span><span class='hlAttr'>");
|
||||
b.append(nextChar);
|
||||
theTarget.append("</span><span class='hlAttr'>");
|
||||
theTarget.append(nextChar);
|
||||
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
|
||||
b.append("<span class='hlQuot'>"");
|
||||
theTarget.append("<span class='hlQuot'>"");
|
||||
inQuote = true;
|
||||
i += 5;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
theTarget.append(nextChar);
|
||||
}
|
||||
} else {
|
||||
if (nextChar == '&' && nextChar2 == 'l' && nextChar3 == 't' && nextChar4 == ';') {
|
||||
b.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
theTarget.append("<span class='hlControl'><</span><span class='hlTagName'>");
|
||||
inTag = true;
|
||||
i += 3;
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
theTarget.append(nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
theTarget.append("</div>");
|
||||
return lineCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -213,6 +258,22 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) response will include the
|
||||
* request headers
|
||||
*/
|
||||
public boolean isShowRequestHeaders() {
|
||||
return myShowRequestHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>true</code>) response will include the
|
||||
* response headers
|
||||
*/
|
||||
public boolean isShowResponseHeaders() {
|
||||
return myShowResponseHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
|
@ -283,7 +344,56 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) response will include the
|
||||
* request headers
|
||||
*
|
||||
* @return Returns a reference to this for easy method chaining
|
||||
*/
|
||||
public ResponseHighlighterInterceptor setShowRequestHeaders(boolean theShowRequestHeaders) {
|
||||
myShowRequestHeaders = theShowRequestHeaders;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>true</code>) response will include the
|
||||
* response headers
|
||||
*
|
||||
* @return Returns a reference to this for easy method chaining
|
||||
*/
|
||||
public ResponseHighlighterInterceptor setShowResponseHeaders(boolean theShowResponseHeaders) {
|
||||
myShowResponseHeaders = theShowResponseHeaders;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void streamRequestHeaders(ServletRequest theServletRequest, StringBuilder b) {
|
||||
if (theServletRequest instanceof HttpServletRequest) {
|
||||
HttpServletRequest sr = (HttpServletRequest) theServletRequest;
|
||||
b.append("<h1>Request</h1>");
|
||||
b.append("<div class=\"headersDiv\">");
|
||||
Enumeration<String> headerNamesEnum = sr.getHeaderNames();
|
||||
while (headerNamesEnum.hasMoreElements()) {
|
||||
String nextHeaderName = headerNamesEnum.nextElement();
|
||||
Enumeration<String> headerValuesEnum = sr.getHeaders(nextHeaderName);
|
||||
while (headerValuesEnum.hasMoreElements()) {
|
||||
String nextHeaderValue = headerValuesEnum.nextElement();
|
||||
b.append("<div class=\"headersRow\">");
|
||||
b.append("<span class=\"headerName\">").append(nextHeaderName).append(": ").append("</span>");
|
||||
b.append("<span class=\"headerValue\">").append(nextHeaderValue).append("</span>");
|
||||
b.append("</div>");
|
||||
}
|
||||
}
|
||||
b.append("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
private void streamResponse(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, IBaseResource resource, ServletRequest theServletRequest, int theStatusCode) {
|
||||
|
||||
if (theRequestDetails.getServer() instanceof RestfulServer) {
|
||||
RestfulServer rs = (RestfulServer) theRequestDetails.getServer();
|
||||
rs.addHeadersToResponse(theServletResponse);
|
||||
}
|
||||
|
||||
IParser p;
|
||||
Map<String, String[]> parameters = theRequestDetails.getParameters();
|
||||
if (parameters.containsKey(Constants.PARAM_FORMAT)) {
|
||||
|
@ -322,8 +432,18 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
b.append(" <head>\n");
|
||||
b.append(" <meta charset=\"utf-8\" />\n");
|
||||
b.append(" <style>\n");
|
||||
b.append(".hlQuot {\n");
|
||||
b.append(" color: #88F;\n");
|
||||
b.append(".httpStatusDiv {");
|
||||
b.append(" font-size: 1.2em;");
|
||||
b.append(" font-weight: bold;");
|
||||
b.append("}");
|
||||
b.append(".hlQuot { color: #88F; }\n");
|
||||
b.append(".hlQuot a { text-decoration: underline; text-decoration-color: #CCC; }\n");
|
||||
b.append(".hlQuot a:HOVER { text-decoration: underline; text-decoration-color: #008; }\n");
|
||||
b.append(".hlQuot .uuid, .hlQuot .dateTime {\n");
|
||||
b.append(" user-select: all;\n");
|
||||
b.append(" -moz-user-select: all;\n");
|
||||
b.append(" -webkit-user-select: all;\n");
|
||||
b.append(" -ms-user-select: element;\n");
|
||||
b.append("}\n");
|
||||
b.append(".hlAttr {\n");
|
||||
b.append(" color: #888;\n");
|
||||
|
@ -340,7 +460,12 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
b.append(".hlUrlBase {\n");
|
||||
b.append("}");
|
||||
b.append(".headersDiv {\n");
|
||||
b.append(" background: #EEE;");
|
||||
b.append(" padding: 10px;");
|
||||
b.append(" margin-left: 10px;");
|
||||
b.append(" border: 1px solid #CCC;");
|
||||
b.append(" border-radius: 10px;");
|
||||
b.append("}");
|
||||
b.append(".headersRow {\n");
|
||||
b.append("}");
|
||||
b.append(".headerName {\n");
|
||||
b.append(" color: #888;\n");
|
||||
|
@ -350,6 +475,32 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
b.append(" color: #88F;\n");
|
||||
b.append(" font-family: monospace;\n");
|
||||
b.append("}");
|
||||
b.append(".responseBodyTable {");
|
||||
b.append(" width: 100%;");
|
||||
b.append(" margin-left: 0px;");
|
||||
b.append(" margin-top: 20px;");
|
||||
b.append("}");
|
||||
b.append(".responseBodyTableFirstColumn {");
|
||||
b.append("}");
|
||||
b.append(".responseBodyTableSecondColumn {");
|
||||
b.append(" width: 100%;");
|
||||
b.append("}");
|
||||
b.append(".lineAnchor A {");
|
||||
b.append(" text-decoration: none;");
|
||||
b.append(" padding-left: 20px;");
|
||||
b.append("}");
|
||||
b.append(".lineAnchor {");
|
||||
b.append(" display: block;");
|
||||
b.append(" padding-right: 20px;");
|
||||
b.append("}");
|
||||
b.append(".selectedLine {");
|
||||
b.append(" background-color: #EEF;");
|
||||
b.append(" font-weight: bold;");
|
||||
b.append("}");
|
||||
b.append("H1 {");
|
||||
b.append(" font-size: 1.1em;");
|
||||
b.append(" color: #666;");
|
||||
b.append("}");
|
||||
b.append("BODY {\n");
|
||||
b.append(" font-family: Arial;\n");
|
||||
b.append("}");
|
||||
|
@ -393,19 +544,73 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
|
||||
b.append("\n");
|
||||
|
||||
// if (isEncodeHeaders()) {
|
||||
// b.append("<h1>Request Headers</h1>");
|
||||
// b.append("<div class=\"headersDiv\">");
|
||||
// for (int next : theRequestDetails.get)
|
||||
// b.append("</div>");
|
||||
// b.append("<h1>Response Headers</h1>");
|
||||
// b.append("<div class=\"headersDiv\">");
|
||||
// b.append("</div>");
|
||||
// b.append("<h1>Response Body</h1>");
|
||||
// }
|
||||
b.append("<pre>");
|
||||
b.append(format(encoded, encoding));
|
||||
b.append("</pre>");
|
||||
// status (e.g. HTTP 200 OK)
|
||||
String statusName = Constants.HTTP_STATUS_NAMES.get(theServletResponse.getStatus());
|
||||
statusName = defaultString(statusName);
|
||||
b.append("<div class=\"httpStatusDiv\">");
|
||||
b.append("HTTP ");
|
||||
b.append(theServletResponse.getStatus());
|
||||
b.append(" ");
|
||||
b.append(statusName);
|
||||
b.append("</div>");
|
||||
|
||||
b.append("\n");
|
||||
b.append("\n");
|
||||
|
||||
if (true) {
|
||||
try {
|
||||
if (isShowRequestHeaders()) {
|
||||
streamRequestHeaders(theServletRequest, b);
|
||||
}
|
||||
if (isShowResponseHeaders()) {
|
||||
streamResponseHeaders(theRequestDetails, theServletResponse, b);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// ignore (this will hit if we're running in a servlet 2.5 environment)
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder target = new StringBuilder();
|
||||
int linesCount = format(encoded, target, encoding);
|
||||
|
||||
b.append("<table class=\"responseBodyTable\" cellspacing=\"0\">");
|
||||
b.append("<tr>");
|
||||
|
||||
b.append("<td class=\"responseBodyTableFirstColumn\"><pre>");
|
||||
for (int i = 1; i <= linesCount; i++) {
|
||||
b.append("<div class=\"lineAnchor\" id=\"anchor");
|
||||
b.append(i);
|
||||
b.append("\">");
|
||||
|
||||
b.append("<a href=\"#L");
|
||||
b.append(i);
|
||||
b.append("\" name=\"L");
|
||||
b.append(i);
|
||||
b.append("\" id=\"L");
|
||||
b.append(i);
|
||||
b.append("\">");
|
||||
b.append(i);
|
||||
b.append("</a></div>");
|
||||
}
|
||||
b.append("</pre></td>");
|
||||
|
||||
// Response Body
|
||||
b.append("<td class=\"responseBodyTableSecondColumn\"><pre>");
|
||||
b.append(target);
|
||||
b.append("</pre></td>");
|
||||
|
||||
b.append("</tr>");
|
||||
b.append("</table>");
|
||||
|
||||
b.append("\n");
|
||||
|
||||
InputStream jsStream = ResponseHighlighterInterceptor.class.getResourceAsStream("ResponseHighlighter.js");
|
||||
String jsStr = jsStream != null ? IOUtils.toString(jsStream, "UTF-8") : "console.log('ResponseHighlighterInterceptor: javascript resource not found')";
|
||||
jsStr = jsStr.replace("FHIR_BASE", theRequestDetails.getServerBaseForRequest());
|
||||
b.append("<script type=\"text/javascript\">");
|
||||
b.append(jsStr);
|
||||
b.append("</script>\n");
|
||||
|
||||
b.append("</body>");
|
||||
b.append("</html>");
|
||||
//@formatter:off
|
||||
|
@ -419,35 +624,31 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
private String createLinkHref(Map<String, String[]> parameters, String formatValue) {
|
||||
StringBuilder rawB = new StringBuilder();
|
||||
for (String next : parameters.keySet()) {
|
||||
if (Constants.PARAM_FORMAT.equals(next)) {
|
||||
continue;
|
||||
}
|
||||
for (String nextValue : parameters.get(next)) {
|
||||
if (isBlank(nextValue)) {
|
||||
continue;
|
||||
}
|
||||
if (rawB.length() == 0) {
|
||||
rawB.append('?');
|
||||
} else {
|
||||
rawB.append('&');
|
||||
}
|
||||
rawB.append(UrlUtil.escape(next));
|
||||
rawB.append('=');
|
||||
rawB.append(UrlUtil.escape(nextValue));
|
||||
}
|
||||
}
|
||||
if (rawB.length() == 0) {
|
||||
rawB.append('?');
|
||||
} else {
|
||||
rawB.append('&');
|
||||
}
|
||||
rawB.append(Constants.PARAM_FORMAT).append('=').append(formatValue);
|
||||
private void streamResponseHeaders(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, StringBuilder b) {
|
||||
if (theServletResponse.getHeaderNames().isEmpty() == false) {
|
||||
b.append("<h1>Response</h1>");
|
||||
|
||||
String link = rawB.toString();
|
||||
return link;
|
||||
b.append("<div class=\"headersDiv\">");
|
||||
for (String nextHeaderName : theServletResponse.getHeaderNames()) {
|
||||
for (String nextHeaderValue : theServletResponse.getHeaders(nextHeaderName)) {
|
||||
/*
|
||||
* Let's pretend we're returning a FHIR content type even though we're
|
||||
* actually returning an HTML one
|
||||
*/
|
||||
if (nextHeaderName.equalsIgnoreCase(Constants.HEADER_CONTENT_TYPE)) {
|
||||
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails, theRequestDetails.getServer().getDefaultResponseEncoding());
|
||||
if (responseEncoding != null && isNotBlank(responseEncoding.getResourceContentType())) {
|
||||
nextHeaderValue = responseEncoding.getResourceContentType() + ";charset=utf-8";
|
||||
}
|
||||
}
|
||||
b.append("<div class=\"headersRow\">");
|
||||
b.append("<span class=\"headerName\">").append(nextHeaderName).append(": ").append("</span>");
|
||||
b.append("<span class=\"headerValue\">").append(nextHeaderValue).append("</span>");
|
||||
b.append("</div>");
|
||||
}
|
||||
}
|
||||
b.append("</div>");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -154,6 +154,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
|
|||
|
||||
case CREATE:
|
||||
case UPDATE:
|
||||
case PATCH:
|
||||
// if (theRequestResource != null) {
|
||||
// if (theRequestResource.getIdElement() != null) {
|
||||
// if (theRequestResource.getIdElement().hasIdPart() == false) {
|
||||
|
|
|
@ -42,14 +42,13 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- The JPA project uses a newer API but we'll try to hold to this version as much as possible. See #283. -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>2.5</version>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Testing -->
|
||||
<!-- <dependency> <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-structures-dstu</artifactId> <version>0.8</version> <scope>test</scope> </dependency> -->
|
||||
<dependency>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.containsStringIgnoringCase;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
|
@ -32,8 +33,7 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.*;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
@ -53,14 +53,14 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.*;
|
||||
|
||||
public class ResponseHighlightingInterceptorTest {
|
||||
|
||||
private static ResponseHighlighterInterceptor ourInterceptor = new ResponseHighlighterInterceptor();
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResponseHighlightingInterceptorTest.class);
|
||||
|
@ -74,6 +74,12 @@ public class ResponseHighlightingInterceptorTest {
|
|||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourInterceptor.setShowRequestHeaders(new ResponseHighlighterInterceptor().isShowRequestHeaders());
|
||||
ourInterceptor.setShowResponseHeaders(new ResponseHighlighterInterceptor().isShowResponseHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #464
|
||||
*/
|
||||
|
@ -89,7 +95,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(responseContent, (stringContainsInOrder("<body>", "<pre>", "\n", "</pre>")));
|
||||
assertThat(responseContent, (stringContainsInOrder("<body>", "<pre>", "<div", "</pre>")));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,7 +113,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(responseContent, (stringContainsInOrder("<body>", "<pre>", "\n", "</pre>")));
|
||||
assertThat(responseContent, (stringContainsInOrder("<body>", "<pre>", "<div", "</pre>")));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,6 +148,73 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowNeither() throws Exception {
|
||||
ourInterceptor.setShowRequestHeaders(false);
|
||||
ourInterceptor.setShowResponseHeaders(false);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=html/json");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("text/html;charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertThat(responseContent, not(containsStringIgnoringCase("Accept")));
|
||||
assertThat(responseContent, not(containsStringIgnoringCase("Content-Type")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowResponse() throws Exception {
|
||||
ourInterceptor.setShowResponseHeaders(true);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=html/json");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("text/html;charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertThat(responseContent, not(containsStringIgnoringCase("Accept")));
|
||||
assertThat(responseContent, (containsStringIgnoringCase("Content-Type")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowRequest() throws Exception {
|
||||
ourInterceptor.setShowRequestHeaders(true);
|
||||
ourInterceptor.setShowResponseHeaders(false);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=html/json");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("text/html;charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertThat(responseContent, (containsStringIgnoringCase("Accept")));
|
||||
assertThat(responseContent, not(containsStringIgnoringCase("Content-Type")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowRequestAndResponse() throws Exception {
|
||||
ourInterceptor.setShowRequestHeaders(true);
|
||||
ourInterceptor.setShowResponseHeaders(true);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=html/json");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("text/html;charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertThat(responseContent, (containsStringIgnoringCase("Accept")));
|
||||
assertThat(responseContent, (containsStringIgnoringCase("Content-Type")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInvalidResource() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Foobar/123");
|
||||
|
@ -190,7 +263,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightException() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -207,7 +280,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setServer(new RestfulServer(ourCtx));
|
||||
reqDetails.setServletRequest(req);
|
||||
|
@ -228,7 +301,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightNormalResponseForcePrettyPrint() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -245,7 +318,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_PRETTY, new String[] { Constants.PARAM_PRETTY_VALUE_TRUE });
|
||||
|
@ -258,12 +331,12 @@ public class ResponseHighlightingInterceptorTest {
|
|||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
assertThat(output, containsString("<span class='hlTagName'>Patient</span>"));
|
||||
assertThat(output, stringContainsInOrder("<body>", "<pre>", "\n", "</pre>"));
|
||||
assertThat(output, stringContainsInOrder("<body>", "<pre>", "<div", "</pre>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHighlightForceRaw() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -280,7 +353,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_PRETTY, new String[] { Constants.PARAM_PRETTY_VALUE_TRUE });
|
||||
|
@ -297,7 +370,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testDontHighlightWhenOriginHeaderPresent() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -320,7 +393,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
reqDetails.setParameters(params);
|
||||
|
@ -337,7 +410,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
*/
|
||||
@Test
|
||||
public void testHighlightForceHtmlCt() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -354,7 +427,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_FORMAT, new String[] { Constants.FORMAT_HTML });
|
||||
|
@ -371,7 +444,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
*/
|
||||
@Test
|
||||
public void testHighlightForceHtmlFormat() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -388,7 +461,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_FORMAT, new String[] { Constants.CT_HTML });
|
||||
|
@ -402,7 +475,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightNormalResponse() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
when(req.getHeaders(Constants.HEADER_ACCEPT)).thenAnswer(new Answer<Enumeration<String>>() {
|
||||
|
@ -419,7 +492,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
reqDetails.setServer(new RestfulServer(ourCtx));
|
||||
|
@ -430,7 +503,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
assertThat(output, containsString("<span class='hlTagName'>Patient</span>"));
|
||||
assertThat(output, stringContainsInOrder("<body>", "<pre>", "\n", "</pre>"));
|
||||
assertThat(output, stringContainsInOrder("<body>", "<pre>", "<div", "</pre>"));
|
||||
assertThat(output, containsString("<a href=\"?_format=json\">"));
|
||||
}
|
||||
|
||||
|
@ -439,7 +512,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
*/
|
||||
@Test
|
||||
public void testHighlightProducesDefaultJsonWithBrowserRequest() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
|
||||
|
@ -457,7 +530,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
RestfulServer server = new RestfulServer(ourCtx);
|
||||
|
@ -477,7 +550,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
@Test
|
||||
public void testHighlightProducesDefaultJsonWithBrowserRequest2() throws Exception {
|
||||
ResponseHighlighterInterceptor ic = new ResponseHighlighterInterceptor();
|
||||
ResponseHighlighterInterceptor ic = ourInterceptor;
|
||||
|
||||
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
|
||||
|
@ -495,7 +568,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
ServletRequestDetails reqDetails = new TestServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
RestfulServer server = new RestfulServer(ourCtx);
|
||||
|
@ -712,7 +785,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
|
||||
ourServlet.registerInterceptor(corsInterceptor);
|
||||
|
||||
ourServlet.registerInterceptor(new ResponseHighlighterInterceptor());
|
||||
ourServlet.registerInterceptor(ourInterceptor);
|
||||
ourServlet.setResourceProviders(patientProvider, new DummyBinaryResourceProvider());
|
||||
ourServlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
|
||||
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||
|
@ -756,9 +829,6 @@ public class ResponseHighlightingInterceptorTest {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
private Patient createPatient1() {
|
||||
|
@ -861,4 +931,11 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
}
|
||||
|
||||
class TestServletRequestDetails extends ServletRequestDetails {
|
||||
@Override
|
||||
public String getServerBaseForRequest() {
|
||||
return "/baseDstu3";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1818,6 +1818,43 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritePatchByInstance() throws Exception {
|
||||
ourConditionalCreateId = "1";
|
||||
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("Rule 1").write().instance("Patient/900").andThen()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
});
|
||||
|
||||
HttpEntityEnclosingRequestBase httpPost;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
String input = "[ { \"op\": \"replace\", \"path\": \"/gender\", \"value\": \"male\" } ]";
|
||||
|
||||
ourHitMethod = false;
|
||||
httpPost = new HttpPatch("http://localhost:" + ourPort + "/Patient/900");
|
||||
httpPost.setEntity(new StringEntity(input, ContentType.parse("application/json-patch+json")));
|
||||
status = ourClient.execute(httpPost);
|
||||
response = extractResponseAndClose(status);
|
||||
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpPost = new HttpPatch("http://localhost:" + ourPort + "/Patient/999");
|
||||
httpPost.setEntity(new StringEntity(input, ContentType.parse("application/json-patch+json")));
|
||||
status = ourClient.execute(httpPost);
|
||||
response = extractResponseAndClose(status);
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteByInstance() throws Exception {
|
||||
|
@ -1875,7 +1912,6 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReadByInstance() throws Exception {
|
||||
ourConditionalCreateId = "1";
|
||||
|
@ -1923,7 +1959,6 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
|
@ -2110,7 +2145,6 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
|
@ -2184,6 +2218,14 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Patch()
|
||||
public MethodOutcome patch(@IdParam IdDt theId, @ResourceParam String theResource, PatchTypeEnum thePatchType) {
|
||||
ourHitMethod = true;
|
||||
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Validate
|
||||
public MethodOutcome validate(@ResourceParam Patient theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding,
|
||||
@Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile, RequestDetails theRequestDetails) {
|
||||
|
@ -2226,5 +2268,4 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.io.InputStreamReader;
|
|||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.Charsets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
|
@ -22,6 +23,10 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
|
||||
public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||
|
||||
private static final String URL_PREFIX_VALUE_SET = "http://hl7.org/fhir/ValueSet/";
|
||||
private static final String URL_PREFIX_STRUCTURE_DEFINITION = "http://hl7.org/fhir/StructureDefinition/";
|
||||
private static final String URL_PREFIX_STRUCTURE_DEFINITION_BASE = "http://hl7.org/fhir/";
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
|
||||
|
||||
private Map<String, CodeSystem> myCodeSystems;
|
||||
|
@ -86,51 +91,28 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
Validate.notBlank(theUri, "theUri must not be null or blank");
|
||||
|
||||
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
|
||||
if (theClass.equals(StructureDefinition.class)) {
|
||||
return (T) fetchStructureDefinition(theContext, theUri);
|
||||
}
|
||||
if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
|
||||
if (theClass.equals(ValueSet.class) || theUri.startsWith(URL_PREFIX_VALUE_SET)) {
|
||||
return (T) fetchValueSet(theContext, theUri);
|
||||
}
|
||||
// if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
// Map<String, ValueSet> defaultValueSets = myDefaultValueSets;
|
||||
// if (defaultValueSets == null) {
|
||||
// String path = theContext.getVersion().getPathToSchemaDefinitions().replace("/schema", "/valueset") + "/valuesets.xml";
|
||||
// InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(path);
|
||||
// if (valuesetText == null) {
|
||||
// return null;
|
||||
// }
|
||||
// InputStreamReader reader;
|
||||
// try {
|
||||
// reader = new InputStreamReader(valuesetText, "UTF-8");
|
||||
// } catch (UnsupportedEncodingException e) {
|
||||
// // Shouldn't happen!
|
||||
// throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
|
||||
// }
|
||||
//
|
||||
// defaultValueSets = new HashMap<String, ValueSet>();
|
||||
//
|
||||
// Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
|
||||
// for (BundleEntryComponent next : bundle.getEntry()) {
|
||||
// IdType nextId = new IdType(next.getFullUrl());
|
||||
// if (nextId.isEmpty() || !nextId.getValue().startsWith("http://hl7.org/fhir/ValueSet/")) {
|
||||
// continue;
|
||||
// }
|
||||
// defaultValueSets.put(nextId.toVersionless().getValue(), (ValueSet) next.getResource());
|
||||
// }
|
||||
//
|
||||
// myDefaultValueSets = defaultValueSets;
|
||||
// }
|
||||
//
|
||||
// return (T) defaultValueSets.get(theUri);
|
||||
// }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) {
|
||||
return provideStructureDefinitionMap(theContext).get(theUrl);
|
||||
String url = theUrl;
|
||||
if (url.startsWith(URL_PREFIX_STRUCTURE_DEFINITION)) {
|
||||
// no change
|
||||
} else if (url.indexOf('/') == -1) {
|
||||
url = URL_PREFIX_STRUCTURE_DEFINITION + url;
|
||||
} else if (StringUtils.countMatches(url, '/') == 1) {
|
||||
url = URL_PREFIX_STRUCTURE_DEFINITION_BASE + url;
|
||||
}
|
||||
return provideStructureDefinitionMap(theContext).get(url);
|
||||
}
|
||||
|
||||
ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -21,9 +21,9 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
*/
|
||||
public class PrePopulatedValidationSupport implements IValidationSupport {
|
||||
|
||||
private Map<String, CodeSystem> myCodeSystems;
|
||||
private Map<String, StructureDefinition> myStructureDefinitions;
|
||||
private Map<String, ValueSet> myValueSets;
|
||||
private Map<String, CodeSystem> myCodeSystems;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -34,37 +34,6 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
myCodeSystems = new HashMap<String, CodeSystem>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new StructureDefinition resource which will be available to the validator. Note that
|
||||
* {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this
|
||||
* value will be used as the logical URL.
|
||||
*/
|
||||
public void addStructureDefinition(StructureDefinition theStructureDefinition) {
|
||||
Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value");
|
||||
myStructureDefinitions.put(theStructureDefinition.getUrl(), theStructureDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new ValueSet resource which will be available to the validator. Note that
|
||||
* {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this
|
||||
* value will be used as the logical URL.
|
||||
*/
|
||||
public void addValueSet(ValueSet theValueSet) {
|
||||
Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value");
|
||||
myValueSets.put(theValueSet.getUrl(), theValueSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new CodeSystem resource which will be available to the validator. Note that
|
||||
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
|
||||
* value will be used as the logical URL.
|
||||
*/
|
||||
public void addCodeSystem(CodeSystem theCodeSystem) {
|
||||
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
|
||||
myCodeSystems.put(theCodeSystem.getUrl(), theCodeSystem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
@ -84,6 +53,79 @@ public class PrePopulatedValidationSupport implements IValidationSupport {
|
|||
myCodeSystems = theCodeSystems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new CodeSystem resource which will be available to the validator. Note that
|
||||
* {@link CodeSystem#getUrl() the URL field) in this resource must contain a value as this
|
||||
* value will be used as the logical URL.
|
||||
* <p>
|
||||
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
|
||||
* it will be stored in three ways:
|
||||
* <ul>
|
||||
* <li>Extension</li>
|
||||
* <li>StructureDefinition/Extension</li>
|
||||
* <li>http://hl7.org/StructureDefinition/Extension</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public void addCodeSystem(CodeSystem theCodeSystem) {
|
||||
Validate.notBlank(theCodeSystem.getUrl(), "theCodeSystem.getUrl() must not return a value");
|
||||
addToMap(theCodeSystem, myCodeSystems, theCodeSystem.getUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new StructureDefinition resource which will be available to the validator. Note that
|
||||
* {@link StructureDefinition#getUrl() the URL field) in this resource must contain a value as this
|
||||
* value will be used as the logical URL.
|
||||
* <p>
|
||||
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
|
||||
* it will be stored in three ways:
|
||||
* <ul>
|
||||
* <li>Extension</li>
|
||||
* <li>StructureDefinition/Extension</li>
|
||||
* <li>http://hl7.org/StructureDefinition/Extension</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public void addStructureDefinition(StructureDefinition theStructureDefinition) {
|
||||
Validate.notBlank(theStructureDefinition.getUrl(), "theStructureDefinition.getUrl() must not return a value");
|
||||
addToMap(theStructureDefinition, myStructureDefinitions, theStructureDefinition.getUrl());
|
||||
}
|
||||
|
||||
private <T extends MetadataResource> void addToMap(T theStructureDefinition, Map<String, T> map, String theUrl) {
|
||||
if (isNotBlank(theUrl)) {
|
||||
map.put(theUrl, theStructureDefinition);
|
||||
|
||||
int lastSlashIdx = theUrl.lastIndexOf('/');
|
||||
if (lastSlashIdx != -1) {
|
||||
map.put(theUrl.substring(lastSlashIdx + 1), theStructureDefinition);
|
||||
int previousSlashIdx = theUrl.lastIndexOf('/', lastSlashIdx - 1);
|
||||
if (previousSlashIdx != -1) {
|
||||
map.put(theUrl.substring(previousSlashIdx + 1), theStructureDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new ValueSet resource which will be available to the validator. Note that
|
||||
* {@link ValueSet#getUrl() the URL field) in this resource must contain a value as this
|
||||
* value will be used as the logical URL.
|
||||
* <p>
|
||||
* Note that if the URL is a canonical FHIR URL (e.g. http://hl7.org/StructureDefinition/Extension),
|
||||
* it will be stored in three ways:
|
||||
* <ul>
|
||||
* <li>Extension</li>
|
||||
* <li>StructureDefinition/Extension</li>
|
||||
* <li>http://hl7.org/StructureDefinition/Extension</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
public void addValueSet(ValueSet theValueSet) {
|
||||
Validate.notBlank(theValueSet.getUrl(), "theValueSet.getUrl() must not return a value");
|
||||
addToMap(theValueSet, myValueSets, theValueSet.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class DefaultProfileValidationSupportTest {
|
||||
|
||||
private DefaultProfileValidationSupport mySvc = new DefaultProfileValidationSupport();
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
|
||||
@Test
|
||||
public void testGetStructureDefinitionsWithRelativeUrls() {
|
||||
assertNotNull(mySvc.fetchStructureDefinition(ourCtx, "http://hl7.org/fhir/StructureDefinition/Extension"));
|
||||
assertNotNull(mySvc.fetchStructureDefinition(ourCtx, "StructureDefinition/Extension"));
|
||||
assertNotNull(mySvc.fetchStructureDefinition(ourCtx, "Extension"));
|
||||
|
||||
assertNull(mySvc.fetchStructureDefinition(ourCtx, "http://hl7.org/fhir/StructureDefinition/Extension2"));
|
||||
assertNull(mySvc.fetchStructureDefinition(ourCtx, "StructureDefinition/Extension2"));
|
||||
assertNull(mySvc.fetchStructureDefinition(ourCtx, "Extension2"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.hl7.fhir.dstu3.context.IWorkerContext;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.utils.StructureMapUtilities;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
public class StructureMapTest {
|
||||
|
||||
/**
|
||||
* The logger object.
|
||||
*/
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StructureMapTest.class);
|
||||
|
||||
/**
|
||||
* The basic fhir context used to parse structure definitions.
|
||||
*/
|
||||
FhirContext context;
|
||||
|
||||
/**
|
||||
* HapiFhirContext used when building strucutre map utilities.
|
||||
*/
|
||||
IWorkerContext hapiContext;
|
||||
|
||||
/**
|
||||
* path to the files used to test the profile generator.
|
||||
*/
|
||||
String resourcePath = null;
|
||||
|
||||
/**
|
||||
* Used to validate definitions as well as add new structure definitions to a registry.
|
||||
*/
|
||||
PrePopulatedValidationSupport validationSupport;
|
||||
|
||||
public StructureMap.StructureMapGroupRuleSourceComponent buildSource(String context, @Nullable String element, @Nullable String variable, @Nullable String type, @Nullable Integer min,
|
||||
@Nullable String max) {
|
||||
StructureMap.StructureMapGroupRuleSourceComponent retVal = new StructureMap.StructureMapGroupRuleSourceComponent();
|
||||
retVal.setContext(context);
|
||||
if (element != null)
|
||||
retVal.setElement(element);
|
||||
if (variable != null)
|
||||
retVal.setVariable(variable);
|
||||
if (type != null)
|
||||
retVal.setType(type);
|
||||
if (min != null)
|
||||
retVal.setMin(min);
|
||||
if (max != null)
|
||||
retVal.setMax(max);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapGroupRuleSourceComponent> buildSourceList(StructureMap.StructureMapGroupRuleSourceComponent[] sources) {
|
||||
List<StructureMap.StructureMapGroupRuleSourceComponent> retVal = new ArrayList<StructureMap.StructureMapGroupRuleSourceComponent>();
|
||||
retVal.addAll(Arrays.asList(sources));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public StructureMap.StructureMapGroupRuleTargetComponent buildTarget(@Nullable String context, @Nullable String element, @Nullable String variable,
|
||||
@Nullable StructureMap.StructureMapTransform transform, @Nullable TargetParam[] params) throws Exception {
|
||||
StructureMap.StructureMapGroupRuleTargetComponent retVal = new StructureMap.StructureMapGroupRuleTargetComponent();
|
||||
if (context != null)
|
||||
retVal.setContext(context);
|
||||
if (element != null)
|
||||
retVal.setElement(element);
|
||||
if (variable != null)
|
||||
retVal.setVariable(variable);
|
||||
if (transform != null)
|
||||
retVal.setTransform(transform);
|
||||
if (params != null) {
|
||||
if (params.length > 0)
|
||||
retVal.setParameter(this.constructParameters(params));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapGroupRuleTargetComponent> buildTargetList(StructureMap.StructureMapGroupRuleTargetComponent[] sources) {
|
||||
List<StructureMap.StructureMapGroupRuleTargetComponent> retVal = new ArrayList<StructureMap.StructureMapGroupRuleTargetComponent>();
|
||||
retVal.addAll(Arrays.asList(sources));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapGroupComponent> buildTestGroup() throws Exception {
|
||||
List<StructureMap.StructureMapGroupComponent> retVal = new ArrayList<StructureMap.StructureMapGroupComponent>();
|
||||
StructureMap.StructureMapGroupComponent group = new StructureMap.StructureMapGroupComponent();
|
||||
group.setName("TestStructureToCoding");
|
||||
group.setTypeMode(StructureMap.StructureMapGroupTypeMode.TYPEANDTYPES);
|
||||
group.setInput(this.buildTestInput());
|
||||
group.setRule(this.buildTestRules());
|
||||
retVal.add(group);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapGroupInputComponent> buildTestInput() {
|
||||
List<StructureMap.StructureMapGroupInputComponent> retVal = new ArrayList<StructureMap.StructureMapGroupInputComponent>();
|
||||
StructureMap.StructureMapGroupInputComponent sourceIn = new StructureMap.StructureMapGroupInputComponent();
|
||||
StructureMap.StructureMapGroupInputComponent targetIn = new StructureMap.StructureMapGroupInputComponent();
|
||||
sourceIn.setName("source");
|
||||
sourceIn.setType("TestStructure");
|
||||
sourceIn.setMode(StructureMap.StructureMapInputMode.SOURCE);
|
||||
targetIn.setName("target");
|
||||
targetIn.setType("http://hl7.org/fhir/StructureDefinition/Coding");
|
||||
targetIn.setMode(StructureMap.StructureMapInputMode.TARGET);
|
||||
retVal.add(sourceIn);
|
||||
retVal.add(targetIn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapGroupRuleComponent> buildTestRules() throws Exception {
|
||||
List<StructureMap.StructureMapGroupRuleComponent> retVal = new ArrayList<StructureMap.StructureMapGroupRuleComponent>();
|
||||
StructureMap.StructureMapGroupRuleComponent codingSystem = new StructureMap.StructureMapGroupRuleComponent();
|
||||
StructureMap.StructureMapGroupRuleComponent codingExtension = new StructureMap.StructureMapGroupRuleComponent();
|
||||
codingSystem.setName("Coding.System");
|
||||
codingSystem.setSource(this.buildSourceList(new StructureMap.StructureMapGroupRuleSourceComponent[] {
|
||||
this.buildSource("source", "system", "v", null, null, null)
|
||||
}));
|
||||
codingSystem.setTarget(this.buildTargetList(new StructureMap.StructureMapGroupRuleTargetComponent[] {
|
||||
this.buildTarget("target", "system", null, StructureMap.StructureMapTransform.COPY, new TargetParam[] { new TargetParam("Id", "v") })
|
||||
}));
|
||||
codingExtension.setName("Coding.Extension");
|
||||
codingExtension.setSource(this.buildSourceList(new StructureMap.StructureMapGroupRuleSourceComponent[] {
|
||||
this.buildSource("source", "system", "v", null, null, null)
|
||||
}));
|
||||
codingExtension.setTarget(this.buildTargetList(new StructureMap.StructureMapGroupRuleTargetComponent[] {
|
||||
this.buildTarget("target", "extension", "ex", null, new TargetParam[] { new TargetParam("", "") }),
|
||||
this.buildTarget("ex", "url", null, StructureMap.StructureMapTransform.COPY, new TargetParam[] { new TargetParam("Id", "v") }),
|
||||
this.buildTarget("ex", "value", null, StructureMap.StructureMapTransform.COPY, new TargetParam[] { new TargetParam("String", "v") })
|
||||
}));
|
||||
retVal.add(codingSystem);
|
||||
retVal.add(codingExtension);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapGroupRuleTargetParameterComponent> constructParameters(TargetParam[] params) throws Exception {
|
||||
List<StructureMap.StructureMapGroupRuleTargetParameterComponent> parameterComponents = new ArrayList<StructureMap.StructureMapGroupRuleTargetParameterComponent>();
|
||||
for (TargetParam tp : params) {
|
||||
if (tp.getType() == "Id") // TODO: Convert TypeParam.Type into an Enum.
|
||||
parameterComponents.add(new StructureMap.StructureMapGroupRuleTargetParameterComponent().setValue(new IdType().setValue(tp.getValue())));
|
||||
else if (tp.getType() == "String")
|
||||
parameterComponents.add(new StructureMap.StructureMapGroupRuleTargetParameterComponent().setValue((new StringType().setValue(tp.getValue()))));
|
||||
else if (tp.getType() == "Boolean") {
|
||||
boolean bValue = Boolean.getBoolean(tp.getValue());
|
||||
parameterComponents.add(new StructureMap.StructureMapGroupRuleTargetParameterComponent().setValue(new BooleanType().setValue(bValue)));
|
||||
} else if (tp.getType() == "Integer") {
|
||||
int iValue = Integer.getInteger(tp.getValue());
|
||||
parameterComponents.add(new StructureMap.StructureMapGroupRuleTargetParameterComponent().setValue(new IntegerType().setValue(iValue)));
|
||||
} else if (tp.getType() == "Decimal") {
|
||||
long lValue = Long.getLong(tp.getValue());
|
||||
parameterComponents.add(new StructureMap.StructureMapGroupRuleTargetParameterComponent().setValue(new DecimalType(lValue)));
|
||||
}
|
||||
}
|
||||
return parameterComponents;
|
||||
}
|
||||
|
||||
public List<StructureMap.StructureMapStructureComponent> createMapStructureList() {
|
||||
List<StructureMap.StructureMapStructureComponent> retVal = new ArrayList<StructureMap.StructureMapStructureComponent>();
|
||||
StructureMap.StructureMapStructureComponent source = new StructureMap.StructureMapStructureComponent();
|
||||
StructureMap.StructureMapStructureComponent target = new StructureMap.StructureMapStructureComponent();
|
||||
source.setUrl("http://opencimi.org/structuredefinition/TestStructure");
|
||||
source.setMode(StructureMap.StructureMapModelMode.SOURCE);
|
||||
target.setUrl("http://hl7.org/fhir/StructureDefinition/Coding");
|
||||
target.setMode(StructureMap.StructureMapModelMode.TARGET);
|
||||
retVal.add(source);
|
||||
retVal.add(target);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public StructureDefinition.StructureDefinitionDifferentialComponent createTestDiff() {
|
||||
StructureDefinition.StructureDefinitionDifferentialComponent retVal = new StructureDefinition.StructureDefinitionDifferentialComponent();
|
||||
List<ElementDefinition> eList = new ArrayList<ElementDefinition>();
|
||||
ElementDefinition ed0 = new ElementDefinition();
|
||||
// ElementDefinition.ElementDefinitionBaseComponent base = new ElementDefinition.ElementDefinitionBaseComponent();
|
||||
// base.setId("http://hl7.org/fhir/StructureDefinition/Element");
|
||||
ed0.setId("TestStructure");
|
||||
ed0.setSliceName("TestStructure");
|
||||
ed0.setPath("TestStructure");
|
||||
// ed0.setBase(base);
|
||||
ed0.setMin(1);
|
||||
ed0.setMax("1");
|
||||
eList.add(ed0);
|
||||
|
||||
ElementDefinition ed = new ElementDefinition();
|
||||
// ElementDefinition.ElementDefinitionBaseComponent base = new ElementDefinition.ElementDefinitionBaseComponent();
|
||||
// base.setId("http://hl7.org/fhir/StructureDefinition/Element");
|
||||
ed.setId("system");
|
||||
ed.setSliceName("system");
|
||||
ed.setPath("TestStructure.system");
|
||||
// ed.setBase(base);
|
||||
ed.setFixed(new UriType().setValue("HTTP://opencimi.org/structuredefinition/TestStructure.html#Debugging"));
|
||||
// ed.setType(this.createTypeRefList());
|
||||
eList.add(ed);
|
||||
retVal.setElement(eList);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public StructureDefinition.StructureDefinitionSnapshotComponent createTestSnapshot() {
|
||||
StructureDefinition.StructureDefinitionSnapshotComponent retVal = new StructureDefinition.StructureDefinitionSnapshotComponent();
|
||||
List<ElementDefinition> eList = new ArrayList<ElementDefinition>();
|
||||
ElementDefinition ed0 = new ElementDefinition();
|
||||
// ElementDefinition.ElementDefinitionBaseComponent base = new ElementDefinition.ElementDefinitionBaseComponent();
|
||||
// base.setId("http://hl7.org/fhir/StructureDefinition/Element");
|
||||
ed0.setId("TestStructure");
|
||||
ed0.setSliceName("TestStructure");
|
||||
ed0.setPath("TestStructure");
|
||||
// ed0.setBase(base);
|
||||
ed0.setMin(1);
|
||||
ed0.setMax("1");
|
||||
eList.add(ed0);
|
||||
|
||||
ElementDefinition ed = new ElementDefinition();
|
||||
// ElementDefinition.ElementDefinitionBaseComponent base = new ElementDefinition.ElementDefinitionBaseComponent();
|
||||
// base.setId("http://hl7.org/fhir/StructureDefinition/Element");
|
||||
ed.setId("system");
|
||||
ed.setSliceName("system");
|
||||
ed.setPath("TestStructure.system");
|
||||
// ed.setBase(base);
|
||||
ed.setFixed(new UriType().setValue("HTTP://opencimi.org/structuredefinition/TestStructure.html#Debugging"));
|
||||
// ed.setType(this.createTypeRefList());
|
||||
ed.setMin(1);
|
||||
ed.setMax("1");
|
||||
eList.add(ed);
|
||||
retVal.setElement(eList);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
public StructureDefinition createTestStructure() {
|
||||
StructureDefinition sd = new StructureDefinition();
|
||||
sd.setId("TestStructure");
|
||||
sd.setUrl("http://opencimi.org/structuredefinition/TestStructure");
|
||||
sd.setStatus(Enumerations.PublicationStatus.DRAFT);
|
||||
sd.setName("TestStructure");
|
||||
sd.setType("TestStructure");
|
||||
sd.setSnapshot(this.createTestSnapshot());
|
||||
sd.setDifferential(this.createTestDiff());
|
||||
sd.setKind(StructureDefinition.StructureDefinitionKind.LOGICAL);
|
||||
|
||||
return sd;
|
||||
}
|
||||
|
||||
public StructureMap createTestStructuremap() throws Exception {
|
||||
StructureMap retMap = new StructureMap();
|
||||
retMap.setUrl("http://opencimi.org/structuremap/testtransform");
|
||||
retMap.setName("TestTransform");
|
||||
retMap.setStatus(Enumerations.PublicationStatus.DRAFT);
|
||||
retMap.setStructure(this.createMapStructureList());
|
||||
retMap.setGroup(this.buildTestGroup());
|
||||
return retMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the resource paths as well as create the contexts using a defalut validator to start with.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
if (this.context == null) {
|
||||
this.context = FhirContext.forDstu3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See #682
|
||||
*/
|
||||
@Test
|
||||
public void testMappingTransform() throws Exception {
|
||||
Map<String, StructureMap> maps = new HashMap<String, StructureMap>(); // Instantiate a hashmap for StructureMaps
|
||||
this.validationSupport = new PrePopulatedValidationSupport(); // Create Validation Instance
|
||||
for (StructureDefinition sd : new DefaultProfileValidationSupport().fetchAllStructureDefinitions(this.context)) { // Read in the default Structure Definitions into a validator that allows custom
|
||||
// declared structure definitions.
|
||||
this.validationSupport.addStructureDefinition(sd);
|
||||
}
|
||||
StructureDefinition sd1 = this.createTestStructure(); // Calls a method that constructs a comp
|
||||
this.validationSupport.addStructureDefinition(sd1); // Add custom structure to validation support.
|
||||
this.hapiContext = new HapiWorkerContext(this.context, this.validationSupport);// Init the Hapi Work
|
||||
StructureMap map = this.createTestStructuremap();
|
||||
maps.put(map.getUrl(), map);
|
||||
StructureMapUtilities scu = new StructureMapUtilities(hapiContext, maps, null, null);
|
||||
List<StructureDefinition> result = scu.analyse(null, map).getProfiles();
|
||||
|
||||
assertEquals(1, result.size());
|
||||
|
||||
ourLog.info(context.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.get(0)));
|
||||
}
|
||||
|
||||
public class TargetParam {
|
||||
private String type;
|
||||
|
||||
private String value;
|
||||
|
||||
public TargetParam(String type, String value) {
|
||||
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
6
pom.xml
6
pom.xml
|
@ -47,7 +47,7 @@
|
|||
</snapshots>
|
||||
<id>bintray-dnault-maven</id>
|
||||
<name>bintray</name>
|
||||
<url>http://dl.bintray.com/dnault/maven</url>
|
||||
<url>https://dl.bintray.com/dnault/maven</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
|
@ -337,6 +337,10 @@
|
|||
<id>ohr</id>
|
||||
<name>Christian Ohr</name>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>eug48</id>
|
||||
<name>Eugene Lubarsky</name>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<licenses>
|
||||
|
|
|
@ -161,6 +161,31 @@
|
|||
XML encoding because of the browser Accept header even though
|
||||
this was not what the client wanted.
|
||||
</action>
|
||||
<action type="add" issue="651">
|
||||
Enhancement to ResponseHighlighterInterceptor where links in the resource
|
||||
body are now converted to actual clickable hyperlinks. Thanks to Eugene Lubarsky
|
||||
for the pull request!
|
||||
</action>
|
||||
<action type="add">
|
||||
BanUnsupportedHttpMethodsInterceptor has been modified so that it now allows
|
||||
HTTP PATCH to proceed.
|
||||
</action>
|
||||
<action type="add" issue="651">
|
||||
Enhancement to ResponseHighlighterInterceptor so that it now can be configured
|
||||
to display the request headers and response headers, and individual lines
|
||||
may be highlighted.
|
||||
</action>
|
||||
<action type="fix">
|
||||
AuthorizationInterceptor did not permit PATCH operations to proceed even
|
||||
if the user had write access for the resource being patched.
|
||||
</action>
|
||||
<action type="fix" issue="682">
|
||||
Fix an issue in HapiWorkerContext where structure definitions are
|
||||
not able to be retrieved if they are referred to by their
|
||||
relative or logical ID. This affects profile tooling such as
|
||||
StructureMapUtilities. Thanks to Travis Lukach for reporting and
|
||||
providing a test case!
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.5" date="2017-06-08">
|
||||
<action type="fix">
|
||||
|
|
|
@ -1441,6 +1441,29 @@ If-Match: W/"3"]]></pre>
|
|||
<a name="exceptions" />
|
||||
</section>
|
||||
|
||||
|
||||
<section name="Instance Level - Patch">
|
||||
|
||||
<p>
|
||||
HAPI FHIR includes basic support for the
|
||||
<a href="http://hl7.org/implement/standards/fhir/http.html#patch">
|
||||
<b>patch</b>
|
||||
</a>
|
||||
operation. This support allows you to perform patches, but does not
|
||||
include logic to actually implement resource patching in the server
|
||||
framework (note that the JPA server does include a patch implementation).
|
||||
</p>
|
||||
<p>
|
||||
The following snippet shows how to define a patch method on a server:
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="patch" />
|
||||
<param name="file" value="examples/src/main/java/example/PatchExamples.java" />
|
||||
</macro>
|
||||
</patch>
|
||||
|
||||
|
||||
<!-- ****************************************************************** -->
|
||||
<!-- ****************************************************************** -->
|
||||
<!-- ****************************************************************** -->
|
||||
|
|
Loading…
Reference in New Issue