More work on the tester, and a number of bug fixes
This commit is contained in:
parent
a1aedf2f31
commit
25a40df96e
|
@ -20,8 +20,11 @@ package ca.uhn.fhir.parser;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -54,6 +57,32 @@ public abstract class BaseParser implements IParser {
|
|||
return parseBundle(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeResourceToString(IResource theResource) throws DataFormatException {
|
||||
Writer stringWriter = new StringWriter();
|
||||
try {
|
||||
encodeResourceToWriter(theResource, stringWriter);
|
||||
} catch (IOException e) {
|
||||
throw new Error("Encountered IOException during write to string - This should not happen!");
|
||||
}
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
|
||||
if (theBundle == null) {
|
||||
throw new NullPointerException("Bundle can not be null");
|
||||
}
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
try {
|
||||
encodeBundleToWriter(theBundle, stringWriter);
|
||||
} catch (IOException e) {
|
||||
throw new Error("Encountered IOException during write to string - This should not happen!");
|
||||
}
|
||||
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException {
|
||||
return parseResource(null, theMessageString);
|
||||
|
|
|
@ -30,11 +30,11 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
|
||||
public interface IParser {
|
||||
|
||||
String encodeBundleToString(Bundle theBundle) throws DataFormatException, IOException;
|
||||
String encodeBundleToString(Bundle theBundle) throws DataFormatException;
|
||||
|
||||
void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException, DataFormatException;
|
||||
|
||||
String encodeResourceToString(IResource theResource) throws DataFormatException, IOException;
|
||||
String encodeResourceToString(IResource theResource) throws DataFormatException;
|
||||
|
||||
void encodeResourceToWriter(IResource theResource, Writer stringWriter) throws IOException, DataFormatException;
|
||||
|
||||
|
@ -44,19 +44,57 @@ public interface IParser {
|
|||
|
||||
Bundle parseBundle(String theMessageString) throws ConfigurationException, DataFormatException;
|
||||
|
||||
<T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader);
|
||||
/**
|
||||
* Parses a resource
|
||||
*
|
||||
* @param theResourceType
|
||||
* The resource type to use. This can be used to explicitly
|
||||
* specify a class which extends a built-in type (e.g. a custom
|
||||
* type extending the default Patient class)
|
||||
* @param theReader
|
||||
* The reader to parse inpou from
|
||||
* @return A parsed resource
|
||||
* @throws DataFormatException
|
||||
* If the resource can not be parsed because the data is not
|
||||
* recognized or invalid for any reason
|
||||
*/
|
||||
<T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) throws DataFormatException;
|
||||
|
||||
<T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString);
|
||||
/**
|
||||
* Parses a resource
|
||||
*
|
||||
* @param theResourceType
|
||||
* The resource type to use. This can be used to explicitly
|
||||
* specify a class which extends a built-in type (e.g. a custom
|
||||
* type extending the default Patient class)
|
||||
* @param theString
|
||||
* The string to parse
|
||||
* @return A parsed resource
|
||||
* @throws DataFormatException
|
||||
* If the resource can not be parsed because the data is not
|
||||
* recognized or invalid for any reason
|
||||
*/
|
||||
<T extends IResource> T parseResource(Class<T> theResourceType, String theString) throws DataFormatException;
|
||||
|
||||
IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException;
|
||||
|
||||
IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException;
|
||||
|
||||
/**
|
||||
* Sets the "pretty print" flag, meaning that the parser will encode
|
||||
* resources with human-readable spacing and newlines between elements
|
||||
* instead of condensing output as much as possible.
|
||||
*
|
||||
* @param thePrettyPrint
|
||||
* The flag
|
||||
* @return Returns an instance of <code>this</code> parser so that method
|
||||
* calls can be conveniently chained
|
||||
*/
|
||||
IParser setPrettyPrint(boolean thePrettyPrint);
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the
|
||||
* encoded values.
|
||||
* If set to <code>true</code> (default is <code>false</code>), narratives
|
||||
* will not be included in the encoded values.
|
||||
*/
|
||||
IParser setSuppressNarratives(boolean theSuppressNarratives);
|
||||
|
||||
|
|
|
@ -20,13 +20,11 @@ package ca.uhn.fhir.parser;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -50,7 +48,6 @@ import javax.json.stream.JsonGeneratorFactory;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
|
@ -145,17 +142,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
return eventWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeBundleToString(Bundle theBundle) throws DataFormatException, IOException {
|
||||
if (theBundle == null) {
|
||||
throw new NullPointerException("Bundle can not be null");
|
||||
}
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
encodeBundleToWriter(theBundle, stringWriter);
|
||||
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
|
||||
JsonGenerator eventWriter = createJsonGenerator(theWriter);
|
||||
|
@ -473,12 +459,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
theEventWriter.writeEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodeResourceToString(IResource theResource) throws DataFormatException, IOException {
|
||||
Writer stringWriter = new StringWriter();
|
||||
encodeResourceToWriter(theResource, stringWriter);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package ca.uhn.fhir.rest.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
|
||||
/**
|
||||
* For searches, a parameter may be annotated with the {@link Sort} annotation. The
|
||||
* parameter should be of type {@link SortSpec}.
|
||||
*
|
||||
* <p>
|
||||
* Note that if you wish to chain
|
||||
* multiple sort parameters (i.e. a sub sort), you should use the {@link SortSpec#setChain(SortSpec)}
|
||||
* method. Multiple parameters should not be annotated with the Sort annotation.
|
||||
* </p>
|
||||
*
|
||||
* @see Search
|
||||
*/
|
||||
@Target(value=ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Sort {
|
||||
// nothing
|
||||
}
|
|
@ -51,6 +51,13 @@ public class MethodOutcome {
|
|||
return myId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OperationOutcome} resource to return to the client or
|
||||
* <code>null</code> if none.
|
||||
*
|
||||
* @return This method <b>will return null</b>, unlike many methods in the
|
||||
* API.
|
||||
*/
|
||||
public OperationOutcome getOperationOutcome() {
|
||||
return myOperationOutcome;
|
||||
}
|
||||
|
@ -63,6 +70,10 @@ public class MethodOutcome {
|
|||
myId = theId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OperationOutcome} resource to return to the client. Set
|
||||
* to <code>null</code> (which is the default) if none.
|
||||
*/
|
||||
public void setOperationOutcome(OperationOutcome theOperationOutcome) {
|
||||
myOperationOutcome = theOperationOutcome;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package ca.uhn.fhir.rest.api;
|
||||
|
||||
public enum SortOrderEnum {
|
||||
|
||||
ASC,
|
||||
DESC
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package ca.uhn.fhir.rest.api;
|
||||
|
||||
/**
|
||||
* Represents values for <a
|
||||
* href="http://hl7.org/implement/standards/fhir/search.html#sort">sorting</a>
|
||||
* resources returned by a server.
|
||||
*/
|
||||
public class SortSpec {
|
||||
|
||||
private String myFieldName;
|
||||
private SortSpec myChain;
|
||||
|
||||
/**
|
||||
* Gets the chained sort specification, or <code>null</code> if none. If
|
||||
* multiple sort parameters are chained (indicating a sub-sort), the second
|
||||
* level sort is chained via this property.
|
||||
*/
|
||||
public SortSpec getChain() {
|
||||
return myChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chained sort specification, or <code>null</code> if none. If
|
||||
* multiple sort parameters are chained (indicating a sub-sort), the second
|
||||
* level sort is chained via this property.
|
||||
*/
|
||||
public void setChain(SortSpec theChain) {
|
||||
myChain = theChain;
|
||||
}
|
||||
|
||||
private SortOrderEnum myOrder;
|
||||
|
||||
/**
|
||||
* Returns the actual name of the field to sort by
|
||||
*/
|
||||
public String getFieldName() {
|
||||
return myFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sort order specified by this parameter, or <code>null</code>
|
||||
* if none is explicitly defined (which means {@link SortOrderEnum#ASC}
|
||||
* according to the <a
|
||||
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR
|
||||
* specification</a>)
|
||||
*/
|
||||
public SortOrderEnum getOrder() {
|
||||
return myOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the actual name of the field to sort by
|
||||
*/
|
||||
public void setFieldName(String theFieldName) {
|
||||
myFieldName = theFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sort order specified by this parameter, or <code>null</code> if
|
||||
* none is explicitly defined (which means {@link SortOrderEnum#ASC}
|
||||
* according to the <a
|
||||
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR
|
||||
* specification</a>)
|
||||
*/
|
||||
public void setOrder(SortOrderEnum theOrder) {
|
||||
myOrder = theOrder;
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,9 @@ import java.io.Reader;
|
|||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -42,6 +44,8 @@ import org.apache.http.entity.ContentType;
|
|||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
import ca.uhn.fhir.rest.method.IClientResponseHandler;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
public abstract class BaseClient {
|
||||
|
@ -52,6 +56,29 @@ public abstract class BaseClient {
|
|||
private HttpResponse myLastResponse;
|
||||
private String myLastResponseBody;
|
||||
private final String myUrlBase;
|
||||
private EncodingEnum myEncoding = null; // default unspecified (will be XML)
|
||||
private boolean myPrettyPrint = false;
|
||||
|
||||
/**
|
||||
* Returns the encoding that will be used on requests. Default is
|
||||
* <code>null</code>, which means the client will not explicitly request an
|
||||
* encoding. (This is standard behaviour according to the FHIR
|
||||
* specification)
|
||||
*/
|
||||
public EncodingEnum getEncoding() {
|
||||
return myEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encoding that will be used on requests. Default is
|
||||
* <code>null</code>, which means the client will not explicitly request an
|
||||
* encoding. (This is standard behaviour according to the FHIR
|
||||
* specification)
|
||||
*/
|
||||
public BaseClient setEncoding(EncodingEnum theEncoding) {
|
||||
myEncoding = theEncoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
BaseClient(HttpClient theClient, String theUrlBase) {
|
||||
super();
|
||||
|
@ -60,14 +87,16 @@ public abstract class BaseClient {
|
|||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public HttpResponse getLastResponse() {
|
||||
return myLastResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public String getLastResponseBody() {
|
||||
return myLastResponseBody;
|
||||
|
@ -83,7 +112,7 @@ public abstract class BaseClient {
|
|||
HttpRequestBase httpRequest;
|
||||
HttpResponse response;
|
||||
try {
|
||||
httpRequest = clientInvocation.asHttpRequest(myUrlBase);
|
||||
httpRequest = clientInvocation.asHttpRequest(myUrlBase, createExtraParams());
|
||||
response = myClient.execute(httpRequest);
|
||||
} catch (DataFormatException e) {
|
||||
throw new FhirClientConnectionException(e);
|
||||
|
@ -91,10 +120,6 @@ public abstract class BaseClient {
|
|||
throw new FhirClientConnectionException(e);
|
||||
}
|
||||
|
||||
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
|
||||
throw BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Reader reader = createReaderFromResponse(response);
|
||||
|
@ -110,7 +135,7 @@ public abstract class BaseClient {
|
|||
}
|
||||
|
||||
ContentType ct = ContentType.get(response.getEntity());
|
||||
String mimeType = ct.getMimeType();
|
||||
String mimeType = ct != null ? ct.getMimeType() : null;
|
||||
|
||||
Map<String, List<String>> headers = new HashMap<String, List<String>>();
|
||||
if (response.getAllHeaders() != null) {
|
||||
|
@ -125,6 +150,10 @@ public abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
|
||||
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
|
||||
throw BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
|
||||
}
|
||||
|
||||
try {
|
||||
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
|
||||
} finally {
|
||||
|
@ -145,29 +174,50 @@ public abstract class BaseClient {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> createExtraParams() {
|
||||
HashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
|
||||
|
||||
if (getEncoding() == EncodingEnum.XML) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList(Constants.CT_XML));
|
||||
} else if (getEncoding() == EncodingEnum.JSON) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList(Constants.CT_JSON));
|
||||
}
|
||||
|
||||
if (isPrettyPrint()) {
|
||||
retVal.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public boolean isKeepResponses() {
|
||||
return myKeepResponses;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public void setKeepResponses(boolean theKeepResponses) {
|
||||
myKeepResponses = theKeepResponses;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public void setLastResponse(HttpResponse theLastResponse) {
|
||||
myLastResponse = theLastResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public void setLastResponseBody(String theLastResponseBody) {
|
||||
myLastResponseBody = theLastResponseBody;
|
||||
|
@ -192,4 +242,25 @@ public abstract class BaseClient {
|
|||
return reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pretty print flag, which is a request to the server for it to
|
||||
* return "pretty printed" responses. Note that this is currently a
|
||||
* non-standard flag (_pretty) which is supported only by HAPI based servers
|
||||
* (and any other servers which might implement it).
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return myPrettyPrint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pretty print flag, which is a request to the server for it to
|
||||
* return "pretty printed" responses. Note that this is currently a
|
||||
* non-standard flag (_pretty) which is supported only by HAPI based servers
|
||||
* (and any other servers which might implement it).
|
||||
*/
|
||||
public BaseClient setPrettyPrint(boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,19 +20,48 @@ package ca.uhn.fhir.rest.client;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public abstract class BaseClientInvocation {
|
||||
|
||||
/**
|
||||
* Create an HTTP request out of this client request
|
||||
*
|
||||
* @param theUrlBase The FHIR server base url (with a trailing "/")
|
||||
* @param theUrlBase
|
||||
* The FHIR server base url (with a trailing "/")
|
||||
* @param theExtraParams
|
||||
* Any extra request parameters the server wishes to add
|
||||
*/
|
||||
public abstract HttpRequestBase asHttpRequest(String theUrlBase) throws DataFormatException, IOException;
|
||||
public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams);
|
||||
|
||||
protected static void appendExtraParamsWithQuestionMark(Map<String, List<String>> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
|
||||
boolean first = theWithQuestionMark;
|
||||
|
||||
if (theExtraParams != null && theExtraParams.isEmpty() == false) {
|
||||
for (Entry<String, List<String>> next : theExtraParams.entrySet()) {
|
||||
for (String nextValue : next.getValue()) {
|
||||
if (first) {
|
||||
theUrlBuilder.append('?');
|
||||
first = false;
|
||||
} else {
|
||||
theUrlBuilder.append('&');
|
||||
}
|
||||
try {
|
||||
theUrlBuilder.append(URLEncoder.encode(next.getKey(), "UTF-8"));
|
||||
theUrlBuilder.append('=');
|
||||
theUrlBuilder.append(URLEncoder.encode(nextValue, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("UTF-8 not supported - This should not happen");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.client;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
|
@ -68,7 +69,7 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase) throws DataFormatException, IOException {
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams) throws DataFormatException {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theUrlBase);
|
||||
if (!theUrlBase.endsWith("/")) {
|
||||
|
@ -76,6 +77,8 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
|
|||
}
|
||||
b.append(StringUtils.defaultString(myUrlExtension));
|
||||
|
||||
appendExtraParamsWithQuestionMark(theExtraParams, b, true);
|
||||
|
||||
String url = b.toString();
|
||||
String contents = myContext.newXmlParser().encodeResourceToString(myResource);
|
||||
StringEntity entity = new StringEntity(contents, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"));
|
||||
|
|
|
@ -20,14 +20,13 @@ package ca.uhn.fhir.rest.client;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public class DeleteClientInvocation extends BaseClientInvocation {
|
||||
|
||||
private String myUrlPath;
|
||||
|
@ -38,7 +37,7 @@ public class DeleteClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase) throws DataFormatException, IOException {
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theUrlBase);
|
||||
if (!theUrlBase.endsWith("/")) {
|
||||
|
@ -46,6 +45,8 @@ public class DeleteClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
b.append(myUrlPath);
|
||||
|
||||
appendExtraParamsWithQuestionMark(theExtraParams, b,true);
|
||||
|
||||
HttpDelete retVal = new HttpDelete(b.toString());
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -32,15 +32,25 @@ import org.apache.http.client.HttpClient;
|
|||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
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.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.CreateMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.HistoryMethodBinding;
|
||||
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.method.UpdateMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.ValidateMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
public class GenericClient extends BaseClient implements IGenericClient {
|
||||
|
@ -49,7 +59,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
private HttpRequestBase myLastRequest;
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public GenericClient(FhirContext theContext, HttpClient theHttpClient, String theServerBase) {
|
||||
super(theHttpClient, theServerBase);
|
||||
|
@ -64,14 +75,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
|
||||
GetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
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);
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseResource(theType, theResponseReader);
|
||||
}
|
||||
|
@ -82,8 +92,30 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return resp;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
|
||||
DeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(toResourceName(theType), theId);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
final String resourceName = myContext.getResourceDefinition(theType).getName();
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
|
||||
* For now, this is a part of the internal API of HAPI - Use with caution as
|
||||
* this method may change!
|
||||
*/
|
||||
public void setLastRequest(HttpRequestBase theLastRequest) {
|
||||
myLastRequest = theLastRequest;
|
||||
|
@ -93,19 +125,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return myContext.getResourceDefinition(theType).getName();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
|
||||
GetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
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);
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseResource(theType, theResponseReader);
|
||||
}
|
||||
|
@ -116,7 +146,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
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>>();
|
||||
|
@ -130,14 +159,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
GetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
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);
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseBundle(theType, theResponseReader);
|
||||
}
|
||||
|
@ -147,4 +175,111 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return resp;
|
||||
}
|
||||
|
||||
public MethodOutcome create(IResource theResource) {
|
||||
BaseClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
|
||||
return resp;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
|
||||
BaseClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(IResource theResource) {
|
||||
BaseClientInvocation invocation = ValidateMethodBinding.createValidateInvocation(theResource, null, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IResource> Bundle history(final Class<T> theType, IdDt theIdDt) {
|
||||
GetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(toResourceName(theType), theIdDt);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseBundle(theType, theResponseReader);
|
||||
}
|
||||
};
|
||||
|
||||
Bundle resp = (Bundle) invokeClient(binding, invocation);
|
||||
return resp;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Conformance conformance() {
|
||||
GetClientInvocation invocation = ConformanceMethodBinding.createConformanceInvocation();
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseResource(Conformance.class, theResponseReader);
|
||||
}
|
||||
};
|
||||
|
||||
Conformance resp = (Conformance) invokeClient(binding, invocation);
|
||||
return resp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ package ca.uhn.fhir.rest.client;
|
|||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
@ -44,13 +44,13 @@ public class GetClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
|
||||
public GetClientInvocation(String theUrlPath) {
|
||||
myParameters = Collections.emptyMap();
|
||||
myParameters = new HashMap<String, List<String>>();
|
||||
myUrlPath = theUrlPath;
|
||||
}
|
||||
|
||||
|
||||
public GetClientInvocation(String... theUrlFragments) {
|
||||
myParameters = Collections.emptyMap();
|
||||
myParameters = new HashMap<String, List<String>>();
|
||||
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class GetClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
|
||||
@Override
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase) {
|
||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theUrlBase);
|
||||
if (!theUrlBase.endsWith("/")) {
|
||||
|
@ -92,6 +92,9 @@ public class GetClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
|
||||
|
||||
return new HttpGet(b.toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ 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.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
|
||||
public interface IGenericClient {
|
||||
|
||||
|
@ -57,4 +59,43 @@ public interface IGenericClient {
|
|||
*/
|
||||
<T extends IResource> Bundle search(Class<T> theType, Map<String, List<IQueryParameterType>> theParams);
|
||||
|
||||
/**
|
||||
* Implementation of the "instance update" method.
|
||||
*
|
||||
* @param theId The ID to update
|
||||
* @param theResource The new resource body
|
||||
* @return An outcome containing the results and possibly the new version ID
|
||||
*/
|
||||
MethodOutcome update(IdDt theIdDt, IResource theResource);
|
||||
|
||||
/**
|
||||
* Implementation of the "type validate" method.
|
||||
*
|
||||
* @param theId The ID to validate
|
||||
* @param theResource The resource to validate
|
||||
* @return An outcome containing any validation issues
|
||||
*/
|
||||
MethodOutcome validate(IResource theResource);
|
||||
|
||||
/**
|
||||
* Implementation of the "delete instance" method.
|
||||
* @param theType The type of resource to delete
|
||||
* @param theId the ID of the resource to delete
|
||||
* @return An outcome
|
||||
*/
|
||||
MethodOutcome delete(Class<? extends IResource> theType, IdDt theId);
|
||||
|
||||
/**
|
||||
* Implementation of the "history instance" method.
|
||||
* @param theType The type of resource to return the history for
|
||||
* @param theId the ID of the resource to return the history for
|
||||
* @return An outcome
|
||||
*/
|
||||
<T extends IResource> Bundle history(Class<T> theType, IdDt theIdDt);
|
||||
|
||||
/**
|
||||
* Retrieves and returns the server conformance statement
|
||||
*/
|
||||
Conformance conformance();
|
||||
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
|||
import ca.uhn.fhir.rest.param.IParameter;
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingUtil;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -277,11 +277,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
// return sm;
|
||||
}
|
||||
|
||||
public static EncodingUtil determineResponseEncoding(HttpServletRequest theRequest, Map<String, String[]> theParams) {
|
||||
public static EncodingEnum determineResponseEncoding(HttpServletRequest theRequest, Map<String, String[]> theParams) {
|
||||
String[] format = theParams.remove(Constants.PARAM_FORMAT);
|
||||
if (format != null) {
|
||||
for (String nextFormat : format) {
|
||||
EncodingUtil retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
|
@ -291,13 +291,13 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
Enumeration<String> acceptValues = theRequest.getHeaders("Accept");
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
EncodingUtil retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
return EncodingUtil.XML;
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
|
||||
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
|
||||
|
@ -357,4 +357,16 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
}
|
||||
}
|
||||
|
||||
protected static boolean prettyPrintResponse(Request theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
||||
boolean prettyPrint = false;
|
||||
if (pretty != null && pretty.length > 0) {
|
||||
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
|
||||
prettyPrint = true;
|
||||
}
|
||||
}
|
||||
return prettyPrint;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
|||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||
import ca.uhn.fhir.rest.param.IParameter;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingUtil;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -58,7 +58,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionNotSpecifiedException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
|
||||
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
||||
public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
|
||||
private boolean myReturnVoid;
|
||||
|
||||
|
@ -83,40 +83,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
|||
if (myReturnVoid) {
|
||||
return null;
|
||||
}
|
||||
List<String> locationHeaders = theHeaders.get("location");
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
if (locationHeaders != null && locationHeaders.size() > 0) {
|
||||
String locationHeader = locationHeaders.get(0);
|
||||
parseContentLocation(retVal, locationHeader);
|
||||
}
|
||||
if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
|
||||
EncodingUtil ct = EncodingUtil.forContentType(theResponseMimeType);
|
||||
if (ct != null) {
|
||||
PushbackReader reader = new PushbackReader(theResponseReader);
|
||||
|
||||
try {
|
||||
int firstByte = reader.read();
|
||||
if (firstByte == -1) {
|
||||
ourLog.debug("No content in response, not going to read");
|
||||
reader = null;
|
||||
} else {
|
||||
reader.unread(firstByte);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ourLog.debug("No content in response, not going to read", e);
|
||||
reader = null;
|
||||
}
|
||||
|
||||
if (reader != null) {
|
||||
IParser parser = ct.newParser(getContext());
|
||||
OperationOutcome outcome = parser.parseResource(OperationOutcome.class, reader);
|
||||
retVal.setOperationOutcome(outcome);
|
||||
}
|
||||
|
||||
} else {
|
||||
ourLog.debug("Ignoring response content of type: {}", theResponseMimeType);
|
||||
}
|
||||
}
|
||||
MethodOutcome retVal = process2xxResponse(getContext(), getResourceName(), theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||
return retVal;
|
||||
case Constants.STATUS_HTTP_400_BAD_REQUEST:
|
||||
throw new InvalidRequestException("Server responded with: " + IOUtils.toString(theResponseReader));
|
||||
|
@ -138,30 +105,86 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
|||
|
||||
}
|
||||
|
||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingUtil theEncoding, HttpServletResponse theResponse) throws IOException {
|
||||
theResponse.setStatus(theE.getStatusCode());
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||
EncodingEnum encoding = BaseMethodBinding.determineResponseEncoding(theRequest.getServletRequest(), theRequest.getParameters());
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
IResource resource;
|
||||
if (requestContainsResource()) {
|
||||
resource = parser.parseResource(theRequest.getInputReader());
|
||||
} else {
|
||||
resource = null;
|
||||
}
|
||||
|
||||
Object[] params = new Object[getParameters().size()];
|
||||
for (int i = 0; i < getParameters().size(); i++) {
|
||||
IParameter param = getParameters().get(i);
|
||||
if (param == null) {
|
||||
continue;
|
||||
}
|
||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, resource);
|
||||
}
|
||||
|
||||
addParametersForServerRequest(theRequest, params);
|
||||
|
||||
MethodOutcome response;
|
||||
try {
|
||||
response = (MethodOutcome) invokeServerMethod(getProvider(), params);
|
||||
} catch (InternalErrorException e) {
|
||||
ourLog.error("Internal error during method invocation", e);
|
||||
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
|
||||
return;
|
||||
} catch (BaseServerResponseException e) {
|
||||
ourLog.info("Exception during method invocation: " + e.getMessage());
|
||||
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getResourceOperationType() == RestfulOperationTypeEnum.CREATE) {
|
||||
if (response == null) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
|
||||
}
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
||||
addLocationHeader(theRequest, theResponse, response);
|
||||
} else if (response == null) {
|
||||
if (isReturnVoid() == false) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||
}
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
||||
} else {
|
||||
if (response.getOperationOutcome() == null) {
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
||||
} else {
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||
}
|
||||
if (getResourceOperationType() == RestfulOperationTypeEnum.UPDATE) {
|
||||
addLocationHeader(theRequest, theResponse, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
theServer.addHeadersToResponse(theResponse);
|
||||
|
||||
if (theE.getOperationOutcome() != null) {
|
||||
theResponse.setContentType(theEncoding.getResourceContentType());
|
||||
IParser parser = theEncoding.newParser(theServer.getFhirContext());
|
||||
|
||||
if (response != null && response.getOperationOutcome() != null) {
|
||||
theResponse.setContentType(encoding.getResourceContentType());
|
||||
Writer writer = theResponse.getWriter();
|
||||
parser.setPrettyPrint(prettyPrintResponse(theRequest));
|
||||
try {
|
||||
parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);
|
||||
parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} else {
|
||||
theResponse.setContentType(Constants.CT_TEXT);
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
writer.append(theE.getMessage());
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
|
||||
// getMethod().in
|
||||
}
|
||||
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -209,111 +232,6 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
|||
* writer.close(); } // getMethod().in }
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||
EncodingUtil encoding = BaseMethodBinding.determineResponseEncoding(theRequest.getServletRequest(), theRequest.getParameters());
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
IResource resource;
|
||||
if (requestContainsResource()) {
|
||||
resource = parser.parseResource(theRequest.getInputReader());
|
||||
} else {
|
||||
resource = null;
|
||||
}
|
||||
|
||||
Object[] params = new Object[getParameters().size()];
|
||||
for (int i = 0; i < getParameters().size(); i++) {
|
||||
IParameter param = getParameters().get(i);
|
||||
if (param == null) {
|
||||
continue;
|
||||
}
|
||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, resource);
|
||||
}
|
||||
|
||||
addParametersForServerRequest(theRequest, params);
|
||||
|
||||
MethodOutcome response;
|
||||
try {
|
||||
response = (MethodOutcome) invokeServerMethod(getProvider(), params);
|
||||
} catch (InternalErrorException e) {
|
||||
ourLog.error("Internal error during method invocation", e);
|
||||
streamOperationOutcome(e, theServer, encoding, theResponse);
|
||||
return;
|
||||
} catch (BaseServerResponseException e) {
|
||||
ourLog.info("Exception during method invocation: "+e.getMessage());
|
||||
streamOperationOutcome(e, theServer, encoding, theResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (getResourceOperationType() == RestfulOperationTypeEnum.CREATE) {
|
||||
if (response == null) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
|
||||
}
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
||||
addLocationHeader(theRequest, theResponse, response);
|
||||
} else if (response == null) {
|
||||
if (isReturnVoid() == false) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||
}
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
||||
} else {
|
||||
if (response.getOperationOutcome() == null) {
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
||||
} else {
|
||||
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||
}
|
||||
if (getResourceOperationType() == RestfulOperationTypeEnum.UPDATE) {
|
||||
addLocationHeader(theRequest, theResponse, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
theServer.addHeadersToResponse(theResponse);
|
||||
|
||||
if (response != null && response.getOperationOutcome() != null) {
|
||||
theResponse.setContentType(encoding.getResourceContentType());
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} else {
|
||||
theResponse.setContentType(Constants.CT_TEXT);
|
||||
Writer writer = theResponse.getWriter();
|
||||
writer.close();
|
||||
}
|
||||
|
||||
// getMethod().in
|
||||
}
|
||||
|
||||
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theRequest.getFhirServerBase());
|
||||
b.append('/');
|
||||
b.append(getResourceName());
|
||||
b.append('/');
|
||||
b.append(response.getId().getValue());
|
||||
if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
||||
b.append("/_history/");
|
||||
b.append(response.getVersionId().getValue());
|
||||
}
|
||||
theResponse.addHeader("Location", b.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override if the incoming request should not contain a
|
||||
* resource
|
||||
*/
|
||||
protected boolean requestContainsResource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
|
||||
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Request theRequest) {
|
||||
Set<RequestType> allowableRequestTypes = provideAllowableRequestTypes();
|
||||
|
@ -333,12 +251,21 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For servers, this method will match only incoming requests that match the
|
||||
* given operation, or which have no operation in the URL if this method
|
||||
* returns null.
|
||||
*/
|
||||
protected abstract String getMatchingOperation();
|
||||
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theRequest.getFhirServerBase());
|
||||
b.append('/');
|
||||
b.append(getResourceName());
|
||||
b.append('/');
|
||||
b.append(response.getId().getValue());
|
||||
if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
||||
b.append("/_history/");
|
||||
b.append(response.getVersionId().getValue());
|
||||
}
|
||||
theResponse.addHeader("Location", b.toString());
|
||||
}
|
||||
|
||||
protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
|
||||
|
||||
/**
|
||||
* Subclasses may override to allow a void method return type, which is
|
||||
|
@ -348,10 +275,91 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected abstract BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName);
|
||||
protected abstract BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource);
|
||||
|
||||
protected void parseContentLocation(MethodOutcome theOutcomeToPopulate, String theLocationHeader) {
|
||||
String resourceNamePart = "/" + getResourceName() + "/";
|
||||
/**
|
||||
* For servers, this method will match only incoming requests that match the
|
||||
* given operation, or which have no operation in the URL if this method
|
||||
* returns null.
|
||||
*/
|
||||
protected abstract String getMatchingOperation();
|
||||
|
||||
protected abstract Set<RequestType> provideAllowableRequestTypes();
|
||||
|
||||
/**
|
||||
* Subclasses may override if the incoming request should not contain a
|
||||
* resource
|
||||
*/
|
||||
protected boolean requestContainsResource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncoding, HttpServletResponse theResponse, Request theRequest) throws IOException {
|
||||
theResponse.setStatus(theE.getStatusCode());
|
||||
|
||||
theServer.addHeadersToResponse(theResponse);
|
||||
|
||||
if (theE.getOperationOutcome() != null) {
|
||||
theResponse.setContentType(theEncoding.getResourceContentType());
|
||||
IParser parser = theEncoding.newParser(theServer.getFhirContext());
|
||||
parser.setPrettyPrint(prettyPrintResponse(theRequest));
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} else {
|
||||
theResponse.setContentType(Constants.CT_TEXT);
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
writer.append(theE.getMessage());
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodOutcome process2xxResponse(FhirContext theContext, String theResourceName, int theResponseStatusCode, String theResponseMimeType, Reader theResponseReader, Map<String, List<String>> theHeaders) {
|
||||
List<String> locationHeaders = theHeaders.get("location");
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
if (locationHeaders != null && locationHeaders.size() > 0) {
|
||||
String locationHeader = locationHeaders.get(0);
|
||||
parseContentLocation(retVal, theResourceName, locationHeader);
|
||||
}
|
||||
if (theResponseStatusCode != Constants.STATUS_HTTP_204_NO_CONTENT) {
|
||||
EncodingEnum ct = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (ct != null) {
|
||||
PushbackReader reader = new PushbackReader(theResponseReader);
|
||||
|
||||
try {
|
||||
int firstByte = reader.read();
|
||||
if (firstByte == -1) {
|
||||
ourLog.debug("No content in response, not going to read");
|
||||
reader = null;
|
||||
} else {
|
||||
reader.unread(firstByte);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ourLog.debug("No content in response, not going to read", e);
|
||||
reader = null;
|
||||
}
|
||||
|
||||
if (reader != null) {
|
||||
IParser parser = ct.newParser(theContext);
|
||||
OperationOutcome outcome = parser.parseResource(OperationOutcome.class, reader);
|
||||
retVal.setOperationOutcome(outcome);
|
||||
}
|
||||
|
||||
} else {
|
||||
ourLog.debug("Ignoring response content of type: {}", theResponseMimeType);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected static void parseContentLocation(MethodOutcome theOutcomeToPopulate, String theResourceName, String theLocationHeader) {
|
||||
String resourceNamePart = "/" + theResourceName + "/";
|
||||
int resourceIndex = theLocationHeader.lastIndexOf(resourceNamePart);
|
||||
if (resourceIndex > -1) {
|
||||
int idIndexStart = resourceIndex + resourceNamePart.length();
|
||||
|
@ -369,6 +377,4 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract Set<RequestType> provideAllowableRequestTypes();
|
||||
|
||||
}
|
||||
|
|
|
@ -20,26 +20,15 @@ package ca.uhn.fhir.rest.method;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||
import ca.uhn.fhir.rest.param.IParameter;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingUtil;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding {
|
||||
|
@ -91,10 +80,7 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
|||
throw new NullPointerException("Resource can not be null");
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(resource);
|
||||
String resourceName = def.getName();
|
||||
|
||||
return createClientInvocation(theArgs, resource, resourceName);
|
||||
return createClientInvocation(theArgs, resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
|||
import ca.uhn.fhir.rest.param.IParameter;
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingUtil;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -179,16 +179,10 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||
|
||||
// Pretty print
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
||||
boolean prettyPrint = false;
|
||||
if (pretty != null && pretty.length > 0) {
|
||||
if ("true".equals(pretty[0])) {
|
||||
prettyPrint = true;
|
||||
}
|
||||
}
|
||||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||
|
||||
// Narrative mode
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||
NarrativeModeEnum narrativeMode = null;
|
||||
if (narrative != null && narrative.length > 0) {
|
||||
|
@ -199,7 +193,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
|
||||
// Determine response encoding
|
||||
EncodingUtil responseEncoding = determineResponseEncoding(theRequest.getServletRequest(), requestParams);
|
||||
EncodingEnum responseEncoding = determineResponseEncoding(theRequest.getServletRequest(), requestParams);
|
||||
|
||||
// Is this request coming from a browser
|
||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
||||
|
@ -233,7 +227,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
private IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||
|
||||
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||
Object retValObj = theResourceMetadata.get(theKey);
|
||||
if (retValObj == null) {
|
||||
return null;
|
||||
|
@ -271,7 +266,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
+ InstantDt.class.getCanonicalName());
|
||||
}
|
||||
|
||||
private IParser getNewParser(EncodingUtil theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
|
||||
private IParser getNewParser(EncodingEnum theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
|
||||
IParser parser;
|
||||
switch (theResponseEncoding) {
|
||||
case JSON:
|
||||
|
@ -285,7 +280,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
||||
}
|
||||
|
||||
private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingUtil theResponseEncoding, String theServerBase,
|
||||
private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase,
|
||||
String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
|
@ -363,7 +358,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
b.append('?').append(Constants.PARAM_PRETTY).append("=true");
|
||||
haveQ = true;
|
||||
}
|
||||
if (theResponseEncoding == EncodingUtil.JSON) {
|
||||
if (theResponseEncoding == EncodingEnum.JSON) {
|
||||
if (!haveQ) {
|
||||
b.append('?');
|
||||
haveQ = true;
|
||||
|
@ -396,7 +391,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingUtil theResponseEncoding, boolean thePrettyPrint,
|
||||
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
|
|
@ -53,6 +53,10 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
|
||||
@Override
|
||||
public GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||
return createConformanceInvocation();
|
||||
}
|
||||
|
||||
public static GetClientInvocation createConformanceInvocation() {
|
||||
return new GetClientInvocation("metadata");
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.Collections;
|
|||
import java.util.Set;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||
|
@ -55,11 +56,20 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
}
|
||||
|
||||
@Override
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName) {
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource) {
|
||||
FhirContext context = getContext();
|
||||
|
||||
return createCreateInvocation(resource, context);
|
||||
}
|
||||
|
||||
public static BaseClientInvocation createCreateInvocation(IResource resource, FhirContext context) {
|
||||
RuntimeResourceDefinition def = context.getResourceDefinition(resource);
|
||||
String resourceName = def.getName();
|
||||
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(resourceName);
|
||||
|
||||
return new PostClientInvocation(getContext(), resource, urlExtension.toString());
|
||||
return new PostClientInvocation(context, resource, urlExtension.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -105,11 +105,11 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName) {
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(resourceName);
|
||||
urlExtension.append(getContext().getResourceDefinition(theResource).getName());
|
||||
|
||||
return new PostClientInvocation(getContext(), resource, urlExtension.toString());
|
||||
return new PostClientInvocation(getContext(), theResource, urlExtension.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,23 +129,22 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
if (idDt == null) {
|
||||
throw new NullPointerException("ID can not be null");
|
||||
}
|
||||
String resourceName = getResourceName();
|
||||
|
||||
DeleteClientInvocation retVal = createDeleteInvocation(resourceName, idDt);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static DeleteClientInvocation createDeleteInvocation(String theResourceName, IdDt idDt) {
|
||||
String id = idDt.getValue();
|
||||
|
||||
DeleteClientInvocation retVal = new DeleteClientInvocation(getResourceName(), id);
|
||||
|
||||
DeleteClientInvocation retVal = new DeleteClientInvocation(theResourceName, id);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
|
||||
String url = theRequest.getCompleteUrl();
|
||||
int resNameIdx = url.indexOf(getResourceName());
|
||||
String id = url.substring(resNameIdx+getResourceName().length() + 1);
|
||||
if (id.contains("/")) {
|
||||
throw new InvalidRequestException("Invalid request path for a DELETE operation: "+theRequest.getCompleteUrl());
|
||||
}
|
||||
|
||||
theParams[myIdParameterIndex] = new IdDt(id);
|
||||
theParams[myIdParameterIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -23,13 +23,13 @@ package ca.uhn.fhir.rest.method;
|
|||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -103,32 +103,42 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
@Override
|
||||
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||
IdDt id = null;
|
||||
String resourceName = myResourceName;
|
||||
if (myIdParamIndex != null) {
|
||||
id = (IdDt) theArgs[myIdParamIndex];
|
||||
if (id == null || isBlank(id.getValue())) {
|
||||
throw new NullPointerException("ID can not be null");
|
||||
}
|
||||
}
|
||||
|
||||
GetClientInvocation retVal = createHistoryInvocation(resourceName, id);
|
||||
|
||||
if (theArgs != null) {
|
||||
for (int idx = 0; idx < theArgs.length; idx++) {
|
||||
IParameter nextParam = getParameters().get(idx);
|
||||
nextParam.translateClientArgumentIntoQueryArgument(theArgs[idx], retVal.getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static GetClientInvocation createHistoryInvocation(String theResourceName, IdDt theId) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
if (myResourceName != null) {
|
||||
b.append(myResourceName);
|
||||
if (myIdParamIndex != null) {
|
||||
IdDt id = (IdDt) theArgs[myIdParamIndex];
|
||||
if (id == null || isBlank(id.getValue())) {
|
||||
throw new NullPointerException("ID can not be null");
|
||||
}
|
||||
if (theResourceName != null) {
|
||||
b.append(theResourceName);
|
||||
if (theId != null) {
|
||||
b.append('/');
|
||||
b.append(id.getValue());
|
||||
b.append(theId.getValue());
|
||||
}
|
||||
}
|
||||
if (b.length() > 0) {
|
||||
b.append('/');
|
||||
}
|
||||
b.append(Constants.PARAM_HISTORY);
|
||||
|
||||
LinkedHashMap<String, List<String>> queryStringArgs=new LinkedHashMap<String, List<String>>();
|
||||
if (theArgs != null) {
|
||||
for (int idx = 0; idx < theArgs.length; idx++) {
|
||||
IParameter nextParam = getParameters().get(idx);
|
||||
nextParam.translateClientArgumentIntoQueryArgument(theArgs[idx], queryStringArgs);
|
||||
}
|
||||
}
|
||||
|
||||
return new GetClientInvocation(queryStringArgs, b.toString());
|
||||
GetClientInvocation retVal = new GetClientInvocation(b.toString());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -139,7 +149,20 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
||||
|
||||
return toResourceList(response);
|
||||
List<IResource> resources = toResourceList(response);
|
||||
int index=0;
|
||||
for (IResource nextResource : resources) {
|
||||
if (nextResource.getId() == null || nextResource.getId().isEmpty()) {
|
||||
throw new InternalErrorException("Server provided resource at index " + index + " with no ID set (using IResource#setId(IdDt))");
|
||||
}
|
||||
IdDt versionId = getIdFromMetadataOrNullIfNone(nextResource.getResourceMetadata(),ResourceMetadataKeyEnum.VERSION_ID);
|
||||
if (versionId == null||versionId.isEmpty()) {
|
||||
throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#Resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, Object))");
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
// ObjectUtils.equals is replaced by a JDK7 method..
|
||||
|
|
|
@ -43,7 +43,7 @@ import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
|
||||
private Integer myIdParameterIndex;
|
||||
|
||||
|
@ -79,9 +79,9 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||
String locationHeader = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||
if (isNotBlank(locationHeader)) {
|
||||
MethodOutcome mo = new MethodOutcome();
|
||||
parseContentLocation(mo, locationHeader);
|
||||
parseContentLocation(mo, getResourceName(), locationHeader);
|
||||
if (mo.getId() == null || mo.getId().isEmpty()) {
|
||||
throw new InvalidRequestException("Invalid Content-Location header for resource " + getResourceName()+ ": " + locationHeader);
|
||||
throw new InvalidRequestException("Invalid Content-Location header for resource " + getResourceName() + ": " + locationHeader);
|
||||
}
|
||||
if (mo.getVersionId() != null && mo.getVersionId().isEmpty() == false) {
|
||||
theRequest.setVersion(mo.getVersionId());
|
||||
|
@ -99,34 +99,43 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||
}
|
||||
|
||||
@Override
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName) {
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
||||
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
|
||||
if (idDt == null) {
|
||||
throw new NullPointerException("ID can not be null");
|
||||
}
|
||||
String id = idDt.getValue();
|
||||
|
||||
IdDt versionIdDt = null;
|
||||
if (myVersionIdParameterIndex != null) {
|
||||
versionIdDt = (IdDt) theArgs[myVersionIdParameterIndex];
|
||||
}
|
||||
FhirContext context = getContext();
|
||||
|
||||
PutClientInvocation retVal = createUpdateInvocation(theResource, idDt, versionIdDt, context);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static PutClientInvocation createUpdateInvocation(IResource theResource, IdDt idDt, IdDt versionIdDt, FhirContext context) {
|
||||
String id = idDt.getValue();
|
||||
String resourceName = context.getResourceDefinition(theResource).getName();
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(resourceName);
|
||||
urlExtension.append('/');
|
||||
urlExtension.append(id);
|
||||
PutClientInvocation retVal = new PutClientInvocation(getContext(), resource, urlExtension.toString());
|
||||
PutClientInvocation retVal = new PutClientInvocation(context, theResource, urlExtension.toString());
|
||||
|
||||
if (myVersionIdParameterIndex != null) {
|
||||
IdDt versionIdDt = (IdDt) theArgs[myVersionIdParameterIndex];
|
||||
if (versionIdDt != null) {
|
||||
String versionId = versionIdDt.getValue();
|
||||
if (StringUtils.isNotBlank(versionId)) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('/');
|
||||
b.append(urlExtension);
|
||||
b.append("/_history/");
|
||||
b.append(versionId);
|
||||
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString());
|
||||
}
|
||||
if (versionIdDt != null) {
|
||||
String versionId = versionIdDt.getValue();
|
||||
if (StringUtils.isNotBlank(versionId)) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append('/');
|
||||
b.append(urlExtension);
|
||||
b.append("/_history/");
|
||||
b.append(versionId);
|
||||
retVal.addHeader(Constants.HEADER_CONTENT_LOCATION, b.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -149,7 +158,6 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||
return Collections.singleton(RequestType.PUT);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getMatchingOperation() {
|
||||
return null;
|
||||
|
|
|
@ -31,11 +31,11 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||
import ca.uhn.fhir.rest.client.PutClientInvocation;
|
||||
import ca.uhn.fhir.rest.client.PostClientInvocation;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
|
||||
private Integer myIdParameterIndex;
|
||||
|
||||
|
@ -63,22 +63,31 @@ class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourc
|
|||
}
|
||||
|
||||
@Override
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource resource, String resourceName) {
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(resourceName);
|
||||
urlExtension.append(Constants.PARAM_VALIDATE);
|
||||
protected BaseClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
||||
FhirContext context = getContext();
|
||||
|
||||
IdDt idDt=null;
|
||||
if (myIdParameterIndex != null) {
|
||||
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
|
||||
if (idDt != null && idDt.isEmpty() == false) {
|
||||
String id = idDt.getValue();
|
||||
urlExtension.append('/');
|
||||
urlExtension.append(id);
|
||||
}
|
||||
idDt = (IdDt) theArgs[myIdParameterIndex];
|
||||
}
|
||||
|
||||
PutClientInvocation retVal = new PutClientInvocation(getContext(), resource, urlExtension.toString());
|
||||
PostClientInvocation retVal = createValidateInvocation(theResource, idDt, context);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static PostClientInvocation createValidateInvocation(IResource theResource, IdDt theId, FhirContext theContext) {
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(theContext.getResourceDefinition(theResource).getName());
|
||||
urlExtension.append('/');
|
||||
urlExtension.append(Constants.PARAM_VALIDATE);
|
||||
|
||||
if (theId != null && theId.isEmpty() == false) {
|
||||
String id = theId.getValue();
|
||||
urlExtension.append('/');
|
||||
urlExtension.append(id);
|
||||
}
|
||||
// TODO: is post correct here?
|
||||
PostClientInvocation retVal = new PostClientInvocation(theContext, theResource, urlExtension.toString());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -90,6 +99,7 @@ class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourc
|
|||
|
||||
@Override
|
||||
protected Set<RequestType> provideAllowableRequestTypes() {
|
||||
// TODO: is post correct here?
|
||||
return Collections.singleton(RequestType.POST);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ public class SinceParameter implements IParameter {
|
|||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but can not be of collection type");
|
||||
}
|
||||
if (!ParameterUtil.getBindableInstantTypes().contains(theParameterType)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but cis an invalid type, must be one of: " + ParameterUtil.getBindableInstantTypes());
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Since.class.getName() + " but is an invalid type, must be one of: " + ParameterUtil.getBindableInstantTypes());
|
||||
}
|
||||
myType = theParameterType;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.rest.annotation.Sort;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class SortParameter implements IParameter {
|
||||
|
||||
@Override
|
||||
public void translateClientArgumentIntoQueryArgument(Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments) throws InternalErrorException {
|
||||
SortSpec ss = (SortSpec) theSourceClientArgument;
|
||||
if (ss ==null) {
|
||||
return;
|
||||
}
|
||||
String name;
|
||||
if (ss.getOrder()==null) {
|
||||
name = Constants.PARAM_SORT;
|
||||
}else if (ss.getOrder() == SortOrderEnum.ASC) {
|
||||
name = Constants.PARAM_SORT_ASC;
|
||||
}else {
|
||||
name = Constants.PARAM_SORT_DESC;
|
||||
}
|
||||
|
||||
if (ss.getFieldName() != null) {
|
||||
if (!theTargetQueryArguments.containsKey(name)) {
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||
if (theOuterCollectionType != null) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Sort.class.getName() + " but can not be of collection type");
|
||||
}
|
||||
if (!ParameterUtil.getBindableInstantTypes().contains(theParameterType)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Sort.class.getName() + " but is an invalid type, must be: " + SortSpec.class.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -35,7 +35,7 @@ public class Constants {
|
|||
public static final String CT_ATOM_XML = "application/atom+xml";
|
||||
public static final Set<String> FORMAT_VAL_XML;
|
||||
public static final Set<String> FORMAT_VAL_JSON;
|
||||
public static final Map<String, EncodingUtil> FORMAT_VAL_TO_ENCODING;
|
||||
public static final Map<String, EncodingEnum> FORMAT_VAL_TO_ENCODING;
|
||||
public static final String CT_XML = "application/xml";
|
||||
public static final String CT_JSON = "application/json";
|
||||
public static final String CT_HTML = "text/html";
|
||||
|
@ -62,9 +62,13 @@ public class Constants {
|
|||
public static final String PARAM_VALIDATE = "_validate";
|
||||
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
|
||||
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
|
||||
public static final String PARAM_PRETTY_VALUE_TRUE = "true";
|
||||
public static final String PARAM_SORT = "_sort";
|
||||
public static final String PARAM_SORT_ASC = "_sort:asc";
|
||||
public static final String PARAM_SORT_DESC = "_sort:desc";
|
||||
|
||||
static {
|
||||
Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>();
|
||||
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
||||
|
||||
HashSet<String> valXml = new HashSet<String>();
|
||||
valXml.add(CT_FHIR_XML);
|
||||
|
@ -72,7 +76,7 @@ public class Constants {
|
|||
valXml.add("xml");
|
||||
FORMAT_VAL_XML = Collections.unmodifiableSet(valXml);
|
||||
for (String string : valXml) {
|
||||
valToEncoding.put(string, EncodingUtil.XML);
|
||||
valToEncoding.put(string, EncodingEnum.XML);
|
||||
}
|
||||
|
||||
HashSet<String> valJson = new HashSet<String>();
|
||||
|
@ -81,7 +85,7 @@ public class Constants {
|
|||
valJson.add("json");
|
||||
FORMAT_VAL_JSON = Collections.unmodifiableSet(valJson);
|
||||
for (String string : valJson) {
|
||||
valToEncoding.put(string, EncodingUtil.JSON);
|
||||
valToEncoding.put(string, EncodingEnum.JSON);
|
||||
}
|
||||
|
||||
FORMAT_VAL_TO_ENCODING=Collections.unmodifiableMap(valToEncoding);
|
||||
|
|
|
@ -25,7 +25,7 @@ import java.util.HashMap;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
|
||||
public enum EncodingUtil {
|
||||
public enum EncodingEnum {
|
||||
|
||||
XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML, Constants.CT_XML) {
|
||||
@Override
|
||||
|
@ -43,11 +43,11 @@ public enum EncodingUtil {
|
|||
|
||||
;
|
||||
|
||||
private static HashMap<String, EncodingUtil> ourContentTypeToEncoding;
|
||||
private static HashMap<String, EncodingEnum> ourContentTypeToEncoding;
|
||||
|
||||
static {
|
||||
ourContentTypeToEncoding = new HashMap<String, EncodingUtil>();
|
||||
for (EncodingUtil next: values()) {
|
||||
ourContentTypeToEncoding = new HashMap<String, EncodingEnum>();
|
||||
for (EncodingEnum next: values()) {
|
||||
ourContentTypeToEncoding.put(next.getBundleContentType(), next);
|
||||
ourContentTypeToEncoding.put(next.getResourceContentType(), next);
|
||||
ourContentTypeToEncoding.put(next.getBrowserFriendlyBundleContentType(), next);
|
||||
|
@ -58,7 +58,7 @@ public enum EncodingUtil {
|
|||
private String myBundleContentType;
|
||||
private String myBrowserFriendlyContentType;
|
||||
|
||||
EncodingUtil(String theResourceContentType, String theBundleContentType, String theBrowserFriendlyContentType) {
|
||||
EncodingEnum(String theResourceContentType, String theBundleContentType, String theBrowserFriendlyContentType) {
|
||||
myResourceContentType = theResourceContentType;
|
||||
myBundleContentType = theBundleContentType;
|
||||
myBrowserFriendlyContentType = theBrowserFriendlyContentType;
|
||||
|
@ -78,7 +78,7 @@ public enum EncodingUtil {
|
|||
return myBrowserFriendlyContentType;
|
||||
}
|
||||
|
||||
public static EncodingUtil forContentType(String theContentType) {
|
||||
public static EncodingEnum forContentType(String theContentType) {
|
||||
return ourContentTypeToEncoding.get(theContentType);
|
||||
}
|
||||
|
|
@ -81,7 +81,11 @@ public class RestfulServer extends HttpServlet {
|
|||
* Constructor
|
||||
*/
|
||||
public RestfulServer() {
|
||||
myFhirContext = new FhirContext();
|
||||
this(new FhirContext());
|
||||
}
|
||||
|
||||
public RestfulServer(FhirContext theCtx) {
|
||||
myFhirContext = theCtx;
|
||||
myServerConformanceProvider = new ServerConformanceProvider(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,10 @@ public class ResourceNotFoundException extends BaseServerResponseException {
|
|||
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is not known");
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(Class<? extends IResource> theClass, IdDt thePatientId) {
|
||||
super(STATUS_CODE, "Resource of type " + theClass.getSimpleName() + " with ID " + thePatientId + " is not known");
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(String theMessage) {
|
||||
super(STATUS_CODE, theMessage);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.server.provider;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -31,6 +33,7 @@ import ca.uhn.fhir.model.api.ExtensionDt;
|
|||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceOperation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulConformanceModeEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||
|
@ -40,10 +43,8 @@ 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.method.BaseMethodBinding;
|
||||
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;
|
||||
|
@ -155,8 +156,26 @@ public class ServerConformanceProvider {
|
|||
}
|
||||
}
|
||||
|
||||
Collections.sort(resource.getOperation(), new Comparator<RestResourceOperation>() {
|
||||
@Override
|
||||
public int compare(RestResourceOperation theO1, RestResourceOperation theO2) {
|
||||
RestfulOperationTypeEnum o1 = theO1.getCode().getValueAsEnum();
|
||||
RestfulOperationTypeEnum o2 = theO2.getCode().getValueAsEnum();
|
||||
if (o1 == null && o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (o1 == null) {
|
||||
return 1;
|
||||
}
|
||||
if (o2==null) {
|
||||
return -1;
|
||||
}
|
||||
return o1.ordinal() - o2.ordinal();
|
||||
}});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
myConformance = retVal;
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.server.tester;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -35,8 +36,12 @@ 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.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
|
@ -49,6 +54,7 @@ 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.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -57,21 +63,20 @@ import ca.uhn.fhir.rest.annotation.Metadata;
|
|||
import ca.uhn.fhir.rest.client.GenericClient;
|
||||
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.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
public class PublicTesterServlet extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicTesterServlet.class);
|
||||
private static final boolean DEBUGMODE = true;
|
||||
private TemplateEngine myTemplateEngine;
|
||||
private HashMap<String, String> myStaticResources;
|
||||
private String myServerBase;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicTesterServlet.class);
|
||||
private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html";
|
||||
private static final long serialVersionUID = 1L;
|
||||
private FhirContext myCtx;
|
||||
private String myServerBase;
|
||||
private HashMap<String, String> myStaticResources;
|
||||
|
||||
public void setServerBase(String theServerBase) {
|
||||
myServerBase = theServerBase;
|
||||
}
|
||||
private TemplateEngine myTemplateEngine;
|
||||
|
||||
public PublicTesterServlet() {
|
||||
myStaticResources = new HashMap<String, String>();
|
||||
|
@ -91,125 +96,18 @@ public class PublicTesterServlet extends HttpServlet {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
if (DEBUGMODE) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
public void init(ServletConfig theConfig) throws ServletException {
|
||||
myTemplateEngine = new TemplateEngine();
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(new ProfileResourceResolver());
|
||||
myTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
myTemplateEngine.setDialect(dialect);
|
||||
myTemplateEngine.initialize();
|
||||
}
|
||||
|
||||
try {
|
||||
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
|
||||
client.setKeepResponses(true);
|
||||
String method = theReq.getParameter("method");
|
||||
|
||||
String requestUrl;
|
||||
String action;
|
||||
String resultStatus;
|
||||
String resultBody;
|
||||
String resultSyntaxHighlighterClass;
|
||||
|
||||
if ("read".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");
|
||||
}
|
||||
client.read(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
String versionId = StringUtils.defaultString(theReq.getParameter("versionid"));
|
||||
if (StringUtils.isBlank(versionId)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No Version ID specified");
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpRequestBase lastRequest = client.getLastRequest();
|
||||
requestUrl = lastRequest.getURI().toASCIIString();
|
||||
action = client.getLastRequest().getMethod();
|
||||
resultStatus = client.getLastResponse().getStatusLine().toString();
|
||||
resultBody = client.getLastResponseBody();
|
||||
|
||||
ContentType ct = ContentType.get(client.getLastResponse().getEntity());
|
||||
String mimeType = ct.getMimeType();
|
||||
EncodingUtil ctEnum = EncodingUtil.forContentType(mimeType);
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
resultSyntaxHighlighterClass = "brush: jscript";
|
||||
break;
|
||||
case XML:
|
||||
resultSyntaxHighlighterClass = "brush: xml";
|
||||
break;
|
||||
default:
|
||||
resultSyntaxHighlighterClass = "brush: plain";
|
||||
break;
|
||||
}
|
||||
|
||||
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
|
||||
ctx.setVariable("base", myServerBase);
|
||||
ctx.setVariable("requestUrl", requestUrl);
|
||||
ctx.setVariable("action", action);
|
||||
ctx.setVariable("resultStatus", resultStatus);
|
||||
ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody));
|
||||
ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
|
||||
|
||||
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theResp.sendError(500, e.toString());
|
||||
}
|
||||
public void setServerBase(String theServerBase) {
|
||||
myServerBase = theServerBase;
|
||||
}
|
||||
|
||||
private RuntimeResourceDefinition getResourceType(HttpServletRequest theReq) throws ServletException {
|
||||
|
@ -221,6 +119,12 @@ public class PublicTesterServlet extends HttpServlet {
|
|||
return def;
|
||||
}
|
||||
|
||||
private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException {
|
||||
InputStream res = PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName);
|
||||
theResp.setContentType(theContentType);
|
||||
IOUtils.copy(res, theResp.getOutputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
if (DEBUGMODE) {
|
||||
|
@ -246,24 +150,289 @@ public class PublicTesterServlet extends HttpServlet {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
if (DEBUGMODE) {
|
||||
myTemplateEngine.getCacheManager().clearAllCaches();
|
||||
}
|
||||
|
||||
try {
|
||||
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient(myServerBase);
|
||||
client.setKeepResponses(true);
|
||||
String method = theReq.getParameter("method");
|
||||
|
||||
String prettyParam = theReq.getParameter("configPretty");
|
||||
if ("on".equals(prettyParam)) {
|
||||
client.setPrettyPrint(true);
|
||||
}
|
||||
if ("xml".equals(theReq.getParameter("configEncoding"))) {
|
||||
client.setEncoding(EncodingEnum.XML);
|
||||
} else if ("json".equals(theReq.getParameter("configEncoding"))) {
|
||||
client.setEncoding(EncodingEnum.JSON);
|
||||
}
|
||||
|
||||
String requestUrl;
|
||||
String action;
|
||||
String resultStatus;
|
||||
String resultBody;
|
||||
String resultSyntaxHighlighterClass;
|
||||
boolean returnsResource;
|
||||
|
||||
try {
|
||||
if ("conformance".equals(method)) {
|
||||
returnsResource = true;
|
||||
client.conformance();
|
||||
} else if ("read".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");
|
||||
}
|
||||
returnsResource = true;
|
||||
|
||||
client.read(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
} else 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");
|
||||
}
|
||||
|
||||
String versionId = StringUtils.defaultString(theReq.getParameter("versionid"));
|
||||
if (StringUtils.isBlank(versionId)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No Version ID specified");
|
||||
}
|
||||
returnsResource = true;
|
||||
|
||||
client.vread(def.getImplementingClass(), new IdDt(id), new IdDt(versionId));
|
||||
|
||||
} else if ("delete".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");
|
||||
}
|
||||
|
||||
returnsResource = false;
|
||||
|
||||
client.delete(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
} else if ("history-instance".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");
|
||||
}
|
||||
|
||||
returnsResource = false;
|
||||
|
||||
client.history(def.getImplementingClass(), new IdDt(id));
|
||||
|
||||
} else if ("create".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
|
||||
if (StringUtils.isBlank(resourceText)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
|
||||
}
|
||||
|
||||
IResource resource;
|
||||
if (client.getEncoding() == null || client.getEncoding() == EncodingEnum.XML) {
|
||||
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
} else {
|
||||
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
}
|
||||
returnsResource = false;
|
||||
|
||||
client.create(resource);
|
||||
|
||||
} else if ("validate".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
|
||||
if (StringUtils.isBlank(resourceText)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
|
||||
}
|
||||
|
||||
IResource resource;
|
||||
if (client.getEncoding() == null || client.getEncoding() == EncodingEnum.XML) {
|
||||
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
} else {
|
||||
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
}
|
||||
returnsResource = false;
|
||||
|
||||
client.validate(resource);
|
||||
|
||||
} else if ("update".equals(method)) {
|
||||
RuntimeResourceDefinition def = getResourceType(theReq);
|
||||
String resourceText = StringUtils.defaultString(theReq.getParameter("resource"));
|
||||
if (StringUtils.isBlank(resourceText)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No resource content specified");
|
||||
}
|
||||
|
||||
String id = StringUtils.defaultString(theReq.getParameter("id"));
|
||||
if (StringUtils.isBlank(id)) {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "No ID specified");
|
||||
}
|
||||
|
||||
IResource resource;
|
||||
if (client.getEncoding() == null || client.getEncoding() == EncodingEnum.XML) {
|
||||
resource = myCtx.newXmlParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
} else {
|
||||
resource = myCtx.newJsonParser().parseResource(def.getImplementingClass(), resourceText);
|
||||
}
|
||||
returnsResource = false;
|
||||
|
||||
client.update(new IdDt(id), resource);
|
||||
|
||||
} 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);
|
||||
|
||||
returnsResource = false;
|
||||
client.search(def.getImplementingClass(), params);
|
||||
|
||||
} else {
|
||||
theResp.sendError(Constants.STATUS_HTTP_400_BAD_REQUEST, "Invalid method: " + method);
|
||||
return;
|
||||
}
|
||||
} catch (BaseServerResponseException e) {
|
||||
ourLog.error("Failed to invoke method", e);
|
||||
returnsResource=false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requestUrl = lastRequest.getURI().toASCIIString();
|
||||
action = client.getLastRequest().getMethod();
|
||||
resultStatus = client.getLastResponse().getStatusLine().toString();
|
||||
resultBody = client.getLastResponseBody();
|
||||
|
||||
HttpResponse lastResponse = client.getLastResponse();
|
||||
ContentType ct = ContentType.get(lastResponse.getEntity());
|
||||
String mimeType = ct != null ? ct.getMimeType() : null;
|
||||
EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType);
|
||||
String narrativeString = "";
|
||||
|
||||
if (ctEnum == null) {
|
||||
resultSyntaxHighlighterClass = "brush: plain";
|
||||
} else {
|
||||
switch (ctEnum) {
|
||||
case JSON:
|
||||
resultSyntaxHighlighterClass = "brush: jscript";
|
||||
if (returnsResource) {
|
||||
narrativeString = parseNarrative(ctEnum, resultBody);
|
||||
}
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
resultSyntaxHighlighterClass = "brush: xml";
|
||||
if (returnsResource) {
|
||||
narrativeString = parseNarrative(ctEnum, resultBody);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
|
||||
ctx.setVariable("base", myServerBase);
|
||||
ctx.setVariable("requestUrl", requestUrl);
|
||||
ctx.setVariable("action", action);
|
||||
ctx.setVariable("resultStatus", resultStatus);
|
||||
ctx.setVariable("requestBody", StringEscapeUtils.escapeHtml4(requestBody));
|
||||
ctx.setVariable("requestSyntaxHighlighterClass", requestSyntaxHighlighterClass);
|
||||
ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody));
|
||||
ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
|
||||
ctx.setVariable("requestHeaders", lastRequest.getAllHeaders());
|
||||
ctx.setVariable("responseHeaders", lastResponse.getAllHeaders());
|
||||
ctx.setVariable("narrative", narrativeString);
|
||||
|
||||
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure during processing", e);
|
||||
theResp.sendError(500, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) {
|
||||
try {
|
||||
IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody);
|
||||
String retVal = resource.getText().getDiv().getValueAsString();
|
||||
return StringUtils.defaultString(retVal);
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to parse resource", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private interface ConformanceClient extends IBasicClient {
|
||||
@Metadata
|
||||
Conformance getConformance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig theConfig) throws ServletException {
|
||||
myTemplateEngine = new TemplateEngine();
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(new ProfileResourceResolver());
|
||||
myTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
myTemplateEngine.setDialect(dialect);
|
||||
myTemplateEngine.initialize();
|
||||
}
|
||||
|
||||
private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html";
|
||||
|
||||
private final class ProfileResourceResolver implements IResourceResolver {
|
||||
|
||||
@Override
|
||||
|
@ -285,10 +454,4 @@ public class PublicTesterServlet extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException {
|
||||
InputStream res = PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName);
|
||||
theResp.setContentType(theContentType);
|
||||
IOUtils.copy(res, theResp.getOutputStream());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
A.selectedFunctionLink {
|
||||
text-decoration: none;
|
||||
background: #E0FFE0;
|
||||
border: 1px #80C080 solid;
|
||||
color: #005000;
|
||||
}
|
||||
|
||||
BODY {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
SPAN.headerName {
|
||||
color: #505080;
|
||||
}
|
||||
|
||||
SPAN.headerValue {
|
||||
color: #70A070;
|
||||
}
|
||||
|
||||
DIV.bodyHeaderBlock {
|
||||
background-color: #E0E0E0;
|
||||
margin-top: 5px;
|
||||
|
@ -9,10 +24,27 @@ DIV.bodyHeaderBlock {
|
|||
padding: 5px;
|
||||
}
|
||||
|
||||
DIV.textareaWrapper {
|
||||
position:relative;
|
||||
height:100%;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
DIV.textareaWrapper TEXTAREA {
|
||||
width:95%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
TABLE.propertyTable {
|
||||
margin-left: 4px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
TD.propertyKeyCell {
|
||||
background-color: #E0E0FF;
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
TD.testerNameCell {
|
||||
|
|
|
@ -35,7 +35,7 @@ This file is a Thymeleaf template for the
|
|||
<div class="bodyHeaderBlock">
|
||||
Software Details
|
||||
</div>
|
||||
<table border="0">
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Software</td>
|
||||
<td th:text="${conf.software.name}">HAPI Restful Server</td>
|
||||
|
@ -46,35 +46,75 @@ This file is a Thymeleaf template for the
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Request Configuration
|
||||
</div>
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Encoding</td>
|
||||
<td>
|
||||
<select id="configEncoding">
|
||||
<option value="" selected="true">(default)</option>
|
||||
<option value="xml">XML</option>
|
||||
<option value="json">JSON</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Pretty Printing</td>
|
||||
<td>
|
||||
<input type="checkbox" id="configPretty"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
|
||||
<td width="1%"/>
|
||||
|
||||
<td width="70%" valign="top">
|
||||
|
||||
<th:block th:each="rest : ${conf.rest}">
|
||||
<div class="bodyHeaderBlock">
|
||||
RESTful Server
|
||||
</div>
|
||||
<table border="0" th:id="systemExpando" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Supports operations:</td>
|
||||
<td valign="top">
|
||||
<a th:onclick="'displayConformance(this, \'systemExpando\'); return false;'" href="#">conformance</a>
|
||||
<th:span th:each="operation : ${rest.operation}">
|
||||
<th:block th:switch="${operation.code.value}">
|
||||
<span th:case="*" th:text="${operation.code.value}"/>
|
||||
</th:block>
|
||||
</th:span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<th:block th:each="resource, resIterStat : ${rest.resource}" th:with="expandoId='resExpando'+${resIterStat.count}">
|
||||
<div class="bodyHeaderBlock">
|
||||
Resource: <th:block th:text="${resource.type.valueAsString}"/>
|
||||
</div>
|
||||
<table border="0" th:id="${expandoId}">
|
||||
<table border="0" th:id="${expandoId}" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Supports operations:</td>
|
||||
<td valign="top">
|
||||
<th:span th:each="operation : ${resource.operation}">
|
||||
<th:block th:switch="${operation.code.value}">
|
||||
<a th:case="'read'" th:href="'javascript:displayRead(\'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\');'">read</a>
|
||||
<a th:case="'vread'" th:href="'javascript:displayVRead(\'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\');'">vread</a>
|
||||
<a th:case="'search-type'" th:href="'javascript:displaySearchType(\'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\');'">search-type</a>
|
||||
<span th:case="*" th:text="${operation.code.value}"/>
|
||||
<a th:case="'read'" href="#" th:onclick="'displayRead(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">read</a>
|
||||
<a th:case="'vread'" href="#" th:onclick="'displayVRead(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">vread</a>
|
||||
<a th:case="'search-type'" href="#" th:onclick="'displaySearchType(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">search-type</a>
|
||||
<a th:case="'create'" href="#" th:onclick="'displayCreate(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">create</a>
|
||||
<a th:case="'update'" href="#" th:onclick="'displayUpdate(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">update</a>
|
||||
<a th:case="'validate'" href="#" th:onclick="'displayValidate(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">validate</a>
|
||||
<a th:case="'delete'" href="#" th:onclick="'displayDelete(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">delete</a>
|
||||
<a th:case="'history-instance'" href="#" th:onclick="'displayHistoryInstance(this, \'' + ${expandoId} + '\', \'' + ${resource.type.valueAsString} + '\'); return false;'">history-instance</a>
|
||||
<span th:case="*" href="#" th:text="${operation.code.value}"/>
|
||||
</th:block>
|
||||
</th:span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
var uniqueIdSeed = 1;
|
||||
|
||||
function addConfigElementsToForm(theForm) {
|
||||
var encoding = document.getElementById('configEncoding');
|
||||
var pretty = document.getElementById('configPretty');
|
||||
|
||||
var newEncoding = document.createElement("input");
|
||||
newEncoding.type='hidden';
|
||||
newEncoding.name='configEncoding';
|
||||
newEncoding.value = encoding.value;
|
||||
theForm.appendChild(newEncoding);
|
||||
|
||||
var newPretty = document.createElement("input");
|
||||
newPretty.type='hidden';
|
||||
newPretty.name='configPretty';
|
||||
if (pretty.checked) {
|
||||
newPretty.value='on';
|
||||
}
|
||||
newPretty.checked = pretty.checked;
|
||||
theForm.appendChild(newPretty);
|
||||
}
|
||||
|
||||
/** Hide any currently displayed tester form */
|
||||
function clearCurrentForm(postCompleteFunction) {
|
||||
|
@ -20,14 +41,110 @@ function clearCurrentForm(postCompleteFunction) {
|
|||
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'search' method */
|
||||
function displaySearchType(expandoTr, resourceName) {
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayConformance(button, expandoTr) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Conformance</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'conformance', type: 'hidden' }),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayCreate(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Create</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'create', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<div class="textareaWrapper">').append(
|
||||
$('<textarea />', { name: 'resource', rows: 10, style: 'white-space: nowrap;' })
|
||||
),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'delete' method */
|
||||
function displayDelete(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Delete</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'delete', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayUpdate(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Update</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'update', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<textarea />', { name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'search' method */
|
||||
function displaySearchType(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
|
||||
// Add a search form for the default (no parameter) search, which should
|
||||
// return all resources of the given type
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Search by Type</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST' }).append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'searchType', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<span>All Resources of Type</span><br />'),
|
||||
|
@ -36,12 +153,14 @@ function displaySearchType(expandoTr, resourceName) {
|
|||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Loop through each supported search parameter and add a search form for it
|
||||
conformance.rest.forEach(function(rest){
|
||||
rest.resource.forEach(function(restResource){
|
||||
if (restResource.type == resourceName) {
|
||||
if (restResource.searchParam) {
|
||||
restResource.searchParam.forEach(function(searchParam){
|
||||
var formElement = $('<form/>', { action: 'PublicTesterResult.html', method: 'POST' });
|
||||
var formElement = $('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" });
|
||||
formElement.append(
|
||||
$('<input />', { name: 'method', value: 'searchType', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' })
|
||||
|
@ -87,14 +206,38 @@ function displaySearchType(expandoTr, resourceName) {
|
|||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayRead(expandoTr, resourceName) {
|
||||
/** Create a tester form for the 'instance-history' method */
|
||||
function displayHistoryInstance(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Read</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST' }).append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'history-instance', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayRead(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Read</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'read', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
|
@ -110,13 +253,37 @@ function displayRead(expandoTr, resourceName) {
|
|||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayVRead(expandoTr, resourceName) {
|
||||
function displayValidate(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Validate</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'validate', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<textarea />', { name: 'resource', cols: 100, rows: 10, style: 'white-space: nowrap;' }),
|
||||
$('<br />'),
|
||||
$('<input />', { type: 'submit', value: 'Submit' })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
showNewForm();
|
||||
}
|
||||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
/** Create a tester form for the 'read' method */
|
||||
function displayVRead(button, expandoTr, resourceName) {
|
||||
highlightSelectedLink(button);
|
||||
var postCompleteFunction = function() {
|
||||
$('#' + expandoTr).append(
|
||||
$('<tr class="testerNameRow" style="display: none;" />').append(
|
||||
$('<td class="testerNameCell">Read</td>'),
|
||||
$('<td />').append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST' }).append(
|
||||
$('<form/>', { action: 'PublicTesterResult.html', method: 'POST', onsubmit: "addConfigElementsToForm(this);" }).append(
|
||||
$('<input />', { name: 'method', value: 'vread', type: 'hidden' }),
|
||||
$('<input />', { name: 'resourceName', value: resourceName, type: 'hidden' }),
|
||||
$('<input />', { name: 'id', placeholder: 'Resource ID', type: 'text' }),
|
||||
|
@ -132,7 +299,13 @@ function displayVRead(expandoTr, resourceName) {
|
|||
clearCurrentForm(postCompleteFunction);
|
||||
}
|
||||
|
||||
var uniqueIdSeed = 1;
|
||||
function highlightSelectedLink(button) {
|
||||
$('.selectedFunctionLink').each(function() {
|
||||
$(this).removeClass('selectedFunctionLink');
|
||||
});
|
||||
button.className = 'selectedFunctionLink';
|
||||
}
|
||||
|
||||
/** Generate a unique ID */
|
||||
function newUniqueId() {
|
||||
return "uid" + uniqueIdSeed++;
|
||||
|
|
|
@ -28,9 +28,7 @@ This file is a Thymeleaf template for the
|
|||
</tr>
|
||||
</table>
|
||||
<div class="bodyHeaderBlock">
|
||||
Executed invocation:<br/>
|
||||
<th:block th:text="${action}">GET</th:block>
|
||||
<a href="http://foo.com/fhir" th:href="${requestUrl}"><th:block th:text="${requestUrl}">http://foo.com/fhir</th:block></a>
|
||||
Executed invocation against FHIR RESTful Server
|
||||
</div>
|
||||
|
||||
<table border="0" width="100%" cellpadding="0" cellspacing="0" style="margin-top: 4px;">
|
||||
|
@ -53,22 +51,61 @@ This file is a Thymeleaf template for the
|
|||
|
||||
<td width="70%" valign="top">
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Request
|
||||
</div>
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td class="propertyKeyCell">Action</td>
|
||||
<td th:text="${action}">GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="propertyKeyCell">URL</td>
|
||||
<td>
|
||||
<a href="http://foo.com/fhir" th:href="${requestUrl}"><th:block th:text="${requestUrl}">http://foo.com/fhir</th:block></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr > <!-- th:if="${not #lists.isEmpty(requestHeaders)}" -->
|
||||
<td valign="top" class="propertyKeyCell">Headers</td>
|
||||
<td valign="top">
|
||||
<div th:each="header : ${requestHeaders}">
|
||||
<span class="headerName" th:text="${header.name} + ': '"/>
|
||||
<span class="headerValue" th:text="${header.value}"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${requestBody} != null">
|
||||
<td valign="top" class="propertyKeyCell">Request Body</td>
|
||||
<td valign="top"><pre th:text="${requestBody}" th:class="${requestSyntaxHighlighterClass}">{}</pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="bodyHeaderBlock">
|
||||
Response
|
||||
</div>
|
||||
<table border="0">
|
||||
<table border="0" class="propertyTable">
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Status</td>
|
||||
<td valign="top" th:text="${resultStatus}">HTTP 200 OK</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top" class="propertyKeyCell">Result</td>
|
||||
<tr > <!-- th:if="${not #lists.isEmpty(responseHeaders)}" -->
|
||||
<td valign="top" class="propertyKeyCell">Headers</td>
|
||||
<td valign="top">
|
||||
<div th:each="header : ${responseHeaders}">
|
||||
<span class="headerName" th:text="${header.name} + ': '"/>
|
||||
<span class="headerValue" th:text="${header.value}"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${!#strings.isEmpty(resultBody)}">
|
||||
<td valign="top" class="propertyKeyCell">Result Body</td>
|
||||
<td valign="top"><pre th:text="${resultBody}" th:class="${resultSyntaxHighlighterClass}">{}</pre></td>
|
||||
</tr>
|
||||
<tr th:if="${!#strings.isEmpty(narrative)}">
|
||||
<td valign="top" class="propertyKeyCell">Result Narrative</td>
|
||||
<td valign="top" th:utext="${narrative}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
<td>
|
||||
<a href="#instance_update">Type - Search</a>
|
||||
<macro name="toc">
|
||||
<param name="section" value="7" />
|
||||
<param name="section" value="8" />
|
||||
<param name="fromDepth" value="2" />
|
||||
</macro>
|
||||
</td>
|
||||
|
@ -451,7 +451,7 @@
|
|||
</p>
|
||||
|
||||
</subsection>
|
||||
<subsection name="Search with a String Parameter">
|
||||
<subsection name="Search Parameters: Introduction (String)">
|
||||
|
||||
<p>
|
||||
To allow a search using given search parameters, add one or more parameters
|
||||
|
@ -482,12 +482,18 @@
|
|||
</p>
|
||||
|
||||
</subsection>
|
||||
<subsection name="Search By Identifier">
|
||||
<subsection name="Search Parameters: Token/Identifier">
|
||||
|
||||
<p>
|
||||
Searches method parameters may be of any type that implements the
|
||||
<a href="./apidocs/ca/uhn/fhir/model/api/IQueryParameterType.html">IQueryParameterType</a>
|
||||
interface. For example, the search below can be used to search by
|
||||
interface.
|
||||
</p>
|
||||
<p>
|
||||
The "token" type is used for parameters which have two parts, such as
|
||||
an idnetifier (which has a system URI, as well as the actual identifier)
|
||||
or a code (which has a code system, as well as the actual code).
|
||||
For example, the search below can be used to search by
|
||||
identifier (e.g. search for an MRN).
|
||||
</p>
|
||||
|
||||
|
@ -503,11 +509,122 @@
|
|||
</p>
|
||||
|
||||
</subsection>
|
||||
<subsection name="Multiple Parameters">
|
||||
|
||||
<subsection name="Search Parameters: Date (Simple)">
|
||||
|
||||
<p>
|
||||
The FHIR specification provides a sytax for specifying
|
||||
dates (and date/times as well, but for simplicity we will just say dates here)
|
||||
as search criteria.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Dates may be optionally prefixed with a qualifier. For example, the
|
||||
string
|
||||
<code>>=2011-01-02</code>
|
||||
means any date on or after 2011-01-02.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To accept a qualified date parameter, use the
|
||||
QualifiedDateParam
|
||||
parameter type.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dates" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
Example URL to invoke this method:
|
||||
<br />
|
||||
<code>http://fhir.example.com/Observation?birthdate=>=2011-01-02</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Invoking a client of thie type involves the following syntax:
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dateClient" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<a name="DATE_RANGES" />
|
||||
<subsection name="Search Parameters: Date (Ranges)">
|
||||
|
||||
<p>
|
||||
A common scenario in searches is to allow searching for resources
|
||||
with values (i.e timestamps) within a range of dates.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
FHIR allows for multiple parameters with the same key, and interprets
|
||||
these as being an "AND" set. So, for example, a range of
|
||||
<br />
|
||||
<code>DATE >= 2011-01-01</code>
|
||||
and
|
||||
<code>DATE < 2011-02-01</code>
|
||||
<br />
|
||||
can be interpreted as any date within January 2011.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following snippet shows how to accept such a range, and combines it
|
||||
with a specific identifier, which is a common scenario. (i.e. Give me a list
|
||||
of observations for a
|
||||
specific patient within a given date range)
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dateRange" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
Example URL to invoke this method:
|
||||
<br />
|
||||
<code>http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01&date=<2011-02-01</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Invoking a client of this type involves the following syntax:
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dateClient" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<h4>Unbounded Ranges</h4>
|
||||
|
||||
<p>
|
||||
Note that when using a date range parameter, it is also possible for
|
||||
the client to request an "unbounded" range. In other words, a range that
|
||||
only a start date and no end date, or vice versa.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An example of this might be the following URL, which refers to any Observation
|
||||
resources for the given MRN and having a date after 2011-01-01.
|
||||
<br/>
|
||||
<code>http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01</code><br/>
|
||||
When such a request is made of a server (or to make such a request from a client),
|
||||
the <code>getLowerBound()</code> or <code>getUpperBound()</code> property of the
|
||||
<code>DateRangeParam</code> object will be set to <code>null</code>.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Combining Multiple Parameters">
|
||||
|
||||
<p>
|
||||
Search methods may take multiple parameters, and these parameters
|
||||
may be optional. To add a second required parameter, annotate the
|
||||
may (or may not) be optional.
|
||||
aaaa
|
||||
To add a second required parameter, annotate the
|
||||
parameter with
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/parameters/RequiredParam.html">@RequiredParam</a>
|
||||
.
|
||||
|
@ -614,96 +731,6 @@
|
|||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Date Parameters - Simple">
|
||||
|
||||
<p>
|
||||
The FHIR specification provides a sytax for specifying
|
||||
dates (and date/times as well, but for simplicity we will just say dates here)
|
||||
as search criteria.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Dates may be optionally prefixed with a qualifier. For example, the
|
||||
string
|
||||
<code>>=2011-01-02</code>
|
||||
means any date on or after 2011-01-02.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To accept a qualified date parameter, use the
|
||||
QualifiedDateParam
|
||||
parameter type.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dates" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
Example URL to invoke this method:
|
||||
<br />
|
||||
<code>http://fhir.example.com/Observation?birthdate=>=2011-01-02</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Invoking a client of thie type involves the following syntax:
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dateClient" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<a name="DATE_RANGES" />
|
||||
<subsection name="Date Parameters - Ranges">
|
||||
|
||||
<p>
|
||||
A common scenario in searches is to allow searching for resources
|
||||
with values (i.e timestamps) within a range of dates.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
FHIR allows for multiple parameters with the same key, and interprets
|
||||
these as being an "AND" set. So, for example, a range of
|
||||
<br />
|
||||
<code>DATE >= 2011-01-01</code>
|
||||
and
|
||||
<code>DATE < 2011-02-01</code>
|
||||
<br />
|
||||
can be interpreted as any date within January 2011.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following snippet shows how to accept such a range, and combines it
|
||||
with a specific identifier, which is a common scenario. (i.e. Give me a list
|
||||
of observations for a
|
||||
specific patient within a given date range)
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dateRange" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<p>
|
||||
Example URL to invoke this method:
|
||||
<br />
|
||||
<code>http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01&date=<2011-02-01</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Invoking a client of this type involves the following syntax:
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dateClient" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Resource Includes (_include)">
|
||||
|
||||
<p>
|
||||
|
|
|
@ -5,14 +5,11 @@ import java.util.List;
|
|||
import ca.uhn.fhir.model.api.BaseResource;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
import ca.uhn.fhir.model.api.IExtension;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.annotation.Block;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
|
@ -44,8 +41,6 @@ public class ResourceWithExtensionsA extends BaseResource {
|
|||
@Child(name = "identifier", type = IdentifierDt.class, order = 0, min = 0, max = Child.MAX_UNLIMITED)
|
||||
private List<IdentifierDt> myIdentifier;
|
||||
|
||||
private IdDt myId;
|
||||
|
||||
public List<Bar1> getBar1() {
|
||||
return myBar1;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class XmlParserTest {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlParserTest.class);
|
||||
|
||||
@Test
|
||||
public void testEncodeBoundCode() throws IOException {
|
||||
public void testEncodeBoundCode() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addAddress().setUse(AddressUseEnum.HOME);
|
||||
|
@ -73,7 +73,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeExtensionWithResourceContent() throws IOException {
|
||||
public void testEncodeExtensionWithResourceContent() {
|
||||
IParser parser = new FhirContext().newXmlParser();
|
||||
|
||||
Patient patient = new Patient();
|
||||
|
@ -94,7 +94,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDeclaredExtensionWithResourceContent() throws IOException {
|
||||
public void testEncodeDeclaredExtensionWithResourceContent() {
|
||||
IParser parser = new FhirContext().newXmlParser();
|
||||
|
||||
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
|
||||
|
@ -113,7 +113,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeDeclaredExtensionWithAddressContent() throws IOException {
|
||||
public void testEncodeDeclaredExtensionWithAddressContent() {
|
||||
IParser parser = new FhirContext().newXmlParser();
|
||||
|
||||
MyPatientWithOneDeclaredAddressExtension patient = new MyPatientWithOneDeclaredAddressExtension();
|
||||
|
@ -132,7 +132,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeUndeclaredExtensionWithAddressContent() throws IOException {
|
||||
public void testEncodeUndeclaredExtensionWithAddressContent() {
|
||||
IParser parser = new FhirContext().newXmlParser();
|
||||
|
||||
Patient patient = new Patient();
|
||||
|
@ -151,7 +151,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeBundleResultCount() throws IOException {
|
||||
public void testEncodeBundleResultCount() {
|
||||
|
||||
Bundle b = new Bundle();
|
||||
b.getTotalResults().setValue(123);
|
||||
|
@ -166,7 +166,7 @@ public class XmlParserTest {
|
|||
|
||||
|
||||
@Test
|
||||
public void testEncodeContainedResources() throws IOException {
|
||||
public void testEncodeContainedResources() {
|
||||
|
||||
DiagnosticReport rpt = new DiagnosticReport();
|
||||
Specimen spm = new Specimen();
|
||||
|
@ -189,7 +189,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeInvalidChildGoodException() throws IOException {
|
||||
public void testEncodeInvalidChildGoodException() {
|
||||
Observation obs = new Observation();
|
||||
obs.setValue(new DecimalDt(112.22));
|
||||
|
||||
|
@ -203,7 +203,7 @@ public class XmlParserTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeResourceRef() throws DataFormatException, IOException {
|
||||
public void testEncodeResourceRef() throws DataFormatException {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setManagingOrganization(new ResourceReferenceDt());
|
||||
|
@ -226,7 +226,7 @@ public class XmlParserTest {
|
|||
|
||||
|
||||
@Test
|
||||
public void testExtensions() throws DataFormatException, IOException {
|
||||
public void testExtensions() throws DataFormatException {
|
||||
|
||||
MyPatient patient = new MyPatient();
|
||||
patient.setPetName(new StringDt("Fido"));
|
||||
|
@ -425,7 +425,7 @@ public class XmlParserTest {
|
|||
|
||||
|
||||
@Test
|
||||
public void testNarrativeGeneration() throws DataFormatException, IOException {
|
||||
public void testNarrativeGeneration() throws DataFormatException {
|
||||
|
||||
Patient patient = new Patient();
|
||||
|
||||
|
|
|
@ -79,34 +79,6 @@ public class ClientTest {
|
|||
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
}
|
||||
|
||||
private String getPatientFeedWithOneResult() {
|
||||
//@formatter:off
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
|
||||
"<title/>\n" +
|
||||
"<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" +
|
||||
"<os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults>\n" +
|
||||
"<published>2014-03-11T16:35:07-04:00</published>\n" +
|
||||
"<author>\n" +
|
||||
"<name>ca.uhn.fhir.rest.server.DummyRestfulServer</name>\n" +
|
||||
"</author>\n" +
|
||||
"<entry>\n" +
|
||||
"<content type=\"text/xml\">"
|
||||
+ "<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
|
||||
+ "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
|
||||
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
|
||||
+ "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
|
||||
+ "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
|
||||
+ "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
|
||||
+ "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
|
||||
+ "</Patient>"
|
||||
+ "</content>\n"
|
||||
+ " </entry>\n"
|
||||
+ "</feed>";
|
||||
//@formatter:on
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
|
||||
|
@ -209,8 +181,27 @@ public class ClientTest {
|
|||
@Test
|
||||
public void testHistoryResourceInstance() throws Exception {
|
||||
|
||||
InstantDt date1 = new InstantDt(new Date(20000L));
|
||||
InstantDt date2 = new InstantDt(new Date(10000L));
|
||||
InstantDt date3 = new InstantDt(new Date(30000L));
|
||||
InstantDt date4 = new InstantDt(new Date(10000L));
|
||||
|
||||
//@formatter:off
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:20.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:30.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/>"
|
||||
+ "<link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>"
|
||||
+ "<published>2014-04-13T18:24:50-04:00</published>"
|
||||
+ "<author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author>"
|
||||
+ "<entry><title>Patient 222</title><id>222</id>"
|
||||
+ "<updated>"+date1.getValueAsString()+"</updated>"
|
||||
+ "<published>"+date2.getValueAsString()+"</published>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/>"
|
||||
+ "<content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content>"
|
||||
+ "</entry>"
|
||||
+ "<entry><title>Patient 222</title><id>222</id>"
|
||||
+ "<updated>"+date3.getValueAsString()+"</updated>"
|
||||
+ "<published>"+date4.getValueAsString()+"</published>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
//@formatter:on
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
|
@ -260,55 +251,30 @@ public class ClientTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHistoryWithParams() throws Exception {
|
||||
|
||||
//@formatter:off
|
||||
final String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:20.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:30.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
//@formatter:on
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), new IntegerDt(12));
|
||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02&_count=12", capt.getAllValues().get(0).getURI().toString());
|
||||
|
||||
String expectedDateString= new InstantDt(new InstantDt("2012-01-02T00:01:02").getValue()).getValueAsString(); // ensures the local timezone
|
||||
expectedDateString=expectedDateString.replace(":", "%3A");
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02").getValue(), new IntegerDt(12).getValue());
|
||||
assertEquals("http://foo/Patient/111/_history?_since="+expectedDateString+"&_count=12", capt.getAllValues().get(1).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
||||
assertEquals("http://foo/Patient/111/_history?_count=12", capt.getAllValues().get(2).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), null);
|
||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02", capt.getAllValues().get(3).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt(), new IntegerDt(12));
|
||||
assertEquals("http://foo/Patient/111/_history?_count=12", capt.getAllValues().get(2).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), new IntegerDt());
|
||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02", capt.getAllValues().get(3).getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryResourceType() throws Exception {
|
||||
|
||||
InstantDt date1 = new InstantDt(new Date(20000L));
|
||||
InstantDt date2 = new InstantDt(new Date(10000L));
|
||||
InstantDt date3 = new InstantDt(new Date(30000L));
|
||||
InstantDt date4 = new InstantDt(new Date(10000L));
|
||||
|
||||
//@formatter:off
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:20.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:30.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/>"
|
||||
+ "<link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>"
|
||||
+ "<published>2014-04-13T18:24:50-04:00</published>"
|
||||
+ "<author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author>"
|
||||
+ "<entry><title>Patient 222</title><id>222</id>"
|
||||
+ "<updated>"+date1.getValueAsString()+"</updated>"
|
||||
+ "<published>"+date2.getValueAsString()+"</published>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/>"
|
||||
+ "<content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content>"
|
||||
+ "</entry>"
|
||||
+ "<entry><title>Patient 222</title><id>222</id>"
|
||||
+ "<updated>"+date3.getValueAsString()+"</updated>"
|
||||
+ "<published>"+date4.getValueAsString()+"</published>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
//@formatter:on
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
|
@ -358,11 +324,30 @@ public class ClientTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHistoryServer() throws Exception {
|
||||
InstantDt date1 = new InstantDt(new Date(20000L));
|
||||
InstantDt date2 = new InstantDt(new Date(10000L));
|
||||
InstantDt date3 = new InstantDt(new Date(30000L));
|
||||
InstantDt date4 = new InstantDt(new Date(10000L));
|
||||
|
||||
//@formatter:off
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:20.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:30.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/>"
|
||||
+ "<link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>"
|
||||
+ "<published>2014-04-13T18:24:50-04:00</published>"
|
||||
+ "<author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author>"
|
||||
+ "<entry><title>Patient 222</title><id>222</id>"
|
||||
+ "<updated>"+date1.getValueAsString()+"</updated>"
|
||||
+ "<published>"+date2.getValueAsString()+"</published>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/>"
|
||||
+ "<content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content>"
|
||||
+ "</entry>"
|
||||
+ "<entry><title>Patient 222</title><id>222</id>"
|
||||
+ "<updated>"+date3.getValueAsString()+"</updated>"
|
||||
+ "<published>"+date4.getValueAsString()+"</published>"
|
||||
+ "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
//@formatter:on
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
|
@ -412,6 +397,49 @@ public class ClientTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryWithParams() throws Exception {
|
||||
|
||||
//@formatter:off
|
||||
final String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:20.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:30.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
//@formatter:on
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), new IntegerDt(12));
|
||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02&_count=12", capt.getAllValues().get(0).getURI().toString());
|
||||
|
||||
String expectedDateString= new InstantDt(new InstantDt("2012-01-02T00:01:02").getValue()).getValueAsString(); // ensures the local timezone
|
||||
expectedDateString=expectedDateString.replace(":", "%3A");
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02").getValue(), new IntegerDt(12).getValue());
|
||||
assertEquals("http://foo/Patient/111/_history?_since="+expectedDateString+"&_count=12", capt.getAllValues().get(1).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
||||
assertEquals("http://foo/Patient/111/_history?_count=12", capt.getAllValues().get(2).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), null);
|
||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02", capt.getAllValues().get(3).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt(), new IntegerDt(12));
|
||||
assertEquals("http://foo/Patient/111/_history?_count=12", capt.getAllValues().get(2).getURI().toString());
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), new IntegerDt());
|
||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02", capt.getAllValues().get(3).getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() throws Exception {
|
||||
|
||||
|
@ -488,62 +516,6 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomType() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
ITestClientWithCustomType client = ctx.newRestfulClient(ITestClientWithCustomType.class, "http://foo");
|
||||
CustomPatient response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||
|
||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getValue().getURI().toString());
|
||||
assertEquals("PRP1660", response.getIdentifier().get(0).getValue().getValue());
|
||||
|
||||
}
|
||||
|
||||
public interface ITestClientWithCustomType extends IBasicClient {
|
||||
@Search()
|
||||
public CustomPatient getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomTypeList() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
ITestClientWithCustomTypeList client = ctx.newRestfulClient(ITestClientWithCustomTypeList.class, "http://foo");
|
||||
List<CustomPatient> response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||
|
||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getValue().getURI().toString());
|
||||
assertEquals("PRP1660", response.get(0).getIdentifier().get(0).getValue().getValue());
|
||||
|
||||
}
|
||||
|
||||
public interface ITestClientWithCustomTypeList extends IBasicClient {
|
||||
@Search()
|
||||
public List<CustomPatient> getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
||||
}
|
||||
|
||||
@ResourceDef(name="Patient")
|
||||
public static class CustomPatient extends Patient
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByToken() throws Exception {
|
||||
|
||||
|
@ -563,6 +535,7 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchComposite() throws Exception {
|
||||
|
||||
|
@ -620,6 +593,45 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomType() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
ITestClientWithCustomType client = ctx.newRestfulClient(ITestClientWithCustomType.class, "http://foo");
|
||||
CustomPatient response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||
|
||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getValue().getURI().toString());
|
||||
assertEquals("PRP1660", response.getIdentifier().get(0).getValue().getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomTypeList() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
ITestClientWithCustomTypeList client = ctx.newRestfulClient(ITestClientWithCustomTypeList.class, "http://foo");
|
||||
List<CustomPatient> response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||
|
||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getValue().getURI().toString());
|
||||
assertEquals("PRP1660", response.get(0).getIdentifier().get(0).getValue().getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithIncludes() throws Exception {
|
||||
|
||||
|
@ -638,29 +650,6 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithStringIncludes() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
ITestClientWithStringIncludes client = ctx.newRestfulClient(ITestClientWithStringIncludes.class, "http://foo");
|
||||
client.getPatientWithIncludes(new StringDt("aaa"), "inc1");
|
||||
|
||||
assertEquals("http://foo/Patient?withIncludes=aaa&_include=inc1", capt.getValue().getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
public interface ITestClientWithStringIncludes extends IBasicClient {
|
||||
@Search()
|
||||
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringDt theString, @IncludeParam String theInclude);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithOptionalParam() throws Exception {
|
||||
|
||||
|
@ -693,6 +682,24 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithStringIncludes() throws Exception {
|
||||
|
||||
String msg = getPatientFeedWithOneResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
ITestClientWithStringIncludes client = ctx.newRestfulClient(ITestClientWithStringIncludes.class, "http://foo");
|
||||
client.getPatientWithIncludes(new StringDt("aaa"), "inc1");
|
||||
|
||||
assertEquals("http://foo/Patient?withIncludes=aaa&_include=inc1", capt.getValue().getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
|
||||
|
@ -778,6 +785,31 @@ public class ClientTest {
|
|||
assertEquals("200", response.getVersionId().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidate() throws Exception {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier("urn:foo", "123");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
|
||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
|
||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||
when(httpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200"));
|
||||
|
||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||
MethodOutcome response = client.validatePatient(patient);
|
||||
|
||||
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||
HttpPost post = (HttpPost) capt.getValue();
|
||||
assertThat(post.getURI().toASCIIString(), StringEndsWith.endsWith("/Patient/_validate"));
|
||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||
assertEquals("100", response.getId().getValue());
|
||||
assertEquals("200", response.getVersionId().getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVRead() throws Exception {
|
||||
|
||||
|
@ -808,7 +840,56 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
private String getPatientFeedWithOneResult() {
|
||||
//@formatter:off
|
||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
|
||||
"<title/>\n" +
|
||||
"<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" +
|
||||
"<os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults>\n" +
|
||||
"<published>2014-03-11T16:35:07-04:00</published>\n" +
|
||||
"<author>\n" +
|
||||
"<name>ca.uhn.fhir.rest.server.DummyRestfulServer</name>\n" +
|
||||
"</author>\n" +
|
||||
"<entry>\n" +
|
||||
"<content type=\"text/xml\">"
|
||||
+ "<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
|
||||
+ "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
|
||||
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
|
||||
+ "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
|
||||
+ "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
|
||||
+ "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
|
||||
+ "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
|
||||
+ "</Patient>"
|
||||
+ "</content>\n"
|
||||
+ " </entry>\n"
|
||||
+ "</feed>";
|
||||
//@formatter:on
|
||||
return msg;
|
||||
}
|
||||
|
||||
private Header[] toHeaderArray(String theName, String theValue) {
|
||||
return new Header[] { new BasicHeader(theName, theValue) };
|
||||
}
|
||||
|
||||
@ResourceDef(name="Patient")
|
||||
public static class CustomPatient extends Patient
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
public interface ITestClientWithCustomType extends IBasicClient {
|
||||
@Search()
|
||||
public CustomPatient getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
||||
}
|
||||
|
||||
public interface ITestClientWithCustomTypeList extends IBasicClient {
|
||||
@Search()
|
||||
public List<CustomPatient> getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
||||
}
|
||||
|
||||
public interface ITestClientWithStringIncludes extends IBasicClient {
|
||||
@Search()
|
||||
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringDt theString, @IncludeParam String theInclude);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.annotation.ResourceParam;
|
|||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||
|
@ -94,4 +95,7 @@ public interface ITestClient extends IBasicClient {
|
|||
@Read(type=Patient.class)
|
||||
Patient getPatientByVersionId(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId);
|
||||
|
||||
@Validate(type=Patient.class)
|
||||
MethodOutcome validatePatient(@ResourceParam Patient thePatient);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
@ -171,11 +172,6 @@ public class ResfulServerMethodTest {
|
|||
@Test
|
||||
public void testDelete() throws Exception {
|
||||
|
||||
// HttpPost httpPost = new HttpPost("http://localhost:" + ourPort +
|
||||
// "/Patient/1");
|
||||
// httpPost.setEntity(new StringEntity("test",
|
||||
// ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
|
||||
HttpDelete httpGet = new HttpDelete("http://localhost:" + ourPort + "/Patient/1234");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
|
@ -189,6 +185,22 @@ public class ResfulServerMethodTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithAdditionalParams() throws Exception {
|
||||
|
||||
HttpDelete httpGet = new HttpDelete("http://localhost:" + ourPort + "/Patient/1234?_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
OperationOutcome patient = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent);
|
||||
assertEquals("1234", patient.getIssueFirstRep().getDetails().getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteNoResponse() throws Exception {
|
||||
|
||||
|
@ -431,6 +443,28 @@ public class ResfulServerMethodTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryFailsIfResourcesAreIncorrectlyPopulated() throws Exception {
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/999/_history");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||
}
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/998/_history");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryResourceType() throws Exception {
|
||||
|
||||
|
@ -532,7 +566,6 @@ public class ResfulServerMethodTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReadOnTypeThatDoesntSupportRead() throws Exception {
|
||||
|
||||
|
@ -865,7 +898,7 @@ public class ResfulServerMethodTest {
|
|||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
OperationOutcome oo =new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
|
||||
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
|
||||
assertEquals("OODETAILS", oo.getIssueFirstRep().getDetails().getValue());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
@ -873,11 +906,12 @@ public class ResfulServerMethodTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
public void testUpdateWrongResourceType() throws Exception {
|
||||
|
||||
// TODO: this method sends in the wrong resource type vs. the URL so it should
|
||||
// give a useful error message (and then make this unit test actually run)
|
||||
// TODO: this method sends in the wrong resource type vs. the URL so it
|
||||
// should
|
||||
// give a useful error message (and then make this unit test actually
|
||||
// run)
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("002");
|
||||
|
||||
|
@ -907,7 +941,6 @@ public class ResfulServerMethodTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUpdateWithVersion() throws Exception {
|
||||
|
||||
|
@ -920,8 +953,9 @@ public class ResfulServerMethodTest {
|
|||
|
||||
HttpResponse status = ourClient.execute(httpPut);
|
||||
|
||||
// String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
// ourLog.info("Response was:\n{}", responseContent);
|
||||
// String responseContent =
|
||||
// IOUtils.toString(status.getEntity().getContent());
|
||||
// ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||
assertNull(status.getEntity());
|
||||
|
@ -946,6 +980,22 @@ public class ResfulServerMethodTest {
|
|||
assertEquals(400, results.getStatusLine().getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateWithPrettyPrintResponse() throws Exception {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FOO");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_validate?_pretty=true");
|
||||
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
assertThat(responseContent, containsString("\n "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidate() throws Exception {
|
||||
|
||||
|
@ -959,6 +1009,7 @@ public class ResfulServerMethodTest {
|
|||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
assertThat(responseContent, not(containsString("\n ")));
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
OperationOutcome oo = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, responseContent);
|
||||
|
@ -991,12 +1042,12 @@ public class ResfulServerMethodTest {
|
|||
|
||||
status = ourClient.execute(httpPost);
|
||||
|
||||
// responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
// ourLog.info("Response was:\n{}", responseContent);
|
||||
// responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
// ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||
assertNull(status.getEntity());
|
||||
// assertEquals("", responseContent);
|
||||
// assertEquals("", responseContent);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1084,8 +1135,7 @@ public class ResfulServerMethodTest {
|
|||
|
||||
/*
|
||||
* *********************
|
||||
* NO NEW METHODS
|
||||
* *********************
|
||||
* NO NEW METHODS *********************
|
||||
*/
|
||||
|
||||
@Override
|
||||
|
@ -1132,19 +1182,28 @@ public class ResfulServerMethodTest {
|
|||
public List<Patient> getHistoryResourceInstance(@IdParam IdDt theId) {
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
|
||||
IdDt id = theId;
|
||||
if (id.getValue().equals("999")) {
|
||||
id = null; // to test the error when no ID is present
|
||||
}
|
||||
|
||||
Patient older = createPatient1();
|
||||
older.setId(theId);
|
||||
older.setId(id);
|
||||
older.getNameFirstRep().getFamilyFirstRep().setValue("OlderFamily");
|
||||
older.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, "1");
|
||||
older.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new Date(10000L));
|
||||
older.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, new InstantDt(new Date(20000L)));
|
||||
older.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, "1");
|
||||
if (id != null && !id.getValue().equals("998")) {
|
||||
older.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, "1");
|
||||
}
|
||||
retVal.add(older);
|
||||
|
||||
Patient newer = createPatient1();
|
||||
newer.setId(theId);
|
||||
newer.getNameFirstRep().getFamilyFirstRep().setValue("NewerFamily");
|
||||
newer.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, "2");
|
||||
if (id != null && !id.getValue().equals("998")) {
|
||||
newer.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, "2");
|
||||
}
|
||||
newer.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new Date(10000L));
|
||||
newer.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, new InstantDt(new Date(30000L)));
|
||||
retVal.add(newer);
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -20,7 +17,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
|||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||
|
@ -28,19 +25,26 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.tester.PublicTesterServlet;
|
||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||
|
||||
public class TesterTest {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TesterTest.class);
|
||||
private int myPort;
|
||||
private Server myServer;
|
||||
private FhirContext myCtx;
|
||||
|
@ -51,14 +55,16 @@ public class TesterTest {
|
|||
myPort = RandomServerPortProvider.findFreePort();
|
||||
myPort = 8888;
|
||||
myServer = new Server(myPort);
|
||||
myCtx = new FhirContext(Patient.class);
|
||||
myRestfulServer = new RestfulServer();
|
||||
myCtx = new FhirContext();
|
||||
myCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
myRestfulServer = new RestfulServer(myCtx);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
PublicTesterServlet testerServlet = new PublicTesterServlet();
|
||||
testerServlet.setServerBase("http://localhost:" + myPort + "/fhir/context");
|
||||
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
|
||||
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
|
||||
ServletHolder handler = new ServletHolder();
|
||||
handler.setServlet(testerServlet);
|
||||
proxyHandler.addServlet(handler, "/fhir/tester/*");
|
||||
|
@ -78,7 +84,7 @@ public class TesterTest {
|
|||
|
||||
@Test
|
||||
public void testTester() throws Exception {
|
||||
if (true) return;
|
||||
if (true) return;
|
||||
|
||||
myRestfulServer.setProviders(new SearchProvider(), new GlobalHistoryProvider());
|
||||
myServer.start();
|
||||
|
@ -92,11 +98,27 @@ public class TesterTest {
|
|||
*/
|
||||
public static class SearchProvider {
|
||||
|
||||
public Map<String, Patient> getIdToPatient() {
|
||||
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
|
||||
private int myNextId = 1;
|
||||
private HashMap<String, Patient> myIdToPatient;
|
||||
|
||||
private static Patient createPatient() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00001");
|
||||
patient.addName();
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientOne");
|
||||
patient.getGender().setText("M");
|
||||
return patient;
|
||||
}
|
||||
|
||||
public SearchProvider() {
|
||||
myIdToPatient = new HashMap<String, Patient>();
|
||||
{
|
||||
Patient patient = createPatient();
|
||||
idToPatient.put("1", patient);
|
||||
myIdToPatient.put("" + myNextId++, patient);
|
||||
}
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
|
@ -108,17 +130,42 @@ public class TesterTest {
|
|||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientTwo");
|
||||
patient.getGender().setText("F");
|
||||
idToPatient.put("2", patient);
|
||||
myIdToPatient.put("" + myNextId++, patient);
|
||||
}
|
||||
return idToPatient;
|
||||
}
|
||||
|
||||
@Create
|
||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||
IdDt id = new IdDt(myNextId++);
|
||||
myIdToPatient.put(id.getValueAsString(), thePatient);
|
||||
return new MethodOutcome(id);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Validate
|
||||
public MethodOutcome validatePatient(@ResourceParam Patient thePatient) {
|
||||
MethodOutcome outcome = new MethodOutcome();
|
||||
outcome.setOperationOutcome(new OperationOutcome());
|
||||
outcome.getOperationOutcome().addIssue().setDetails("This is a detected issue");
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
||||
@Update
|
||||
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
||||
myIdToPatient.put(theId.getValueAsString(), thePatient);
|
||||
return new MethodOutcome(theId);
|
||||
}
|
||||
|
||||
@Delete(type=Patient.class)
|
||||
public MethodOutcome deletePatient(@IdParam IdDt theId) {
|
||||
myIdToPatient.remove(theId.getValue());
|
||||
return new MethodOutcome();
|
||||
}
|
||||
|
||||
@Search(type = Patient.class)
|
||||
public Patient findPatient(
|
||||
@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()) {
|
||||
public Patient findPatient(@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 : myIdToPatient.values()) {
|
||||
for (IdentifierDt nextId : next.getIdentifier()) {
|
||||
if (nextId.matchesSystemAndValue(theIdentifier)) {
|
||||
return next;
|
||||
|
@ -137,7 +184,30 @@ public class TesterTest {
|
|||
*/
|
||||
@Read(type = Patient.class)
|
||||
public Patient getPatientById(@IdParam IdDt theId) {
|
||||
return getIdToPatient().get(theId.getValue());
|
||||
return myIdToPatient.get(theId.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the resource by its identifier
|
||||
*
|
||||
* @param theId
|
||||
* The resource identity
|
||||
* @return The resource
|
||||
*/
|
||||
@History(type = Patient.class)
|
||||
public List<Patient> getPatientHistory(@IdParam IdDt theId) {
|
||||
Patient patient = myIdToPatient.get(theId.getValue());
|
||||
if (patient==null) {
|
||||
throw new ResourceNotFoundException(Patient.class, theId);
|
||||
}
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
for (int i = 0; i < 5;i++) {
|
||||
Patient pat = createPatient();
|
||||
pat.setId(theId);
|
||||
pat.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, ""+i);
|
||||
retVal.add(pat);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -153,14 +223,14 @@ public class TesterTest {
|
|||
myLastCount = theCount;
|
||||
ArrayList<IResource> retVal = new ArrayList<IResource>();
|
||||
|
||||
IResource p = createPatient();
|
||||
IResource p = SearchProvider.createPatient();
|
||||
p.setId(new IdDt("1"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt("A"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new InstantDt("2012-01-01T00:00:01"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, new InstantDt("2012-01-01T01:00:01"));
|
||||
retVal.add(p);
|
||||
|
||||
p = createPatient();
|
||||
p = SearchProvider.createPatient();
|
||||
p.setId(new IdDt("1"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt("B"));
|
||||
p.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, new InstantDt("2012-01-01T00:00:01"));
|
||||
|
@ -179,18 +249,6 @@ public class TesterTest {
|
|||
|
||||
}
|
||||
|
||||
private static Patient createPatient() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00001");
|
||||
patient.addName();
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientOne");
|
||||
patient.getGender().setText("M");
|
||||
return patient;
|
||||
}
|
||||
|
||||
private static Organization createOrganization() {
|
||||
Organization retVal = new Organization();
|
||||
|
|
Loading…
Reference in New Issue