More tester enhancements

This commit is contained in:
jamesagnew 2014-06-26 18:06:23 -04:00
parent 97e17f9149
commit 4cd7ff16fb
10 changed files with 497 additions and 315 deletions

View File

@ -19,7 +19,7 @@
<distributionManagement>
<site>
<id>git.server</id>
<url>scm:git:ssh://git@github.com:jamesagnew/hapi-fhir.git</url>
<url>scm:git:git@github.com:jamesagnew/hapi-fhir.git</url>
</site>
<!-- <site> <id>hl7api.sf.net</id> <url>scp://shell.sourceforge.net/home/project-web/hl7api/htdocs/hapi-fhir</url>
@ -224,56 +224,19 @@
<escapeHTML>false</escapeHTML>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.16</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
</configuration>
<reportSets>
<reportSet>
<id>default</id>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.4</version>
<reportSets>
<reportSet>
<id>normal</id>
<reports>
<report>jxr</report>
</reports>
</reportSet>
<reportSet>
<id>restful-server-example</id>
<reports>
<report>jxr</report>
</reports>
<configuration>
<sourcePath>../restful-server-example/src/main/java</sourcePath>
<destDir>${project.reporting.outputDirectory}/rse-xref</destDir>
<outputDirectory>tmp</outputDirectory>
<reportOutputDirectory>rse-xref</reportOutputDirectory>
</configuration>
</reportSet>
</reportSets>
</plugin>
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId>
<version>2.16</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <version>2.9.1</version> <configuration>
</configuration> <reportSets> <reportSet> <id>default</id> <reports> <report>javadoc</report>
</reports> </reportSet> </reportSets> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId> <version>2.5.3</version> </plugin>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jxr-plugin</artifactId>
<version>2.4</version> <reportSets> <reportSet> <id>normal</id> <reports>
<report>jxr</report> </reports> </reportSet> <reportSet> <id>restful-server-example</id>
<reports> <report>jxr</report> </reports> <configuration> <sourcePath>../restful-server-example/src/main/java</sourcePath>
<destDir>${project.reporting.outputDirectory}/rse-xref</destDir> <outputDirectory>tmp</outputDirectory>
<reportOutputDirectory>rse-xref</reportOutputDirectory> </configuration>
</reportSet> </reportSets> </plugin> -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
@ -296,8 +259,10 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<siteMainDirectory>${user.home}/sites/hapi-fhir</siteMainDirectory>
<scmPubCheckoutDirectory>${user.home}/sites/scm/hapi-fhir</scmPubCheckoutDirectory>
</properties>
<build>
<pluginManagement>
<plugins>
@ -305,7 +270,7 @@
<artifactId>maven-site-plugin</artifactId>
<configuration>
<skip>false</skip>
<skipDeploy>false</skipDeploy>
<skipDeploy>true</skipDeploy>
</configuration>
<dependencies>
<dependency>
@ -462,6 +427,42 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<executions>
<execution>
<id>stage-for-scm-publish</id>
<phase>post-site</phase>
<goals>
<goal>stage</goal>
</goals>
<configuration>
<stagingDirectory>${siteMainDirectory}</stagingDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-scm-publish-plugin</artifactId>
<version>1.1</version>
<configuration>
<checkoutDirectory>${scmPubCheckoutDirectory}</checkoutDirectory>
<content>\${siteMainDirectory}</content>
<tryUpdate>true</tryUpdate>
<scmBranch>gh-pages</scmBranch>
<pubScmUrl>scm:git:git@github.com:jamesagnew/hapi-fhir.git</pubScmUrl>
</configuration>
<executions>
<execution>
<id>scm-publish</id>
<phase>site-deploy</phase>
<goals>
<goal>publish-scm</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>

View File

@ -30,6 +30,8 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.servlet.http.HttpServletResponse;
@ -60,7 +62,9 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.IncludeParameter;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.param.SearchParameter;
import ca.uhn.fhir.rest.server.BundleProviders;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -97,6 +101,16 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
myParameters = ParameterUtil.getResourceParameters(theMethod);
}
public Set<String> getIncludes() {
Set<String> retVal =new TreeSet<String>();
for (IParameter next : myParameters) {
if (next instanceof IncludeParameter) {
retVal.addAll(((IncludeParameter) next).getAllow());
}
}
return retVal;
}
public FhirContext getContext() {
return myContext;
}

View File

