More work on tester

This commit is contained in:
jamesagnew 2014-05-01 08:40:48 -04:00
parent 63c9fb466c
commit 862b1bd79d
14 changed files with 248 additions and 67 deletions

View File

@ -125,7 +125,11 @@ public abstract class BaseClient {
}
}
try {
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} finally {
reader.close();
}
} catch (IllegalStateException e) {
throw new FhirClientConnectionException(e);

View File

@ -69,7 +69,14 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
@Override
public HttpRequestBase asHttpRequest(String theUrlBase) throws DataFormatException, IOException {
String url = theUrlBase + StringUtils.defaultString(myUrlExtension);
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
}
b.append(StringUtils.defaultString(myUrlExtension));
String url = b.toString();
String contents = myContext.newXmlParser().encodeResourceToString(myResource);
StringEntity entity = new StringEntity(contents, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"));

View File

@ -41,6 +41,9 @@ public class DeleteClientInvocation extends BaseClientInvocation {
public HttpRequestBase asHttpRequest(String theUrlBase) throws DataFormatException, IOException {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
}
b.append(myUrlPath);
HttpDelete retVal = new HttpDelete(b.toString());

View File

@ -22,18 +22,24 @@ package ca.uhn.fhir.rest.client;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.ReadMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.server.EncodingUtil;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
@ -109,4 +115,36 @@ public class GenericClient extends BaseClient implements IGenericClient {
T resp = (T) invokeClient(binding, invocation);
return resp;
}
@Override
public <T extends IResource> Bundle search(final Class<T> theType, Map<String, List<IQueryParameterType>> theParams) {
LinkedHashMap<String, List<String>> params = new LinkedHashMap<String, List<String>>();
for (Entry<String, List<IQueryParameterType>> nextEntry : theParams.entrySet()) {
ArrayList<String> valueList = new ArrayList<String>();
params.put(nextEntry.getKey(), valueList);
for (IQueryParameterType nextValue : nextEntry.getValue()) {
valueList.add(nextValue.getValueAsQueryToken());
}
}
GetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase());
}
IClientResponseHandler binding = new IClientResponseHandler() {
@Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingUtil respType = EncodingUtil.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseBundle(theType, theResponseReader);
}
};
Bundle resp = (Bundle) invokeClient(binding, invocation);
return resp;
}
}

View File

@ -66,6 +66,9 @@ public class GetClientInvocation extends BaseClientInvocation {
public HttpRequestBase asHttpRequest(String theUrlBase) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
}
b.append(myUrlPath);
boolean first = true;

View File

@ -20,13 +20,18 @@ package ca.uhn.fhir.rest.client;
* #L%
*/
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
public interface IGenericClient {
/**
* Implementation of the "read" method.
* Implementation of the "instance read" method.
*
* @param theType The type of resource to load
* @param theId The ID to load
@ -35,7 +40,7 @@ public interface IGenericClient {
<T extends IResource> T read(Class<T> theType, IdDt theId);
/**
* Implementation of the "vread" method.
* Implementation of the "instance vread" method.
*
* @param theType The type of resource to load
* @param theId The ID to load
@ -44,4 +49,12 @@ public interface IGenericClient {
*/
<T extends IResource> T vread(Class<T> theType, IdDt theId, IdDt theVersionId);
/**
* Implementation of the "instance search" method.
* @param theType The type of resource to load
* @param theParams
* @return
*/
<T extends IResource> Bundle search(Class<T> theType, Map<String, List<IQueryParameterType>> theParams);
}

View File

@ -92,7 +92,12 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
}
return new GetClientInvocation(queryStringArgs, getResourceName());
String resourceName = getResourceName();
return createSearchInvocation(resourceName, queryStringArgs);
}
public static GetClientInvocation createSearchInvocation(String theResourceName, Map<String, List<String>> theParameters) {
return new GetClientInvocation(theParameters, theResourceName);
}
@Override

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.param;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ -40,6 +42,7 @@ import org.apache.commons.lang3.time.DateUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.annotation.Count;
@ -93,12 +96,14 @@ public class ParameterUtil {
parameter.setName(((RequiredParam) nextAnnotation).name());
parameter.setRequired(true);
parameter.setType(parameterType, innerCollectionType, outerCollectionType);
extractDescription(parameter, annotations);
param = parameter;
} else if (nextAnnotation instanceof OptionalParam) {
SearchParameter parameter = new SearchParameter();
parameter.setName(((OptionalParam) nextAnnotation).name());
parameter.setRequired(false);
parameter.setType(parameterType, innerCollectionType, outerCollectionType);
extractDescription(parameter, annotations);
param = parameter;
} else if (nextAnnotation instanceof IncludeParam) {
if (parameterType != PathSpecification.class || innerCollectionType == null || outerCollectionType != null) {
@ -141,6 +146,19 @@ public class ParameterUtil {
return parameters;
}
private static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
for (Annotation annotation : theAnnotations) {
if (annotation instanceof Description) {
Description desc = (Description) annotation;
if (isNotBlank(desc.formalDefinition())) {
theParameter.setDescription(desc.formalDefinition());
} else {
theParameter.setDescription(desc.shortDefinition());
}
}
}
}
public static InstantDt toInstant(Object theArgument) {
if (theArgument instanceof InstantDt) {
return (InstantDt) theArgument;

View File

@ -38,18 +38,19 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
*/
public class SearchParameter extends BaseQueryParameter {
private String name;
private String myDescription;
private String myName;
private IParamBinder myParamBinder;
private boolean required;
private Class<?> type;
private SearchParamTypeEnum myParamType;
private boolean myRequired;
private Class<?> myType;
public SearchParameter() {
}
public SearchParameter(String name, boolean required) {
this.name = name;
this.required = required;
this.myName = name;
this.myRequired = required;
}
/* (non-Javadoc)
@ -60,21 +61,35 @@ public class SearchParameter extends BaseQueryParameter {
return myParamBinder.encode(theObject);
}
public String getDescription() {
return myDescription;
}
/* (non-Javadoc)
* @see ca.uhn.fhir.rest.param.IParameter#getName()
*/
@Override
public String getName() {
return name;
return myName;
}
@Override
public SearchParamTypeEnum getParamType() {
return myParamType;
}
public Class<?> getType() {
return type;
return myType;
}
@Override
public boolean handlesMissing() {
return false;
}
@Override
public boolean isRequired() {
return required;
return myRequired;
}
/* (non-Javadoc)
@ -85,17 +100,21 @@ public class SearchParameter extends BaseQueryParameter {
return myParamBinder.parse(theString);
}
public void setDescription(String theDescription) {
myDescription = theDescription;
}
public void setName(String name) {
this.name = name;
this.myName = name;
}
public void setRequired(boolean required) {
this.required = required;
this.myRequired = required;
}
@SuppressWarnings("unchecked")
public void setType(final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
this.type = type;
this.myType = type;
if (IQueryParameterType.class.isAssignableFrom(type)) {
this.myParamBinder = new QueryParameterTypeBinder((Class<? extends IQueryParameterType>) type);
} else if (IQueryParameterOr.class.isAssignableFrom(type)) {
@ -132,14 +151,4 @@ public class SearchParameter extends BaseQueryParameter {
}
@Override
public SearchParamTypeEnum getParamType() {
return myParamType;
}
@Override
public boolean handlesMissing() {
return false;
}
}

View File

@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.method.ReadMethodBinding;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.BaseQueryParameter;
import ca.uhn.fhir.rest.param.SearchParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.ResourceBinding;
import ca.uhn.fhir.rest.server.RestfulServer;
@ -110,19 +111,18 @@ public class ServerConformanceProvider {
RestResourceSearchParam searchParam = null;
StringDt searchParamChain = null;
for (IParameter nextParameterObj : params) {
if (!(nextParameterObj instanceof BaseQueryParameter)) {
if (!(nextParameterObj instanceof SearchParameter)) {
continue;
}
BaseQueryParameter nextParameter = (BaseQueryParameter)nextParameterObj;
if (nextParameter.getName().startsWith("_")) {
continue;
}
SearchParameter nextParameter = (SearchParameter)nextParameterObj;
if (searchParam == null) {
if (!nameToSearchParam.containsKey(nextParameter.getName())) {
RestResourceSearchParam param = resource.addSearchParam();
param.setName(nextParameter.getName());
param.setDocumentation(nextParameter.getDescription());
param.setType(nextParameter.getParamType());
searchParam = param;
} else {
searchParam = nameToSearchParam.get(nextParameter.getName());

View File

@ -22,7 +22,11 @@ package ca.uhn.fhir.rest.server.tester;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@ -44,14 +48,17 @@ import org.thymeleaf.templateresolver.TemplateResolver;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
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.server.Constants;
import ca.uhn.fhir.rest.server.EncodingUtil;
import ca.uhn.fhir.testmodel.IdentifierDt;
public class PublicTesterServlet extends HttpServlet {
@ -74,14 +81,13 @@ public class PublicTesterServlet extends HttpServlet {
myStaticResources.put("PublicTester.css", "text/css");
myStaticResources.put("hapi_fhir_banner.png", "image/png");
myStaticResources.put("hapi_fhir_banner_right.png", "image/png");
myStaticResources.put("shCore.js","text/javascript");
myStaticResources.put("shBrushJScript.js" ,"text/javascript");
myStaticResources.put("shBrushXml.js" ,"text/javascript");
myStaticResources.put("shBrushPlain.js" ,"text/javascript");
myStaticResources.put("shCore.js", "text/javascript");
myStaticResources.put("shBrushJScript.js", "text/javascript");
myStaticResources.put("shBrushXml.js", "text/javascript");
myStaticResources.put("shBrushPlain.js", "text/javascript");
myStaticResources.put("shCore.css", "text/css");
myStaticResources.put("shThemeDefault.css", "text/css");
myCtx = new FhirContext();
}
@ -103,25 +109,16 @@ public class PublicTesterServlet extends HttpServlet {
String resultSyntaxHighlighterClass;
if ("read".equals(method)) {
String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName"));
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
if (def == null) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid resourceName: " + resourceName);
return;
}
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
}
client.read(def.getImplementingClass(), new IdDt(id));
} if ("vread".equals(method)) {
String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName"));
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
if (def == null) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid resourceName: " + resourceName);
return;
}
if ("vread".equals(method)) {
RuntimeResourceDefinition def = getResourceType(theReq);
String id = StringUtils.defaultString(theReq.getParameter("id"));
if (StringUtils.isBlank(id)) {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
@ -133,6 +130,47 @@ public class PublicTesterServlet extends HttpServlet {
}
client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId));
} else if ("searchType".equals(method)) {
Map<String, List<IQueryParameterType>> params = new HashMap<String, List<IQueryParameterType>>();
HashSet<String> hashSet = new HashSet<String>(theReq.getParameterMap().keySet());
String paramName = null;
IQueryParameterType paramValue = null;
while (hashSet.isEmpty() == false) {
String nextKey = hashSet.iterator().next();
String nextValue = theReq.getParameter(nextKey);
paramName=null;
paramValue=null;
if (nextKey.startsWith("param.token.")) {
int prefixLength = "param.token.".length();
paramName = nextKey.substring(prefixLength + 2);
String systemKey = "param.token."+"1." + paramName;
String valueKey = "param.token."+"2." + paramName;
String system = theReq.getParameter(systemKey);
String value = theReq.getParameter(valueKey);
paramValue = new IdentifierDt(system, value);
hashSet.remove(systemKey);
hashSet.remove(valueKey);
} else if (nextKey.startsWith("param.string.")) {
paramName = nextKey.substring("param.string.".length());
paramValue = new StringDt(nextValue);
}
if (paramName != null) {
if (params.containsKey(paramName) == false) {
params.put(paramName, new ArrayList<IQueryParameterType>());
}
params.get(paramName).add(paramValue);
}
hashSet.remove(nextKey);
}
RuntimeResourceDefinition def = getResourceType(theReq);
client.search(def.getImplementingClass(), params);
} else {
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method);
@ -150,13 +188,13 @@ public class PublicTesterServlet extends HttpServlet {
EncodingUtil ctEnum = EncodingUtil.forContentType(mimeType);
switch (ctEnum) {
case JSON:
resultSyntaxHighlighterClass="brush: jscript";
resultSyntaxHighlighterClass = "brush: jscript";
break;
case XML:
resultSyntaxHighlighterClass="brush: xml";
resultSyntaxHighlighterClass = "brush: xml";
break;
default:
resultSyntaxHighlighterClass="brush: plain";
resultSyntaxHighlighterClass = "brush: plain";
break;
}
@ -175,6 +213,15 @@ public class PublicTesterServlet extends HttpServlet {
}
}
private RuntimeResourceDefinition getResourceType(HttpServletRequest theReq) throws ServletException {
String resourceName = StringUtils.defaultString(theReq.getParameter("resourceName"));
RuntimeResourceDefinition def = myCtx.getResourceDefinition(resourceName);
if (def == null) {
throw new ServletException("Invalid resourceName: " + resourceName);
}
return def;
}
@Override
protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
if (DEBUGMODE) {

View File

@ -46,11 +46,24 @@ function displaySearchType(expandoTr, resourceName) {
$('<input />', { name: 'method', value: 'searchType', type: 'hidden' }),
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' })
)
var inputId = newUniqueId();
if (searchParam.documentation && searchParam.documentation.length > 0) {
formElement.append(
$('<input />', { name: 'param.' + searchParam.name, placeholder: searchParam.name, type: 'text', id: inputId }),
$('<span>' + searchParam.documentation + '<br /></span>')
);
}
var inputId = newUniqueId();
if (searchParam.type && searchParam.type == 'token') {
formElement.append(
$('<input />', { name: 'param.token.1.' + searchParam.name, placeholder: 'system/namespace', type: 'text', id: inputId }),
$('<input />', { name: 'param.token.2.' + searchParam.name, placeholder: 'value', type: 'text', id: inputId }),
$('<label for="' + inputId + '">' + searchParam.name + '</input>')
);
} else {
formElement.append(
$('<input />', { name: 'param.string.' + searchParam.name, placeholder: searchParam.name, type: 'text', id: inputId }),
$('<label for="' + inputId + '">' + searchParam.name + '</input>')
);
}
formElement.append(
$('<br />'),
$('<input />', { type: 'submit', value: 'Submit' })

View File

@ -1,9 +1,12 @@
package ca.uhn.fhir.rest.server;
import java.io.IOException;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import javax.servlet.ServletException;
import java.util.Collection;
import java.util.List;
import org.hamcrest.core.StringContains;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
@ -11,9 +14,11 @@ 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.Patient;
import ca.uhn.fhir.parser.DataFormatException;
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.SearchParameter;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
public class DocumentationTest {
@ -31,10 +36,24 @@ public class DocumentationTest {
rs.init(null);
boolean found=false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier (MRN or other card number)", param.getDescription());
found=true;
}
}
assertTrue(found);
Conformance conformance = sc.getServerConformance();
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(conf);
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
assertThat(conf, containsString("<type value=\"token\"/>"));
}
/**
@ -43,7 +62,9 @@ public class DocumentationTest {
public static class SearchProvider {
@Search(type = Patient.class)
public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
public Patient findPatient(
@Description(shortDefinition = "The patient's identifier (MRN or other card number)")
@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
return null;
}

View File

@ -115,7 +115,7 @@ public class TesterTest {
@Search(type = Patient.class)
public Patient findPatient(
@Description(shortDefinition="The patient's identifier (MRN or other card number)")
@Description(shortDefinition="The patient's identifier (MRN or other card number). Example system 'urn:hapitest:mrns', example MRN '00002'")
@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier
) {
for (Patient next : getIdToPatient().values()) {