@ -40,6 +40,10 @@ public class IncludeParameter extends BaseQueryParameter {
private Class<? extends Collection<Include>> myInstantiableCollectionType;
private HashSet<String> myAllow;
public HashSet<String> getAllow() {
return myAllow;
}
private Class<?> mySpecType;
public IncludeParameter(IncludeParam theAnnotation, Class<? extends Collection<Include>> theInstantiableCollectionType, Class<?> theSpecType) {

View File

@ -28,8 +28,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
@ -98,6 +102,8 @@ public class ServerConformanceProvider {
resource.getType().setValue(def.getName());
resource.getProfile().setId(new IdDt(def.getResourceProfile()));
TreeSet<String> includes = new TreeSet<String>();
Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String, Conformance.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
RestfulOperationTypeEnum resOp = nextMethodBinding.getResourceOperationType();
@ -117,7 +123,10 @@ public class ServerConformanceProvider {
}
if (nextMethodBinding instanceof SearchMethodBinding) {
List<IParameter> params = ((SearchMethodBinding) nextMethodBinding).getParameters();
SearchMethodBinding searchMethodBinding = (SearchMethodBinding) nextMethodBinding;
includes.addAll(searchMethodBinding.getIncludes());
List<IParameter> params = searchMethodBinding.getParameters();
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
for (IParameter nextParameter : params) {
if ((nextParameter instanceof SearchParameter)) {
@ -128,12 +137,12 @@ public class ServerConformanceProvider {
@Override
public int compare(SearchParameter theO1, SearchParameter theO2) {
if (theO1.isRequired() == theO2.isRequired()) {
return 0;
return theO1.getName().compareTo(theO2.getName());
}
if (theO1.isRequired()) {
return 1;
return -1;
}
return -1;
return 1;
}
});
if (searchParameters.isEmpty()) {
@ -145,11 +154,33 @@ public class ServerConformanceProvider {
ExtensionDt searchParamChain = null;
for (SearchParameter nextParameter : searchParameters) {
String nextParamName = nextParameter.getName();
String chain = null;
if (nextParamName.contains(".")) {
chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamName = nextParamName.substring(0, nextParamName.indexOf('.'));
}
String nextParamDescription = nextParameter.getDescription();
/*
* If the parameter has no description, default to the one from the resource
*/
if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamName);
if (paramDef != null) {
nextParamDescription = paramDef.getDescription();
}
}
if (searchParam == null || allOptional) {
if (!nameToSearchParam.containsKey(nextParameter.getName())) {
RestResourceSearchParam param = resource.addSearchParam();
param.setName(nextParameter.getName());
param.setDocumentation(nextParameter.getDescription());
param.setName(nextParamName);
if (StringUtils.isNotBlank(chain)) {
param.addChain(chain);
}
param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType());
searchParam = param;
} else {
@ -162,22 +193,20 @@ public class ServerConformanceProvider {
} else {
searchParamChain = searchParam.addUndeclaredExtension(false, ExtensionConstants.CONF_ADDITIONAL_PARAM);
// if (searchParamChain == null) {
// } else {
// searchParamChain = searchParamChain.addUndeclaredExtension(false,
// ExtensionConstants.CONF_ADDITIONAL_PARAM);
// }
if (searchParamChain == null) {
searchParamChain = searchParam.addUndeclaredExtension(false, ExtensionConstants.CONF_ADDITIONAL_PARAM);
} else {
searchParamChain = searchParamChain.addUndeclaredExtension(false, ExtensionConstants.CONF_ADDITIONAL_PARAM);
}
ExtensionDt ext = new ExtensionDt();
ext.setUrl(ExtensionConstants.CONF_ADDITIONAL_PARAM_NAME);
ext.setValue(new StringDt(nextParameter.getName()));
ext.setValue(new StringDt(nextParamName));
searchParamChain.getUndeclaredExtensions().add(ext);
ext = new ExtensionDt();
ext.setUrl(ExtensionConstants.CONF_ADDITIONAL_PARAM_DESCRIPTION);
ext.setValue(new StringDt(nextParameter.getDescription()));
ext.setValue(new StringDt(nextParamDescription));
searchParamChain.getUndeclaredExtensions().add(ext);
ext = new ExtensionDt();
@ -217,6 +246,10 @@ public class ServerConformanceProvider {
}
for (String nextInclude : includes) {
resource.addSearchInclude(nextInclude);
}
}
myConformance = retVal;
@ -224,8 +257,7 @@ public class ServerConformanceProvider {
}
/**
* Sets the cache property (default is true). If set to true, the same response will be returned for each
* invocation.
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
*/
public void setCache(boolean theCache) {
myCache = theCache;

View File

@ -20,12 +20,13 @@ package ca.uhn.fhir.rest.server.tester;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultIfEmpty;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -43,12 +44,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.WriterOutputStream;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
@ -73,11 +71,10 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Metadata;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.StringParam;
@ -122,6 +119,8 @@ public class RestfulTesterServlet extends HttpServlet {
myStaticResources.put("css/tester.css", "text/css");
myStaticResources.put("img/hapi_fhir_banner.png", "image/png");
myStaticResources.put("img/hapi_fhir_banner_right.png", "image/png");
myStaticResources.put("js/RestfulTester.js", "text/javascript");
myStaticResources.put("js/bootstrap.min.js", "text/javascript");
myStaticResources.put("js/jquery-2.1.0.min.js", "text/javascript");
@ -361,7 +360,8 @@ public class RestfulTesterServlet extends HttpServlet {
} else if (body.startsWith("<")) {
resource = myCtx.newXmlParser().parseResource(body);
} else {
theContext.getVariables().put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
theContext.getVariables().put("errorMsg",
"Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
return;
}
} catch (DataFormatException e) {
@ -400,36 +400,11 @@ public class RestfulTesterServlet extends HttpServlet {
while (true) {
paramIdx++;
String nextName = theReq.getParameter("param." + paramIdx + ".name");
if (isBlank(nextName)) {
String paramIdxString = Integer.toString(paramIdx);
boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query);
if (!shouldContinue) {
break;
}
String nextType = theReq.getParameter("param." + paramIdx + ".type");
StringBuilder b = new StringBuilder();
for (int i = 0; i < 100; i++) {
b.append(defaultString(theReq.getParameter("param." + paramIdx + "." + i)));
}
String paramValue = b.toString();
if (isBlank(paramValue)) {
continue;
}
if ("token".equals(nextType)) {
if (paramValue.length() < 2) {
continue;
}
}
// if ("xml".equals(theReq.getParameter("encoding"))) {
// query.encodedXml();
// }else if ("json".equals(theReq.getParameter("encoding"))) {
// query.encodedJson();
// }
query.where(new StringParam(nextName).matches().value(paramValue));
}
String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE);
@ -473,34 +448,6 @@ public class RestfulTesterServlet extends HttpServlet {
try {
HttpRequestBase lastRequest = client.getLastRequest();
String requestBody = null;
String requestSyntaxHighlighterClass = null;
if (lastRequest instanceof HttpEntityEnclosingRequest) {
HttpEntityEnclosingRequest lastEERequest = (HttpEntityEnclosingRequest) lastRequest;
HttpEntity lastEE = lastEERequest.getEntity();
if (lastEE.isRepeatable()) {
StringWriter requestCapture = new StringWriter();
lastEE.writeTo(new WriterOutputStream(requestCapture, "UTF-8"));
requestBody = requestCapture.toString();
ContentType ct = ContentType.get(lastEE);
String mimeType = ct.getMimeType();
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
if (ctEnum == null) {
requestSyntaxHighlighterClass = "brush: plain";
} else {
switch (ctEnum) {
case JSON:
requestSyntaxHighlighterClass = "brush: jscript";
break;
case XML:
default:
requestSyntaxHighlighterClass = "brush: xml";
break;
}
}
}
}
String resultSyntaxHighlighterClass;
String requestUrl = lastRequest != null ? lastRequest.getURI().toASCIIString() : null;
String action = client.getLastRequest() != null ? client.getLastRequest().getMethod() : null;
String resultStatus = client.getLastResponse() != null ? client.getLastResponse().getStatusLine().toString() : null;
@ -516,12 +463,10 @@ public class RestfulTesterServlet extends HttpServlet {
Bundle bundle = null;
if (ctEnum == null) {
resultSyntaxHighlighterClass = "brush: plain";
resultDescription.append("Non-FHIR response");
} else {
switch (ctEnum) {
case JSON:
resultSyntaxHighlighterClass = "brush: jscript";
if (returnsResource == ResultType.RESOURCE) {
narrativeString = parseNarrative(ctEnum, resultBody);
resultDescription.append("JSON resource");
@ -532,7 +477,6 @@ public class RestfulTesterServlet extends HttpServlet {
break;
case XML:
default:
resultSyntaxHighlighterClass = "brush: xml";
if (returnsResource == ResultType.RESOURCE) {
narrativeString = parseNarrative(ctEnum, resultBody);
resultDescription.append("XML resource");
@ -557,11 +501,9 @@ public class RestfulTesterServlet extends HttpServlet {
theContext.setVariable("requestUrl", requestUrl);
String requestBodyText = format(requestBody, ctEnum);
theContext.setVariable("requestBody", requestBodyText);
theContext.setVariable("requestSyntaxHighlighterClass", requestSyntaxHighlighterClass);
String resultBodyText = format(resultBody, ctEnum);
theContext.setVariable("resultBody", resultBodyText);
theContext.setVariable("resultBodyIsLong", resultBodyText.length() > 1000);
theContext.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
theContext.setVariable("requestHeaders", requestHeaders);
theContext.setVariable("responseHeaders", responseHeaders);
theContext.setVariable("narrative", narrativeString);
@ -573,6 +515,47 @@ public class RestfulTesterServlet extends HttpServlet {
}
}
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery) {
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
if (isBlank(nextName)) {
return false;
}
String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier"));
String nextType = theReq.getParameter("param." + paramIdxString + ".type");
StringBuilder b = new StringBuilder();
for (int i = 0; i < 100; i++) {
b.append(defaultString(theReq.getParameter("param." + paramIdxString + "." + i)));
}
String paramValue = b.toString();
if (isBlank(paramValue)) {
return true;
}
if ("token".equals(nextType)) {
if (paramValue.length() < 2) {
return true;
}
}
// if ("xml".equals(theReq.getParameter("encoding"))) {
// query.encodedXml();
// }else if ("json".equals(theReq.getParameter("encoding"))) {
// query.encodedJson();
// }
theQuery.where(new StringParam(nextName + nextQualifier).matches().value(paramValue));
if (StringUtils.isNotBlank(theReq.getParameter("param." + paramIdxString + ".0.name"))) {
handleSearchParam(paramIdxString+".0", theReq, theQuery);
}
return true;
}
private String format(String theResultBody, EncodingEnum theEncodingEnum) {
String str = StringEscapeUtils.escapeHtml4(theResultBody);
if (str == null || theEncodingEnum == null) {
@ -586,7 +569,7 @@ public class RestfulTesterServlet extends HttpServlet {
boolean inValue = false;
boolean inQuote = false;
for (int i = 0; i < str.length(); i++) {
char prevChar = (i > 0) ? str.charAt(i-1): ' ';
char prevChar = (i > 0) ? str.charAt(i - 1) : ' ';
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) : ' ';
@ -599,7 +582,7 @@ public class RestfulTesterServlet extends HttpServlet {
b.append("quot;</span>");
i += 5;
inQuote = false;
}else if (nextChar == '\\' && nextChar2 == '"') {
} else if (nextChar == '\\' && nextChar2 == '"') {
b.append("quot;</span>");
i += 5;
inQuote = false;
@ -608,12 +591,12 @@ public class RestfulTesterServlet extends HttpServlet {
if (nextChar == ':') {
inValue = true;
b.append(nextChar);
}else if (nextChar == '[' || nextChar == '[') {
} else if (nextChar == '[' || nextChar == '[') {
b.append("<span class='hlControl'>");
b.append(nextChar);
b.append("</span>");
inValue = false;
}else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
} else if (nextChar == '}' || nextChar == '}' || nextChar == ',') {
b.append("<span class='hlControl'>");
b.append(nextChar);
b.append("</span>");
@ -621,12 +604,12 @@ public class RestfulTesterServlet extends HttpServlet {
} else if (nextChar == '&' && nextChar2 == 'q' && nextChar3 == 'u' && nextChar4 == 'o' && nextChar5 == 't' && nextChar6 == ';') {
if (inValue) {
b.append("<span class='hlQuot'>&quot;");
}else {
b.append("<span class='hlTagName'>&quot;");
} else {
b.append("<span class='hlTagName'>&quot;");
}
inQuote = true;
i += 5;
}else if (nextChar == ':') {
} else if (nextChar == ':') {
b.append("<span class='hlControl'>");
b.append(nextChar);
b.append("</span>");
@ -634,9 +617,9 @@ public class RestfulTesterServlet extends HttpServlet {
} else {
b.append(nextChar);
}
}
}
}
} else {
boolean inQuote = false;
boolean inTag = false;
@ -768,13 +751,16 @@ public class RestfulTesterServlet extends HttpServlet {
if (isNotBlank(resourceName)) {
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
TreeSet<String> includes = new TreeSet<String>();
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != SearchParamTypeEnum.REFERENCE) {
continue;
for (Rest nextRest : conformance.getRest()) {
for (RestResource nextRes : nextRest.getResource()) {
if (nextRes.getType().getValue().equals(resourceName)) {
for (StringDt next : nextRes.getSearchInclude()) {
if (next.isEmpty() == false) {
includes.add(next.getValue());
}
}
}
}
String nextPath = nextSpDef.getPath();
includes.add(nextPath);
}
ctx.setVariable("includes", includes);
@ -828,8 +814,8 @@ public class RestfulTesterServlet extends HttpServlet {
}
/**
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user.
* This can be used, for instance, to filter out "Authorization" headers. Note that names are not case sensitive.
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user. This can be used, for instance, to filter out "Authorization" headers. Note that
* names are not case sensitive.
*/
public void setFilterHeaders(String... theHeaderNames) {
myFilterHeaders = new HashSet<String>();
@ -851,11 +837,6 @@ public class RestfulTesterServlet extends HttpServlet {
}
}
private interface ConformanceClient extends IBasicClient {
@Metadata
Conformance getConformance();
}
private final class ProfileResourceResolver implements IResourceResolver {
@Override

View File

@ -15,14 +15,6 @@
var resourceName = '<th:block th:utext="${resourceName}"/>';
</script>
<!-- Syntaxhighlighter -->
<script src="shCore.js" type="text/javascript"></script>
<script src="shBrushJScript.js" type="text/javascript"></script>
<script src="shBrushXml.js" type="text/javascript"></script>
<script src="shBrushPlain.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="shCore.css"/>
<link rel="stylesheet" type="text/css" href="shThemeDefault.css"/>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet" />
@ -41,6 +33,8 @@
<!-- Fontawesome -->
<link href="fa/css/font-awesome.min.css" rel="stylesheet" />
<script src="js/RestfulTester.js" type="text/javascript" />
</head>
@ -403,133 +397,6 @@
</div>
</div>
</div>
<script type="text/javascript">
/*
$('#search-btn').click(
function() {
$(this).html('<i class="fa fa-cog fa-spin"></i> Searching...')
//btn.button('loading');
});
*/
var numRows = 0;
function addSearchParamRow() {
var nextRow = numRows++;
var select = $('<select/>', {/*style:'margin-left:30px;'*/});
var plusBtn = $('<button />', {type:'button', 'class':'btn btn-success btn-block'});
plusBtn.append($('<span />', {'class':'glyphicon glyphicon-plus'}));
plusBtn.isAdd = true;
var rowDiv = $('<div />', { 'class': 'row top-buffer', id: 'search-param-row-' + nextRow }).append(
$('<div />', { 'class': 'col-sm-1' }).append(plusBtn),
$('<div />', { 'class': 'col-sm-5' }).append(select),
$('<div />', { id: 'search-param-rowopts-' + nextRow })
);
$("#search-param-rows").append(rowDiv);
plusBtn.click(function() {
addSearchParamRow();
});
var params = new Array();
conformance.rest.forEach(function(rest){
rest.resource.forEach(function(restResource){
if (restResource.type == resourceName) {
if (restResource.searchParam) {
restResource.searchParam.forEach(function(searchParam){
params[searchParam.name] = searchParam;
select.append(
$('<option />', { value: searchParam.name }).text(searchParam.name + ' - ' + searchParam.documentation)
);
});
}
}
});
});
select.select2();
handleSearchParamTypeChange(select, params, nextRow);
select.change(function(){ handleSearchParamTypeChange(select, params, nextRow); });
}
function handleSearchParamTypeChange(select, params, rowNum) {
var oldVal = select.prevVal;
var newVal = select.val();
if (oldVal == newVal) {
return;
}
$('#search-param-rowopts-' + rowNum).empty();
var searchParam = params[newVal];
$('#search-param-rowopts-' + rowNum).append(
$('<input />', { name: 'param.' + rowNum + '.name', type: 'hidden', value: searchParam.name }),
$('<input />', { name: 'param.' + rowNum + '.type', type: 'hidden', value: searchParam.type })
);
if (searchParam.type == 'token') {
$('#search-param-rowopts-' + rowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { name: 'param.' + rowNum + '.0', placeholder: 'system/namespace', type: 'text', 'class': 'form-control' })
),
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { name: 'param.' + rowNum + '.1', type: 'hidden', value: '|' }),
$('<input />', { name: 'param.' + rowNum + '.2', placeholder: 'value', type: 'text', 'class': 'form-control' })
)
);
} else if (searchParam.type == 'string' || searchParam.type == 'number') {
$('#search-param-rowopts-' + rowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { name: 'param.' + rowNum + '.0', placeholder: 'value', type: 'text', 'class': 'form-control' })
)
);
} else if (searchParam.type == 'date') {
if (/date$/.test(searchParam.name)) {
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DD' }).append(
$('<input />', { type:'text', 'class':'form-control', name: 'param.' + rowNum + '.1' }),
$('<div />', { 'class':'input-group-addon'}).append(
$('<span />', { 'class':'glyphicon glyphicon-calendar'})
)
);
input.datetimepicker({
pickTime: false,
showToday: true
});
} else {
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DDTHH:mm:ss' }).append(
$('<input />', { type:'text', 'class':'form-control', name: 'param.' + rowNum + '.1' }),
$('<div />', { 'class':'input-group-addon'}).append(
$('<span />', { 'class':'glyphicon glyphicon-calendar'})
)
);
input.datetimepicker({
sideBySide: true,
use24hours: true,
showToday: true
});
}
$('#search-param-rowopts-' + rowNum).append(
$('<div />', { 'class': 'col-sm-2 btn-group' }).append(
$('<select />', {type:'text', name:'param.'+rowNum+'.0', 'class':'form-control', autocomplete:'off'}).append(
$('<option value="">(qualifier)</option>'),
$('<option value="=">=</option>'),
$('<option value=">=">&gt;=</option>'),
$('<option value=">">&gt;</option>'),
$('<option value="&lt;=">&lt;=</option>'),
$('<option value="&lt;">&lt;</option>')
)
),
$('<div />', { 'class': 'col-sm-4' }).append(
input
)
);
}
select.prevVal = newVal;
}
$( document ).ready(function() {
addSearchParamRow();
});
</script>
<div class="panel panel-default" th:if="${resourceName.empty} == false">
<div class="panel-heading">
@ -907,7 +774,7 @@
<td>Request Body</td>
<td valign="top" style="margin: 0px; padding: 0px;">
<pre class="requestBodyPre resultBodyPlaceholder" id="requestBodyPlaceholder">...loading...</pre>
<pre th:utext="${requestBody}" th:class="${requestSyntaxHighlighterClass} + ' resultBodyPre pre'" id="requestBodyActual" style="display: none;">{}</pre>
<pre th:utext="${requestBody}" class="resultBodyPre pre" id="requestBodyActual" style="display: none;">{}</pre>
</td>
</tr>
</tbody>
@ -1051,20 +918,7 @@
<div class="panel-heading" sstyle="margin: 5px;">
<h4 class="panel-title">
Raw Message
<button th:if="${resultBodyIsLong}" class="btn btn-info btn-sm" type="button" id="format-result-btn">
<span class="glyphicon glyphicon-eye-open"></span>
Colour
</button>
</h4>
<script type="text/javascript" th:if="${resultBodyIsLong}">
$('#format-result-btn').click(function() {
//$('#resultBodyActualPre').setClass('<th:block th:text="${resultSyntaxHighlighterClass}"/>');
document.getElementById('resultBodyActualPre').className='<th:block th:text="${resultSyntaxHighlighterClass}"/>';
document.getElementById('format-result-btn').disabled ='disabled';
SyntaxHighlighter.highlight();
lineWrap();
});
</script>
</div>
</td>
</tr>
@ -1072,7 +926,7 @@
<td valign="top" style="margin: 0px; padding: 0px; font-weight: normal;">
<pre class="resultBodyPre resultBodyPlaceholder" id="resultBodyPlaceholder">...loading...</pre>
<div id="resultBodyActual" class="resultBodyActual" style="display: none;">
<pre id="resultBodyActualPre" th:utext="${resultBody}" th:class="(${resultBodyIsLong} ? '' : ${resultSyntaxHighlighterClass}) + ' resultBodyPre pre'">{}</pre>
<pre id="resultBodyActualPre" th:utext="${resultBody}" class="resultBodyPre pre">{}</pre>
</div>
</td>
</tr>
@ -1091,8 +945,6 @@
reqwidth = $('#requestBodyPlaceholder').outerWidth(true);
}
SyntaxHighlighter.all();
$('#resultBodyPlaceholder').hide();
$('#resultBodyActual').width(reswidth);
$('#resultBodyActual').show();

View File

@ -230,6 +230,18 @@ PRE.resultBodyPlaceholder {
padding: 0px;
}
SPAN.searchParamDescription {
font-size: 0.8em;
text-align: justify;
color: #000066;
}
DIV.searchParamSeparator {
height: 1px;
border-top: 1px solid #CCC;
margin: 3px;
}
.select2-container {
width: 100% !important;
}

View File

@ -0,0 +1,195 @@
function addChildParam(searchParam, nextExt) {
var childParam = new Object();
for (var extIdx = 0; extIdx < nextExt.extension.length; extIdx++) {
var childExt = nextExt.extension[extIdx];
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamName") {
childParam.name = childExt.valueString;
}
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamDescription") {
childParam.documentation = childExt.valueString;
}
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamType") {
childParam.type = childExt.valueCode;
}
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParam") {
addChildParam(childParam, childExt);
}
}
searchParam.childParam = childParam;
}
var numRows = 0;
function addSearchParamRow() {
var nextRow = numRows++;
var select = $('<select/>', {/*style:'margin-left:30px;'*/});
var plusBtn = $('<button />', {type:'button', 'class':'btn btn-success btn-block'});
plusBtn.append($('<span />', {'class':'glyphicon glyphicon-plus'}));
plusBtn.isAdd = true;
var rowDiv = $('<div />', { 'class': 'row top-buffer', id: 'search-param-row-' + nextRow }).append(
$('<div />', { 'class': 'col-sm-1' }).append(plusBtn),
$('<div />', { 'class': 'col-sm-5' }).append(select),
$('<div />', { id: 'search-param-rowopts-' + nextRow })
);
$("#search-param-rows").append(rowDiv);
plusBtn.click(function() {
addSearchParamRow();
});
var params = new Array();
conformance.rest.forEach(function(rest){
rest.resource.forEach(function(restResource){
if (restResource.type == resourceName) {
if (restResource.searchParam) {
for (var i = 0; i < restResource.searchParam.length; i++) {
var searchParam = restResource.searchParam[i];
params[searchParam.name] = searchParam;
select.append(
$('<option />', { value: searchParam.name }).text(searchParam.name + ' - ' + searchParam.documentation)
);
if (restResource._searchParam && restResource._searchParam[i] != null) {
if (restResource._searchParam[i].extension) {
for (var j = 0; j < restResource._searchParam[i].extension.length; j++) {
var nextExt = restResource._searchParam[i].extension[j];
if (nextExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParam") {
addChildParam(searchParam, nextExt);
}
}
}
}
}
/*
restResource.searchParam.forEach(function(searchParam){
params[searchParam.name] = searchParam;
select.append(
$('<option />', { value: searchParam.name }).text(searchParam.name + ' - ' + searchParam.documentation)
);
});
*/
}
}
});
});
select.select2();
handleSearchParamTypeChange(select, params, nextRow);
select.change(function(){ handleSearchParamTypeChange(select, params, nextRow); });
}
function addSerarchControls(searchParam, searchParamName, containerRowNum, rowNum) {
$('#search-param-rowopts-' + containerRowNum).append(
$('<br clear="all"/>'),
$('<div class="searchParamSeparator"/>'),
$('<div />', { 'class': 'col-sm-6' }).append(
$('<span class="searchParamDescription">' + searchParam.documentation + '</span>')
)
);
if (searchParam.chain && searchParam.chain.length > 0) {
$('#search-param-rowopts-' + containerRowNum).append(
$('<input />', { name: 'param.' + rowNum + '.qualifier', type: 'hidden', value: '.' + searchParam.chain[0] })
);
}
$('#search-param-rowopts-' + containerRowNum).append(
$('<input />', { name: 'param.' + rowNum + '.name', type: 'hidden', value: searchParam.name })
);
if (searchParam.type == 'token') {
$('#search-param-rowopts-' + containerRowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { name: 'param.' + rowNum + '.0', placeholder: 'system/namespace', type: 'text', 'class': 'form-control' })
),
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { name: 'param.' + rowNum + '.1', type: 'hidden', value: '|' }),
$('<input />', { name: 'param.' + rowNum + '.2', placeholder: 'value', type: 'text', 'class': 'form-control' })
)
);
} else if (searchParam.type == 'string' || searchParam.type == 'number') {
$('#search-param-rowopts-' + containerRowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { name: 'param.' + rowNum + '.0', placeholder: 'value', type: 'text', 'class': 'form-control' })
)
);
} else if (searchParam.type == 'date') {
if (/date$/.test(searchParam.name)) {
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DD' }).append(
$('<input />', { type:'text', 'class':'form-control', name: 'param.' + rowNum + '.1' }),
$('<div />', { 'class':'input-group-addon'}).append(
$('<span />', { 'class':'glyphicon glyphicon-calendar'})
)
);
input.datetimepicker({
pickTime: false,
showToday: true
});
} else {
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DDTHH:mm:ss' }).append(
$('<input />', { type:'text', 'class':'form-control', name: 'param.' + rowNum + '.1' }),
$('<div />', { 'class':'input-group-addon'}).append(
$('<span />', { 'class':'glyphicon glyphicon-calendar'})
)
);
input.datetimepicker({
sideBySide: true,
use24hours: true,
showToday: true
});
}
$('#search-param-rowopts-' + containerRowNum).append(
$('<div />', { 'class': 'col-sm-2 btn-group' }).append(
$('<select />', {type:'text', name:'param.'+rowNum+'.0', 'class':'form-control', autocomplete:'off'}).append(
$('<option value="">(qualifier)</option>'),
$('<option value=">=">&gt;=</option>'),
$('<option value=">">&gt;</option>'),
$('<option value="&lt;=">&lt;=</option>'),
$('<option value="&lt;">&lt;</option>')
)
),
$('<div />', { 'class': 'col-sm-4' }).append(
input
)
);
}
if (searchParam.childParam) {
$('#search-param-rowopts-' + containerRowNum).append(
/*
$('<br clear="all"/>'),
$('<div style="height: 5px;"/>'),
$('<div />', { 'class': 'col-sm-6' }).append(
$('<span>' + searchParam.childParam.documentation + '</span>')
),
*/
$('<input />', { name: 'param.' + rowNum + '.0.type', type: 'hidden', value: searchParam.childParam.type })
);
addSerarchControls(searchParam.childParam, searchParamName, containerRowNum, rowNum + '.0');
}
}
function handleSearchParamTypeChange(select, params, rowNum) {
var oldVal = select.prevVal;
var newVal = select.val();
if (oldVal == newVal) {
return;
}
$('#search-param-rowopts-' + rowNum).empty();
var searchParam = params[newVal];
$('#search-param-rowopts-' + rowNum).append(
$('<input />', { name: 'param.' + rowNum + '.type', type: 'hidden', value: searchParam.type })
);
addSerarchControls(searchParam, searchParam.name, rowNum, rowNum);
select.prevVal = newVal;
}
$( document ).ready(function() {
addSearchParamRow();
});

View File

@ -5,20 +5,29 @@ import static org.junit.Assert.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.SearchParameter;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
@ -90,6 +99,31 @@ public class ServerConformanceProviderTest {
assertThat(conf, containsString("<type value=\"token\"/>"));
}
@Test
public void testProviderWithRequiredAndOptional() throws Exception {
RestfulServer rs = new RestfulServer();
rs.setProviders(new ProviderWithRequiredAndOptional());
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(null);
Conformance conformance = sc.getServerConformance();
String conf = new FhirContext().newJsonParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(conf);
RestResource res = conformance.getRestFirstRep().getResourceFirstRep();
assertEquals("DiagnosticReport", res.getType().getValueAsString());
RestResourceSearchParam p0 = res.getSearchParam().get(0);
assertEquals("subject", p0.getName().getValue());
assertEquals(1,res.getSearchInclude().size());
assertEquals("DiagnosticReport.result", res.getSearchIncludeFirstRep().getValue());
}
/**
* Created by dsotnikov on 2/25/2014.
@ -123,5 +157,22 @@ public class ServerConformanceProviderTest {
}
}
public static class ProviderWithRequiredAndOptional {
@Search
public List<DiagnosticReport> findDiagnosticReportsByPatient (
@RequiredParam(name=DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId,
@OptionalParam(name=DiagnosticReport.SP_NAME) CodingListParam theNames,
@OptionalParam(name=DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
@IncludeParam(allow= {"DiagnosticReport.result"}) Set<Include> theIncludes
) throws Exception {
return null;
}
}
}

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.test;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Server;
@ -13,14 +14,23 @@ import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.rest.annotation.IncludeParam;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.tester.RestfulTesterServlet;
import ca.uhn.test.jpasrv.DiagnosticReportResourceProvider;
@ -51,11 +61,18 @@ public class JpaTestApp {
DiagnosticReportResourceProvider diagnosticReportRp = new DiagnosticReportResourceProvider();
diagnosticReportRp.setDao(diagnosticReportDao);
IFhirSystemDao systemDao = appCtx.getBean("mySystemDao", IFhirSystemDao.class);
JpaSystemProvider systemProvider = new JpaSystemProvider(systemDao);
RestfulServer restServer = new RestfulServer();
restServer.setResourceProviders(diagnosticReportRp,patientRp, questionnaireRp, organizationRp);
IResourceProvider rp = diagnosticReportRp;
rp = new ProviderWithRequiredAndOptional();
restServer.setResourceProviders(rp,patientRp, questionnaireRp, organizationRp);
restServer.setProviders(systemProvider);
restServer.setPagingProvider(new FifoMemoryPagingProvider(10));
@ -119,5 +136,28 @@ public class JpaTestApp {
}
}
public static class ProviderWithRequiredAndOptional implements IResourceProvider {
@Search
public List<DiagnosticReport> findDiagnosticReportsByPatient (
@RequiredParam(name=DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId,
@OptionalParam(name=DiagnosticReport.SP_NAME) CodingListParam theNames,
@OptionalParam(name=DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
@IncludeParam(allow= {"DiagnosticReport.result"}) Set<Include> theIncludes
) throws Exception {
return null;
}
@Override
public Class<? extends IResource> getResourceType() {
return DiagnosticReport.class;
}
}
}