Add interceptor framework
This commit is contained in:
parent
2285d3812e
commit
d15dbd4317
|
@ -6,6 +6,7 @@
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="bin" path="src/site/example/java"/>
|
||||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
<wb-module deploy-name="hapi-fhir-base">
|
<wb-module deploy-name="hapi-fhir-base">
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/java"/>
|
<wb-resource deploy-path="/" source-path="/src/main/java"/>
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
|
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
|
||||||
|
<wb-resource deploy-path="/" source-path="/src/site/example/java"/>
|
||||||
</wb-module>
|
</wb-module>
|
||||||
</project-modules>
|
</project-modules>
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>1.7.6</version>
|
<version>${slf4j_version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
|
|
@ -14,6 +14,19 @@
|
||||||
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
|
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
|
||||||
"&identifier=system|codepart1\|codepart2"
|
"&identifier=system|codepart1\|codepart2"
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for OPTIONS verb (which returns the server conformance statement)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for CORS headers in server
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Bump SLF4j dependency to latest version (1.7.7)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add interceptor framework for clients (annotation based and generic), and add interceptors
|
||||||
|
for configurable logging, capturing requests and responses, and HTTP basic auth.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.4" date="2014-Jul-13">
|
<release version="0.4" date="2014-Jul-13">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
|
@ -127,7 +127,7 @@ public abstract class BaseResource extends BaseElement implements IResource {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean isBaseEmpty() {
|
protected boolean isBaseEmpty() {
|
||||||
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText);
|
return super.isBaseEmpty() && ElementUtil.isEmpty(myLanguage, myText, myId);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,8 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
for (BundleEntry nextEntry : theBundle.getEntries()) {
|
for (BundleEntry nextEntry : theBundle.getEntries()) {
|
||||||
eventWriter.writeStartObject();
|
eventWriter.writeStartObject();
|
||||||
|
|
||||||
if (nextEntry.getDeletedAt() !=null&&nextEntry.getDeletedAt().isEmpty()==false) {
|
boolean deleted = nextEntry.getDeletedAt() !=null&&nextEntry.getDeletedAt().isEmpty()==false;
|
||||||
|
if (deleted) {
|
||||||
writeTagWithTextNode(eventWriter, "deleted", nextEntry.getDeletedAt());
|
writeTagWithTextNode(eventWriter, "deleted", nextEntry.getDeletedAt());
|
||||||
}
|
}
|
||||||
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
|
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
|
||||||
|
@ -210,7 +211,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
writeAuthor(nextEntry, eventWriter);
|
writeAuthor(nextEntry, eventWriter);
|
||||||
|
|
||||||
IResource resource = nextEntry.getResource();
|
IResource resource = nextEntry.getResource();
|
||||||
if (resource != null && !resource.isEmpty()) {
|
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
|
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
|
||||||
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
|
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content", false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,7 +199,7 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
IResource resource = nextEntry.getResource();
|
IResource resource = nextEntry.getResource();
|
||||||
if (resource != null && !resource.isEmpty()) {
|
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||||
eventWriter.writeStartElement("content");
|
eventWriter.writeStartElement("content");
|
||||||
eventWriter.writeAttribute("type", "text/xml");
|
eventWriter.writeAttribute("type", "text/xml");
|
||||||
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
|
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
|
|
||||||
|
import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||||
|
@ -51,6 +52,8 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
|
||||||
|
|
||||||
myMethodToLambda.put(theClientType.getMethod("setEncoding", EncodingEnum.class), new SetEncodingLambda());
|
myMethodToLambda.put(theClientType.getMethod("setEncoding", EncodingEnum.class), new SetEncodingLambda());
|
||||||
myMethodToLambda.put(theClientType.getMethod("setPrettyPrint", boolean.class), new SetPrettyPrintLambda());
|
myMethodToLambda.put(theClientType.getMethod("setPrettyPrint", boolean.class), new SetPrettyPrintLambda());
|
||||||
|
myMethodToLambda.put(theClientType.getMethod("registerInterceptor", IClientInterceptor.class), new RegisterInterceptorLambda());
|
||||||
|
myMethodToLambda.put(theClientType.getMethod("unregisterInterceptor", IClientInterceptor.class), new UnregisterInterceptorLambda());
|
||||||
|
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
|
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
|
||||||
|
@ -105,5 +108,22 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private class UnregisterInterceptorLambda implements ILambda {
|
||||||
|
@Override
|
||||||
|
public Object handle(Object[] theArgs) {
|
||||||
|
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
|
||||||
|
unregisterInterceptor(interceptor);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RegisterInterceptorLambda implements ILambda {
|
||||||
|
@Override
|
||||||
|
public Object handle(Object[] theArgs) {
|
||||||
|
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
|
||||||
|
registerInterceptor(interceptor);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,14 @@ import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
|
||||||
|
@ -49,6 +51,8 @@ import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||||
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
||||||
|
import ca.uhn.fhir.rest.gclient.ICreate;
|
||||||
|
import ca.uhn.fhir.rest.gclient.ICreateTyped;
|
||||||
import ca.uhn.fhir.rest.gclient.ICriterion;
|
import ca.uhn.fhir.rest.gclient.ICriterion;
|
||||||
import ca.uhn.fhir.rest.gclient.ICriterionInternal;
|
import ca.uhn.fhir.rest.gclient.ICriterionInternal;
|
||||||
import ca.uhn.fhir.rest.gclient.IGetPage;
|
import ca.uhn.fhir.rest.gclient.IGetPage;
|
||||||
|
@ -77,6 +81,7 @@ import ca.uhn.fhir.rest.method.ValidateMethodBinding;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
public class GenericClient extends BaseClient implements IGenericClient {
|
public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
|
@ -106,6 +111,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICreate create() {
|
||||||
|
return new CreateInternal();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodOutcome create(IResource theResource) {
|
public MethodOutcome create(IResource theResource) {
|
||||||
BaseHttpClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
|
BaseHttpClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
|
||||||
|
@ -150,11 +160,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return new GetTagsInternal();
|
return new GetTagsInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ITransaction transaction() {
|
|
||||||
return new TransactionInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IResource> Bundle history(final Class<T> theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) {
|
public <T extends IResource> Bundle history(final Class<T> theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) {
|
||||||
String resourceName = theType != null ? toResourceName(theType) : null;
|
String resourceName = theType != null ? toResourceName(theType) : null;
|
||||||
|
@ -179,6 +184,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return myLogRequestAndResponse;
|
return myLogRequestAndResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IGetPage loadPage() {
|
||||||
|
return new LoadPageInternal();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
|
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
|
||||||
if (theId == null || theId.hasIdPart() == false) {
|
if (theId == null || theId.hasIdPart() == false) {
|
||||||
|
@ -241,6 +251,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
myLogRequestAndResponse = theLogRequestAndResponse;
|
myLogRequestAndResponse = theLogRequestAndResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String toResourceName(Class<? extends IResource> theType) {
|
||||||
|
return myContext.getResourceDefinition(theType).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ITransaction transaction() {
|
||||||
|
return new TransactionInternal();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> transaction(List<IResource> theResources) {
|
public List<IResource> transaction(List<IResource> theResources) {
|
||||||
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext);
|
BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(theResources, myContext);
|
||||||
|
@ -305,15 +324,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return vread(theType, new IdDt(theId), new IdDt(theVersionId));
|
return vread(theType, new IdDt(theId), new IdDt(theVersionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toResourceName(Class<? extends IResource> theType) {
|
|
||||||
return myContext.getResourceDefinition(theType).getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
|
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
|
||||||
private EncodingEnum myParamEncoding;
|
private EncodingEnum myParamEncoding;
|
||||||
private Boolean myPrettyPrint;
|
private Boolean myPrettyPrint;
|
||||||
private boolean myQueryLogRequestAndResponse;
|
private boolean myQueryLogRequestAndResponse;
|
||||||
|
|
||||||
|
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
|
||||||
|
if (!params.containsKey(parameterName)) {
|
||||||
|
params.put(parameterName, new ArrayList<String>());
|
||||||
|
}
|
||||||
|
params.get(parameterName).add(parameterValue);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public T andLogRequestAndResponse(boolean theLogRequestAndResponse) {
|
public T andLogRequestAndResponse(boolean theLogRequestAndResponse) {
|
||||||
|
@ -328,26 +350,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public T encodedXml() {
|
public T encodedXml() {
|
||||||
myParamEncoding = EncodingEnum.XML;
|
myParamEncoding = EncodingEnum.XML;
|
||||||
return (T) this;
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public T prettyPrint() {
|
|
||||||
myPrettyPrint = true;
|
|
||||||
return (T) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
|
|
||||||
if (!params.containsKey(parameterName)) {
|
|
||||||
params.put(parameterName, new ArrayList<String>());
|
|
||||||
}
|
|
||||||
params.get(parameterName).add(parameterValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
|
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
|
||||||
if (myParamEncoding != null) {
|
if (myParamEncoding != null) {
|
||||||
theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
|
theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
|
||||||
|
@ -365,6 +374,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public T prettyPrint() {
|
||||||
|
myPrettyPrint = true;
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
|
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
|
||||||
|
@ -376,7 +392,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
|
||||||
|
BaseServerResponseException {
|
||||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||||
if (respType == null) {
|
if (respType == null) {
|
||||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||||
|
@ -386,18 +403,70 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ResourceListResponseHandler implements IClientResponseHandler<List<IResource>> {
|
private class CreateInternal extends BaseClientExecutable<ICreateTyped, MethodOutcome> implements ICreate, ICreateTyped {
|
||||||
|
|
||||||
private Class<? extends IResource> myType;
|
private String myId;
|
||||||
|
private IResource myResource;
|
||||||
|
private String myResourceBody;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodOutcome execute() {
|
||||||
|
if (myResource == null) {
|
||||||
|
EncodingEnum encoding = null;
|
||||||
|
for (int i = 0; i < myResourceBody.length() && encoding == null; i++) {
|
||||||
|
switch (myResourceBody.charAt(i)) {
|
||||||
|
case '<':
|
||||||
|
encoding = EncodingEnum.XML;
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
encoding = EncodingEnum.JSON;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (encoding == null) {
|
||||||
|
throw new InvalidRequestException("FHIR client can't determine resource encoding");
|
||||||
|
}
|
||||||
|
myResource = encoding.newParser(myContext).parseResource(myResourceBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseHttpClientInvocation invocation = CreateMethodBinding.createCreateInvocation(myResource,myResourceBody, myId, myContext);
|
||||||
|
|
||||||
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
|
||||||
|
final String resourceName = def.getName();
|
||||||
|
|
||||||
|
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
|
||||||
|
|
||||||
|
Map<String, List<String>> params = new HashMap<String, List<String>>();
|
||||||
|
return invoke(params, binding, invocation);
|
||||||
|
|
||||||
public ResourceListResponseHandler(Class<? extends IResource> theType) {
|
|
||||||
myType = theType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public ICreateTyped resource(IResource theResource) {
|
||||||
return new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources();
|
Validate.notNull(theResource, "Resource can not be null");
|
||||||
|
myResource = theResource;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ICreateTyped resource(String theResourceBody) {
|
||||||
|
Validate.notBlank(theResourceBody, "Body can not be null or blank");
|
||||||
|
myResourceBody = theResourceBody;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreateInternal withId(IdDt theId) {
|
||||||
|
myId = theId.getIdPart();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreateInternal withId(String theId) {
|
||||||
|
myId = theId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ForInternal extends BaseClientExecutable<IQuery, Bundle> implements IQuery {
|
private class ForInternal extends BaseClientExecutable<IQuery, Bundle> implements IQuery {
|
||||||
|
@ -496,10 +565,31 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped, Bundle> implements IGetPageTyped {
|
||||||
|
|
||||||
|
private String myUrl;
|
||||||
|
|
||||||
|
public GetPageInternal(String theUrl) {
|
||||||
|
myUrl = theUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bundle execute() {
|
||||||
|
|
||||||
|
BundleResponseHandler binding = new BundleResponseHandler(null);
|
||||||
|
HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myUrl);
|
||||||
|
|
||||||
|
Map<String, List<String>> params = null;
|
||||||
|
return invoke(params, binding, invocation);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private class GetTagsInternal extends BaseClientExecutable<IGetTags, TagList> implements IGetTags {
|
private class GetTagsInternal extends BaseClientExecutable<IGetTags, TagList> implements IGetTags {
|
||||||
|
|
||||||
private String myResourceName;
|
|
||||||
private String myId;
|
private String myId;
|
||||||
|
private String myResourceName;
|
||||||
private String myVersionId;
|
private String myVersionId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -537,14 +627,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setResourceClass(Class<? extends IResource> theClass) {
|
|
||||||
if (theClass != null) {
|
|
||||||
myResourceName = myContext.getResourceDefinition(theClass).getName();
|
|
||||||
} else {
|
|
||||||
myResourceName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IGetTags forResource(Class<? extends IResource> theClass, String theId) {
|
public IGetTags forResource(Class<? extends IResource> theClass, String theId) {
|
||||||
setResourceClass(theClass);
|
setResourceClass(theClass);
|
||||||
|
@ -560,6 +642,33 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setResourceClass(Class<? extends IResource> theClass) {
|
||||||
|
if (theClass != null) {
|
||||||
|
myResourceName = myContext.getResourceDefinition(theClass).getName();
|
||||||
|
} else {
|
||||||
|
myResourceName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class LoadPageInternal implements IGetPage {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IGetPageTyped next(Bundle theBundle) {
|
||||||
|
return new GetPageInternal(theBundle.getLinkNext().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IGetPageTyped previous(Bundle theBundle) {
|
||||||
|
return new GetPageInternal(theBundle.getLinkPrevious().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IGetPageTyped url(String thePageUrl) {
|
||||||
|
return new GetPageInternal(thePageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
|
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
|
||||||
|
@ -570,7 +679,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
|
||||||
|
BaseServerResponseException {
|
||||||
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
@ -595,10 +705,25 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ResourceListResponseHandler implements IClientResponseHandler<List<IResource>> {
|
||||||
|
|
||||||
|
private Class<? extends IResource> myType;
|
||||||
|
|
||||||
|
public ResourceListResponseHandler(Class<? extends IResource> theType) {
|
||||||
|
myType = theType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
|
||||||
|
BaseServerResponseException {
|
||||||
|
return new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final class ResourceResponseHandler<T extends IResource> implements IClientResponseHandler<T> {
|
private final class ResourceResponseHandler<T extends IResource> implements IClientResponseHandler<T> {
|
||||||
|
|
||||||
private Class<T> myType;
|
|
||||||
private IdDt myId;
|
private IdDt myId;
|
||||||
|
private Class<T> myType;
|
||||||
|
|
||||||
public ResourceResponseHandler(Class<T> theType, IdDt theId) {
|
public ResourceResponseHandler(Class<T> theType, IdDt theId) {
|
||||||
myType = theType;
|
myType = theType;
|
||||||
|
@ -666,7 +791,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
private final class TagListResponseHandler implements IClientResponseHandler<TagList> {
|
private final class TagListResponseHandler implements IClientResponseHandler<TagList> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
|
||||||
|
BaseServerResponseException {
|
||||||
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
|
||||||
if (respType == null) {
|
if (respType == null) {
|
||||||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||||
|
@ -676,59 +802,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGetPage loadPage() {
|
|
||||||
return new LoadPageInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class LoadPageInternal implements IGetPage {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGetPageTyped previous(Bundle theBundle) {
|
|
||||||
return new GetPageInternal(theBundle.getLinkPrevious().getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGetPageTyped next(Bundle theBundle) {
|
|
||||||
return new GetPageInternal(theBundle.getLinkNext().getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IGetPageTyped url(String thePageUrl) {
|
|
||||||
return new GetPageInternal(thePageUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class TransactionInternal implements ITransaction {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ITransactionTyped<List<IResource>> withResources(List<IResource> theResources) {
|
|
||||||
return new TransactionExecutable<List<IResource>>(theResources);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ITransactionTyped<Bundle> withBundle(Bundle theResources) {
|
|
||||||
return new TransactionExecutable<Bundle>(theResources);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T> implements ITransactionTyped<T> {
|
private final class TransactionExecutable<T> extends BaseClientExecutable<ITransactionTyped<T>, T> implements ITransactionTyped<T> {
|
||||||
|
|
||||||
private List<IResource> myResources;
|
|
||||||
private Bundle myBundle;
|
private Bundle myBundle;
|
||||||
|
private List<IResource> myResources;
|
||||||
public TransactionExecutable(List<IResource> theResources) {
|
|
||||||
myResources=theResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransactionExecutable(Bundle theResources) {
|
public TransactionExecutable(Bundle theResources) {
|
||||||
myBundle = theResources;
|
myBundle = theResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransactionExecutable(List<IResource> theResources) {
|
||||||
|
myResources = theResources;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public T execute() {
|
public T execute() {
|
||||||
|
@ -747,23 +833,16 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GetPageInternal extends BaseClientExecutable<IGetPageTyped, Bundle> implements IGetPageTyped {
|
private final class TransactionInternal implements ITransaction {
|
||||||
|
|
||||||
private String myUrl;
|
@Override
|
||||||
|
public ITransactionTyped<Bundle> withBundle(Bundle theResources) {
|
||||||
public GetPageInternal(String theUrl) {
|
return new TransactionExecutable<Bundle>(theResources);
|
||||||
myUrl = theUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle execute() {
|
public ITransactionTyped<List<IResource>> withResources(List<IResource> theResources) {
|
||||||
|
return new TransactionExecutable<List<IResource>>(theResources);
|
||||||
BundleResponseHandler binding = new BundleResponseHandler(null);
|
|
||||||
HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myUrl);
|
|
||||||
|
|
||||||
Map<String, List<String>> params = null;
|
|
||||||
return invoke(params, binding, invocation);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.apache.http.HttpRequestInterceptor;
|
||||||
|
import org.apache.http.auth.AuthState;
|
||||||
|
import org.apache.http.auth.Credentials;
|
||||||
|
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||||
|
import org.apache.http.client.protocol.HttpClientContext;
|
||||||
|
import org.apache.http.impl.auth.BasicScheme;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR Library
|
* HAPI FHIR Library
|
||||||
|
@ -20,25 +34,9 @@ package ca.uhn.fhir.rest.client;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
|
||||||
import org.apache.http.HttpRequest;
|
|
||||||
import org.apache.http.HttpRequestInterceptor;
|
|
||||||
import org.apache.http.auth.AuthState;
|
|
||||||
import org.apache.http.auth.Credentials;
|
|
||||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
|
||||||
import org.apache.http.client.protocol.HttpClientContext;
|
|
||||||
import org.apache.http.impl.auth.BasicScheme;
|
|
||||||
import org.apache.http.protocol.HttpContext;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP interceptor to be used for adding HTTP basic auth username/password tokens
|
* @deprecated Use {@link ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor} instead. Note that that class is a HAPI client interceptor instead of being a commons-httpclient interceptor, so you register it to your client instance once it's created using {@link IGenericClient#registerInterceptor(IClientInterceptor)} or {@link IBasicClient#registerInterceptor(IClientInterceptor)} instead
|
||||||
* to requests
|
|
||||||
* <p>
|
|
||||||
* See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#HTTP_Basic_Authorization">HAPI Documentation</a>
|
|
||||||
* for information on how to use this class.
|
|
||||||
* </p>
|
|
||||||
*/
|
*/
|
||||||
public class HttpBasicAuthInterceptor implements HttpRequestInterceptor {
|
public class HttpBasicAuthInterceptor implements HttpRequestInterceptor {
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,21 @@ package ca.uhn.fhir.rest.client;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
|
||||||
public interface IClientInterceptor {
|
public interface IClientInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired by the client just before invoking the HTTP client request
|
||||||
|
*/
|
||||||
void interceptRequest(HttpRequestBase theRequest);
|
void interceptRequest(HttpRequestBase theRequest);
|
||||||
|
|
||||||
void interceptResponse(HttpResponse theRequest);
|
/**
|
||||||
|
* Fired by the client upon receiving an HTTP response, prior to processing that response
|
||||||
|
*/
|
||||||
|
void interceptResponse(HttpResponse theResponse) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,24 @@ import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||||
|
import ca.uhn.fhir.rest.gclient.ICreate;
|
||||||
import ca.uhn.fhir.rest.gclient.IGetPage;
|
import ca.uhn.fhir.rest.gclient.IGetPage;
|
||||||
import ca.uhn.fhir.rest.gclient.IGetTags;
|
import ca.uhn.fhir.rest.gclient.IGetTags;
|
||||||
import ca.uhn.fhir.rest.gclient.ITransaction;
|
import ca.uhn.fhir.rest.gclient.ITransaction;
|
||||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||||
|
|
||||||
public interface IGenericClient {
|
public interface IGenericClient {
|
||||||
|
/**
|
||||||
|
* Register a new interceptor for this client. An interceptor can be used to add additional
|
||||||
|
* logging, or add security headers, or pre-process responses, etc.
|
||||||
|
*/
|
||||||
|
void registerInterceptor(IClientInterceptor theInterceptor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
|
||||||
|
*/
|
||||||
|
void unregisterInterceptor(IClientInterceptor theInterceptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves and returns the server conformance statement
|
* Retrieves and returns the server conformance statement
|
||||||
|
@ -243,4 +255,9 @@ public interface IGenericClient {
|
||||||
*/
|
*/
|
||||||
ITransaction transaction();
|
ITransaction transaction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fluent method for the "create" operation, which creates a new resource instance on the server
|
||||||
|
*/
|
||||||
|
ICreate create();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,11 @@ package ca.uhn.fhir.rest.client.api;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public interface IRestfulClient {
|
public interface IRestfulClient {
|
||||||
|
@ -64,6 +66,15 @@ public interface IRestfulClient {
|
||||||
*/
|
*/
|
||||||
String getServerBase();
|
String getServerBase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new interceptor for this client. An interceptor can be used to add additional
|
||||||
|
* logging, or add security headers, or pre-process responses, etc.
|
||||||
|
*/
|
||||||
|
void registerInterceptor(IClientInterceptor theInterceptor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
|
||||||
|
*/
|
||||||
|
void unregisterInterceptor(IClientInterceptor theInterceptor);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package ca.uhn.fhir.rest.client.interceptor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP interceptor to be used for adding HTTP basic auth username/password tokens
|
||||||
|
* to requests
|
||||||
|
* <p>
|
||||||
|
* See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#HTTP_Basic_Authorization">HAPI Documentation</a>
|
||||||
|
* for information on how to use this class.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class BasicAuthInterceptor implements IClientInterceptor {
|
||||||
|
|
||||||
|
private String myUsername;
|
||||||
|
private String myPassword;
|
||||||
|
|
||||||
|
public BasicAuthInterceptor(String theUsername, String thePassword) {
|
||||||
|
super();
|
||||||
|
myUsername = theUsername;
|
||||||
|
myPassword = thePassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptRequest(HttpRequestBase theRequest) {
|
||||||
|
String authorizationUnescaped = StringUtils.defaultString(myUsername) + ":" + StringUtils.defaultString(myPassword);
|
||||||
|
String encoded;
|
||||||
|
try {
|
||||||
|
encoded = Base64.encodeBase64String(authorizationUnescaped.getBytes("ISO-8859-1"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalErrorException("Could not find US-ASCII encoding. This shouldn't happen!");
|
||||||
|
}
|
||||||
|
theRequest.addHeader(Constants.HEADER_AUTHORIZATION, ("Basic " + encoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptResponse(HttpResponse theResponse) throws IOException {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package ca.uhn.fhir.rest.client.interceptor;
|
||||||
|
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client interceptor which simply captures request and response objects and stored them so that they can be inspected after a client
|
||||||
|
* call has returned
|
||||||
|
*/
|
||||||
|
public class CapturingInterceptor implements IClientInterceptor {
|
||||||
|
|
||||||
|
private HttpRequestBase myLastRequest;
|
||||||
|
private HttpResponse myLastResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the last request and response values
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
myLastRequest = null;
|
||||||
|
myLastResponse = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequestBase getLastRequest() {
|
||||||
|
return myLastRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpResponse getLastResponse() {
|
||||||
|
return myLastResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptRequest(HttpRequestBase theRequest) {
|
||||||
|
myLastRequest = theRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptResponse(HttpResponse theRequest) {
|
||||||
|
myLastResponse = theRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
package ca.uhn.fhir.rest.client.interceptor;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpEntityEnclosingRequest;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
import org.apache.http.entity.HttpEntityWrapper;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
|
public class LoggingInterceptor implements IClientInterceptor {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class);
|
||||||
|
private boolean myLogRequestSummary = true;
|
||||||
|
private boolean myLogResponseSummary = true;
|
||||||
|
private boolean myLogResponseBody = false;
|
||||||
|
private boolean myLogRequestBody = false;
|
||||||
|
private boolean myLogResponseHeaders=false;
|
||||||
|
private boolean myLogRequestHeaders=false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public LoggingInterceptor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param theVerbose If set to true, all logging is enabled
|
||||||
|
*/
|
||||||
|
public LoggingInterceptor(boolean theVerbose) {
|
||||||
|
if (theVerbose) {
|
||||||
|
setLogRequestBody(true);
|
||||||
|
setLogRequestSummary(true);
|
||||||
|
setLogResponseBody(true);
|
||||||
|
setLogResponseSummary(true);
|
||||||
|
setLogRequestHeaders(true);
|
||||||
|
setLogResponseHeaders(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptRequest(HttpRequestBase theRequest) {
|
||||||
|
if (myLogRequestSummary) {
|
||||||
|
ourLog.info("Client request: {}", theRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myLogRequestHeaders) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (int i = 0;i < theRequest.getAllHeaders().length;i++) {
|
||||||
|
Header next = theRequest.getAllHeaders()[i];
|
||||||
|
b.append(next.getName() + ": " + next.getValue());
|
||||||
|
if(i+1 < theRequest.getAllHeaders().length) {
|
||||||
|
b.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ourLog.info("Client request headers:\n{}", b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myLogRequestBody) {
|
||||||
|
if (theRequest instanceof HttpEntityEnclosingRequest) {
|
||||||
|
HttpEntity entity = ((HttpEntityEnclosingRequest) theRequest).getEntity();
|
||||||
|
if (entity.isRepeatable()) {
|
||||||
|
try {
|
||||||
|
String content = IOUtils.toString(entity.getContent());
|
||||||
|
ourLog.info("Client request body:\n{}", content);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
ourLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
ourLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptResponse(HttpResponse theResponse) throws IOException {
|
||||||
|
if (myLogResponseSummary) {
|
||||||
|
String message = "HTTP " + theResponse.getStatusLine().getStatusCode() + " " + theResponse.getStatusLine().getReasonPhrase();
|
||||||
|
ourLog.info("Client response: {}", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myLogRequestHeaders) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (int i = 0;i < theResponse.getAllHeaders().length;i++) {
|
||||||
|
Header next = theResponse.getAllHeaders()[i];
|
||||||
|
b.append(next.getName() + ": " + next.getValue());
|
||||||
|
if(i+1 < theResponse.getAllHeaders().length) {
|
||||||
|
b.append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ourLog.info("Client response headers:\n{}", b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myLogResponseBody) {
|
||||||
|
HttpEntity respEntity = theResponse.getEntity();
|
||||||
|
final byte[] bytes;
|
||||||
|
try {
|
||||||
|
bytes = IOUtils.toByteArray(respEntity.getContent());
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw new InternalErrorException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLog.info("Client response body:\n{}", new String(bytes));
|
||||||
|
|
||||||
|
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyEntityWrapper extends HttpEntityWrapper {
|
||||||
|
|
||||||
|
private byte[] myBytes;
|
||||||
|
|
||||||
|
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
|
||||||
|
super(theWrappedEntity);
|
||||||
|
myBytes = theBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getContent() throws IOException {
|
||||||
|
return new ByteArrayInputStream(myBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(OutputStream theOutstream) throws IOException {
|
||||||
|
theOutstream.write(myBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should a summary (one line) for each request be logged, containing the URL and other information
|
||||||
|
*/
|
||||||
|
public void setLogRequestSummary(boolean theValue) {
|
||||||
|
myLogRequestSummary = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should a summary (one line) for each request be logged, containing the URL and other information
|
||||||
|
*/
|
||||||
|
public void setLogResponseSummary(boolean theValue) {
|
||||||
|
myLogResponseSummary = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should headers for each request be logged, containing the URL and other information
|
||||||
|
*/
|
||||||
|
public void setLogRequestHeaders(boolean theValue) {
|
||||||
|
myLogRequestHeaders = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should headers for each request be logged, containing the URL and other information
|
||||||
|
*/
|
||||||
|
public void setLogResponseHeaders(boolean theValue) {
|
||||||
|
myLogResponseHeaders = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should a summary (one line) for each request be logged, containing the URL and other information
|
||||||
|
*/
|
||||||
|
public void setLogRequestBody(boolean theValue) {
|
||||||
|
myLogRequestBody = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should a summary (one line) for each request be logged, containing the URL and other information
|
||||||
|
*/
|
||||||
|
public void setLogResponseBody(boolean theValue) {
|
||||||
|
myLogResponseBody = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ca.uhn.fhir.rest.gclient;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
|
||||||
|
public interface ICreate {
|
||||||
|
|
||||||
|
ICreateTyped resource(IResource theResource);
|
||||||
|
|
||||||
|
ICreateTyped resource(String theResourceAsText);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package ca.uhn.fhir.rest.gclient;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
|
||||||
|
public interface ICreateTyped extends IClientExecutable<ICreateTyped, MethodOutcome> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you want the explicitly state an ID for your created resource, put that ID here. You generally do not
|
||||||
|
* need to invoke this method, so that the server will assign the ID itself.
|
||||||
|
*/
|
||||||
|
ICreateTyped withId(String theId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you want the explicitly state an ID for your created resource, put that ID here. You generally do not
|
||||||
|
* need to invoke this method, so that the server will assign the ID itself.
|
||||||
|
*/
|
||||||
|
ICreateTyped withId(IdDt theId);
|
||||||
|
|
||||||
|
}
|
|
@ -50,6 +50,7 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
|
||||||
private final TagList myTagList;
|
private final TagList myTagList;
|
||||||
private final List<IResource> myResources;
|
private final List<IResource> myResources;
|
||||||
private final Bundle myBundle;
|
private final Bundle myBundle;
|
||||||
|
private final String myContents;
|
||||||
|
|
||||||
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||||
super();
|
super();
|
||||||
|
@ -59,6 +60,7 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
|
||||||
myTagList = null;
|
myTagList = null;
|
||||||
myResources = null;
|
myResources = null;
|
||||||
myBundle = null;
|
myBundle = null;
|
||||||
|
myContents = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
|
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
|
||||||
|
@ -72,6 +74,7 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
|
||||||
myTagList = theTagList;
|
myTagList = theTagList;
|
||||||
myResources = null;
|
myResources = null;
|
||||||
myBundle = null;
|
myBundle = null;
|
||||||
|
myContents = null;
|
||||||
|
|
||||||
myUrlExtension = StringUtils.join(theUrlExtension, '/');
|
myUrlExtension = StringUtils.join(theUrlExtension, '/');
|
||||||
}
|
}
|
||||||
|
@ -83,6 +86,7 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
|
||||||
myUrlExtension = null;
|
myUrlExtension = null;
|
||||||
myResources = theResources;
|
myResources = theResources;
|
||||||
myBundle = null;
|
myBundle = null;
|
||||||
|
myContents = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseHttpClientInvocationWithContents(FhirContext theContext, Bundle theBundle) {
|
public BaseHttpClientInvocationWithContents(FhirContext theContext, Bundle theBundle) {
|
||||||
|
@ -92,8 +96,20 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
|
||||||
myUrlExtension = null;
|
myUrlExtension = null;
|
||||||
myResources = null;
|
myResources = null;
|
||||||
myBundle = theBundle;
|
myBundle = theBundle;
|
||||||
|
myContents = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, String theUrlExtension) {
|
||||||
|
myContext = theContext;
|
||||||
|
myResource = null;
|
||||||
|
myTagList = null;
|
||||||
|
myUrlExtension = theUrlExtension;
|
||||||
|
myResources = null;
|
||||||
|
myBundle = null;
|
||||||
|
myContents = theContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
|
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
|
@ -131,6 +147,8 @@ public abstract class BaseHttpClientInvocationWithContents extends BaseHttpClien
|
||||||
} else if (myResources != null) {
|
} else if (myResources != null) {
|
||||||
Bundle bundle = RestfulServer.createBundleFromResourceList(myContext, "", myResources, "", "", myResources.size());
|
Bundle bundle = RestfulServer.createBundleFromResourceList(myContext, "", myResources, "", "", myResources.size());
|
||||||
contents = parser.encodeBundleToString(bundle);
|
contents = parser.encodeBundleToString(bundle);
|
||||||
|
} else if (myContents != null) {
|
||||||
|
contents = myContents;
|
||||||
} else {
|
} else {
|
||||||
contents = parser.encodeResourceToString(myResource);
|
contents = parser.encodeResourceToString(myResource);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,13 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
@ -55,6 +58,18 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
||||||
return Collections.singleton(RequestType.POST);
|
return Collections.singleton(RequestType.POST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
|
||||||
|
IResource retVal = super.parseIncomingServerResource(theRequest);
|
||||||
|
|
||||||
|
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
||||||
|
retVal.setId(theRequest.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
||||||
FhirContext context = getContext();
|
FhirContext context = getContext();
|
||||||
|
@ -72,14 +87,26 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, FhirContext theContext) {
|
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, FhirContext theContext) {
|
||||||
|
return createCreateInvocation(theResource, null,null, theContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpPostClientInvocation createCreateInvocation(IResource theResource, String theResourceBody, String theId, FhirContext theContext) {
|
||||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
|
||||||
String resourceName = def.getName();
|
String resourceName = def.getName();
|
||||||
|
|
||||||
|
|
||||||
StringBuilder urlExtension = new StringBuilder();
|
StringBuilder urlExtension = new StringBuilder();
|
||||||
urlExtension.append(resourceName);
|
urlExtension.append(resourceName);
|
||||||
|
if (StringUtils.isNotBlank(theId)) {
|
||||||
|
urlExtension.append('/');
|
||||||
|
urlExtension.append(theId);
|
||||||
|
}
|
||||||
|
|
||||||
HttpPostClientInvocation retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
|
HttpPostClientInvocation retVal;
|
||||||
|
if (StringUtils.isBlank(theResourceBody)) {
|
||||||
|
retVal = new HttpPostClientInvocation(theContext, theResource, urlExtension.toString());
|
||||||
|
}else {
|
||||||
|
retVal = new HttpPostClientInvocation(theContext, theResourceBody, urlExtension.toString());
|
||||||
|
}
|
||||||
addTagsToPostOrPut(theResource, retVal);
|
addTagsToPostOrPut(theResource, retVal);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -51,6 +51,10 @@ public class HttpPostClientInvocation extends BaseHttpClientInvocationWithConten
|
||||||
super(theContext,theBundle);
|
super(theContext,theBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpPostClientInvocation(FhirContext theContext, String theContents, String theUrlExtension) {
|
||||||
|
super(theContext,theContents, theUrlExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpPost createRequest(String url, AbstractHttpEntity theEntity) {
|
protected HttpPost createRequest(String url, AbstractHttpEntity theEntity) {
|
||||||
|
|
|
@ -28,6 +28,10 @@ import java.util.Set;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
|
|
||||||
|
public static final String HEADER_CORS_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
|
||||||
|
public static final String HEADER_CORS_ALLOW_METHODS = "Access-Control-Allow-Methods";
|
||||||
|
public static final String HEADER_CORS_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
|
||||||
|
|
||||||
public static final String CHARSET_UTF_8 = "UTF-8";
|
public static final String CHARSET_UTF_8 = "UTF-8";
|
||||||
public static final String CT_ATOM_XML = "application/atom+xml";
|
public static final String CT_ATOM_XML = "application/atom+xml";
|
||||||
public static final String CT_FHIR_JSON = "application/json+fhir";
|
public static final String CT_FHIR_JSON = "application/json+fhir";
|
||||||
|
@ -88,6 +92,8 @@ public class Constants {
|
||||||
public static final String ENCODING_GZIP = "gzip";
|
public static final String ENCODING_GZIP = "gzip";
|
||||||
public static final String HEADER_LOCATION = "Location";
|
public static final String HEADER_LOCATION = "Location";
|
||||||
public static final String HEADER_LOCATION_LC = HEADER_LOCATION.toLowerCase();
|
public static final String HEADER_LOCATION_LC = HEADER_LOCATION.toLowerCase();
|
||||||
|
public static final String HEADERVALUE_CORS_ALLOW_METHODS_ALL = "GET, POST, PUT, DELETE";
|
||||||
|
public static final String HEADER_AUTHORIZATION = "Authorization";
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -79,6 +79,7 @@ import ca.uhn.fhir.util.VersionUtil;
|
||||||
|
|
||||||
public class RestfulServer extends HttpServlet {
|
public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@ -99,6 +100,20 @@ public class RestfulServer extends HttpServlet {
|
||||||
private String myServerVersion = VersionUtil.getVersion();
|
private String myServerVersion = VersionUtil.getVersion();
|
||||||
private boolean myStarted;
|
private boolean myStarted;
|
||||||
private boolean myUseBrowserFriendlyContentTypes;
|
private boolean myUseBrowserFriendlyContentTypes;
|
||||||
|
private String myCorsAllowDomain;
|
||||||
|
|
||||||
|
public String getCorsAllowDomain() {
|
||||||
|
return myCorsAllowDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to anything other than <code>null</code> (which is the default), the server will return CORS (Cross Origin Resource Sharing) headers with the given domain string.
|
||||||
|
* <p>
|
||||||
|
* A value of "*" indicates that the server allows access to all domains (which may be appropriate in development situations but is generally not appropriate in production)
|
||||||
|
*/
|
||||||
|
public void setCorsAllowDomain(String theCorsAllowDomain) {
|
||||||
|
myCorsAllowDomain = theCorsAllowDomain;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -120,6 +135,13 @@ public class RestfulServer extends HttpServlet {
|
||||||
*/
|
*/
|
||||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||||
theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
theHttpResponse.addHeader("X-Powered-By", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
||||||
|
|
||||||
|
if (isNotBlank(myCorsAllowDomain)) {
|
||||||
|
theHttpResponse.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, myCorsAllowDomain);
|
||||||
|
theHttpResponse.addHeader(Constants.HEADER_CORS_ALLOW_METHODS, Constants.HEADERVALUE_CORS_ALLOW_METHODS_ALL);
|
||||||
|
theHttpResponse.addHeader(Constants.HEADER_CORS_EXPOSE_HEADERS, Constants.HEADER_CONTENT_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
||||||
|
@ -352,7 +374,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
int start = Math.min(offsetI, resultList.size() - 1);
|
int start = Math.min(offsetI, resultList.size() - 1);
|
||||||
|
|
||||||
EncodingEnum responseEncoding = determineRequestEncoding(theRequest);
|
EncodingEnum responseEncoding = determineResponseEncoding(theRequest.getServletRequest());
|
||||||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||||
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
||||||
|
@ -418,7 +440,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
ResourceBinding resourceBinding = null;
|
ResourceBinding resourceBinding = null;
|
||||||
BaseMethodBinding<?> resourceMethod = null;
|
BaseMethodBinding<?> resourceMethod = null;
|
||||||
if ("metadata".equals(resourceName)) {
|
if ("metadata".equals(resourceName) || theRequestType == RequestType.OPTIONS) {
|
||||||
resourceMethod = myServerConformanceMethod;
|
resourceMethod = myServerConformanceMethod;
|
||||||
} else if (resourceName == null) {
|
} else if (resourceName == null) {
|
||||||
resourceBinding = myNullResourceBinding;
|
resourceBinding = myNullResourceBinding;
|
||||||
|
|
|
@ -3,10 +3,11 @@ package example;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.client.HttpBasicAuthInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
|
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public class ClientExamples {
|
public class ClientExamples {
|
||||||
|
@ -17,29 +18,50 @@ public class ClientExamples {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void createSecurity() {
|
public void createSecurity() {
|
||||||
{
|
|
||||||
//START SNIPPET: security
|
//START SNIPPET: security
|
||||||
// Create a context and get the client factory so it can be configured
|
// Create a context and get the client factory so it can be configured
|
||||||
FhirContext ctx = new FhirContext();
|
FhirContext ctx = new FhirContext();
|
||||||
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
|
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
|
||||||
|
|
||||||
// Create an HTTP Client Builder
|
//Create an HTTP basic auth interceptor
|
||||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
|
||||||
|
|
||||||
// This interceptor adds HTTP username/password to every request
|
|
||||||
String username = "foobar";
|
String username = "foobar";
|
||||||
String password = "boobear";
|
String password = "boobear";
|
||||||
builder.addInterceptorFirst(new HttpBasicAuthInterceptor(username, password));
|
BasicAuthInterceptor authInterceptor = new BasicAuthInterceptor(username, password);
|
||||||
|
|
||||||
// Use the new HTTP client builder
|
// Register the interceptor with your client (either style)
|
||||||
clientFactory.setHttpClient(builder.build());
|
|
||||||
|
|
||||||
// This factory is applied to both styles of client
|
|
||||||
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
|
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
|
||||||
|
annotationClient.registerInterceptor(authInterceptor);
|
||||||
|
|
||||||
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
|
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
|
||||||
|
annotationClient.registerInterceptor(authInterceptor);
|
||||||
//END SNIPPET: security
|
//END SNIPPET: security
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void createLogging() {
|
||||||
|
{
|
||||||
|
//START SNIPPET: logging
|
||||||
|
//Create a context and get the client factory so it can be configured
|
||||||
|
FhirContext ctx = new FhirContext();
|
||||||
|
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();
|
||||||
|
|
||||||
|
//Create a logging interceptor
|
||||||
|
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
|
||||||
|
|
||||||
|
// Optionally you may configure the interceptor (by default only summary info is logged)
|
||||||
|
loggingInterceptor.setLogRequestSummary(true);
|
||||||
|
loggingInterceptor.setLogRequestBody(true);
|
||||||
|
|
||||||
|
//Register the interceptor with your client (either style)
|
||||||
|
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
|
||||||
|
annotationClient.registerInterceptor(loggingInterceptor);
|
||||||
|
|
||||||
|
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
|
||||||
|
annotationClient.registerInterceptor(loggingInterceptor);
|
||||||
|
//END SNIPPET: logging
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************/
|
/******************************/
|
||||||
{
|
{
|
||||||
|
|
|
@ -165,6 +165,28 @@
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
|
<subsection name="Configuring Encoding (JSON/XML)">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Restful client interfaces that you create will also extend
|
||||||
|
the interface
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/rest/client/api/IRestfulClient.html">IRestfulClient</a>,
|
||||||
|
which comes with some helpful methods for configuring the way that
|
||||||
|
the client will interact with the server.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The following snippet shows how to configure the cliet to explicitly
|
||||||
|
request JSON or XML responses, and how to request "pretty printed" responses
|
||||||
|
on servers that support this (HAPI based servers currently).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="clientConfig" />
|
||||||
|
<param name="file" value="src/site/example/java/example/ClientExamples.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
<subsection name="A Complete Example">
|
<subsection name="A Complete Example">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -205,6 +227,21 @@
|
||||||
on the RestfulClientFactory.
|
on the RestfulClientFactory.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<subsection name="Interceptors">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Both generic clients and annotation-driven clients support
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/rest/client/IClientInterceptor.html">Client Interceptors</a>,
|
||||||
|
which may be registered in order to provide specific behaviour to each
|
||||||
|
client request.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following section shows some sample interceptors which may be used.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
<subsection name="HTTP Basic Authorization">
|
<subsection name="HTTP Basic Authorization">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -219,23 +256,15 @@
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
<subsection name="Configuring Encoding (JSON/XML)">
|
<subsection name="Logging Requests and Responses">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Restful client interfaces that you create will also extend
|
The following example shows how to configure your client to
|
||||||
the interface
|
use a specific username and password in every request.
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/client/api/IRestfulClient.html">IRestfulClient</a>,
|
|
||||||
which comes with some helpful methods for configuring the way that
|
|
||||||
the client will interact with the server.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The following snippet shows how to configure the cliet to explicitly
|
|
||||||
request JSON or XML responses, and how to request "pretty printed" responses
|
|
||||||
on servers that support this (HAPI based servers currently).
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<macro name="snippet">
|
<macro name="snippet">
|
||||||
<param name="id" value="clientConfig" />
|
<param name="id" value="logging" />
|
||||||
<param name="file" value="src/site/example/java/example/ClientExamples.java" />
|
<param name="file" value="src/site/example/java/example/ClientExamples.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.methods.HttpPut;
|
import org.apache.http.client.methods.HttpPut;
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
import org.apache.http.message.BasicHeader;
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.message.BasicHttpResponse;
|
||||||
import org.apache.http.message.BasicStatusLine;
|
import org.apache.http.message.BasicStatusLine;
|
||||||
import org.hamcrest.core.StringContains;
|
import org.hamcrest.core.StringContains;
|
||||||
import org.hamcrest.core.StringEndsWith;
|
import org.hamcrest.core.StringEndsWith;
|
||||||
|
@ -57,6 +58,8 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.param.CodingListParam;
|
import ca.uhn.fhir.rest.param.CodingListParam;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
||||||
|
@ -73,8 +76,6 @@ public class ClientTest {
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private HttpResponse httpResponse;
|
private HttpResponse httpResponse;
|
||||||
|
|
||||||
// atom-document-large.xml
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
ctx = new FhirContext(Patient.class, Conformance.class);
|
ctx = new FhirContext(Patient.class, Conformance.class);
|
||||||
|
@ -85,6 +86,36 @@ public class ClientTest {
|
||||||
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// atom-document-large.xml
|
||||||
|
|
||||||
|
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
|
@Test
|
||||||
public void testCreate() throws Exception {
|
public void testCreate() throws Exception {
|
||||||
|
|
||||||
|
@ -99,8 +130,13 @@ public class ClientTest {
|
||||||
when(httpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200"));
|
when(httpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200"));
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
|
CapturingInterceptor interceptor = new CapturingInterceptor();
|
||||||
|
client.registerInterceptor(interceptor);
|
||||||
|
|
||||||
MethodOutcome response = client.createPatient(patient);
|
MethodOutcome response = client.createPatient(patient);
|
||||||
|
|
||||||
|
assertEquals(interceptor.getLastRequest().getURI().toASCIIString(), "http://foo/Patient");
|
||||||
|
|
||||||
assertEquals(HttpPost.class, capt.getValue().getClass());
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
HttpPost post = (HttpPost) capt.getValue();
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||||
|
@ -129,8 +165,7 @@ public class ClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some servers (older ones?) return the resourcde you created instead of an
|
* Some servers (older ones?) return the resourcde you created instead of an OperationOutcome. We just need to ignore it.
|
||||||
* OperationOutcome. We just need to ignore it.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithResourceResponse() throws Exception {
|
public void testCreateWithResourceResponse() throws Exception {
|
||||||
|
@ -562,6 +597,29 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFailureInternalError() throws Exception {
|
||||||
|
|
||||||
|
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), 500, "INTERNAL"));
|
||||||
|
Header[] headers = new Header[1];
|
||||||
|
headers[0] = new BasicHeader(Constants.HEADER_LAST_MODIFIED, "2011-01-02T22:01:02");
|
||||||
|
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Internal Failure"), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
|
try {
|
||||||
|
client.getPatientById(new IdDt("111"));
|
||||||
|
fail();
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("INTERNAL"));
|
||||||
|
assertThat(e.getResponseBody(), containsString("Internal Failure"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadFailureNoCharset() throws Exception {
|
public void testReadFailureNoCharset() throws Exception {
|
||||||
|
|
||||||
|
@ -588,30 +646,6 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadFailureInternalError() throws Exception {
|
|
||||||
|
|
||||||
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), 500, "INTERNAL"));
|
|
||||||
Header[] headers = new Header[1];
|
|
||||||
headers[0] = new BasicHeader(Constants.HEADER_LAST_MODIFIED, "2011-01-02T22:01:02");
|
|
||||||
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
|
||||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT));
|
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Internal Failure"), Charset.forName("UTF-8")));
|
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
|
||||||
try {
|
|
||||||
client.getPatientById(new IdDt("111"));
|
|
||||||
fail();
|
|
||||||
} catch (InternalErrorException e) {
|
|
||||||
assertThat(e.getMessage(), containsString("INTERNAL"));
|
|
||||||
assertThat(e.getResponseBody(), containsString("Internal Failure"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadNoCharset() throws Exception {
|
public void testReadNoCharset() throws Exception {
|
||||||
|
|
||||||
|
@ -677,11 +711,14 @@ public class ClientTest {
|
||||||
String msg = getPatientFeedWithOneResult();
|
String msg = getPatientFeedWithOneResult();
|
||||||
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
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.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().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")));
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
// httpResponse = new BasicHttpResponse(statusline, catalog, locale)
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
List<Patient> response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
List<Patient> response = client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||||
|
|
||||||
|
@ -690,25 +727,6 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSearchByToken() 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")));
|
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
|
||||||
Patient response = client.findPatientByMrn(new IdentifierDt("urn:foo", "123"));
|
|
||||||
|
|
||||||
assertEquals("http://foo/Patient?identifier=urn%3Afoo%7C123", capt.getValue().getURI().toString());
|
|
||||||
assertEquals("PRP1660", response.getIdentifier().get(0).getValue().getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByQuantity() throws Exception {
|
public void testSearchByQuantity() throws Exception {
|
||||||
|
|
||||||
|
@ -728,6 +746,25 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchByToken() 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")));
|
||||||
|
|
||||||
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
|
Patient response = client.findPatientByMrn(new IdentifierDt("urn:foo", "123"));
|
||||||
|
|
||||||
|
assertEquals("http://foo/Patient?identifier=urn%3Afoo%7C123", capt.getValue().getURI().toString());
|
||||||
|
assertEquals("PRP1660", response.getIdentifier().get(0).getValue().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchComposite() throws Exception {
|
public void testSearchComposite() throws Exception {
|
||||||
|
|
||||||
|
@ -946,8 +983,7 @@ public class ClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a FHIR content type, but no content and make sure we handle this
|
* Return a FHIR content type, but no content and make sure we handle this without crashing
|
||||||
* without crashing
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWithEmptyResponse() throws Exception {
|
public void testUpdateWithEmptyResponse() throws Exception {
|
||||||
|
@ -1063,38 +1099,14 @@ 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) {
|
private Header[] toHeaderArray(String theName, String theValue) {
|
||||||
return new Header[] { new BasicHeader(theName, theValue) };
|
return new Header[] { new BasicHeader(theName, theValue) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface ClientWithoutAnnotation extends IBasicClient {
|
||||||
|
Patient read(@IdParam IdDt theId);
|
||||||
|
}
|
||||||
|
|
||||||
@ResourceDef(name = "Patient")
|
@ResourceDef(name = "Patient")
|
||||||
public static class CustomPatient extends Patient {
|
public static class CustomPatient extends Patient {
|
||||||
// nothing
|
// nothing
|
||||||
|
@ -1114,8 +1126,4 @@ public class ClientTest {
|
||||||
@Search()
|
@Search()
|
||||||
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringDt theString, @IncludeParam String theInclude);
|
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringDt theString, @IncludeParam String theInclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ClientWithoutAnnotation extends IBasicClient {
|
|
||||||
Patient read(@IdParam IdDt theId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import org.apache.http.Header;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.ProtocolVersion;
|
import org.apache.http.ProtocolVersion;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
import org.apache.http.message.BasicHeader;
|
import org.apache.http.message.BasicHeader;
|
||||||
import org.apache.http.message.BasicStatusLine;
|
import org.apache.http.message.BasicStatusLine;
|
||||||
|
@ -59,7 +60,7 @@ public class GenericClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithTag() throws Exception {
|
public void testCreateWithTagNonFluent() throws Exception {
|
||||||
|
|
||||||
Patient p1 = new Patient();
|
Patient p1 = new Patient();
|
||||||
p1.addIdentifier("foo:bar", "12345");
|
p1.addIdentifier("foo:bar", "12345");
|
||||||
|
@ -88,6 +89,52 @@ public class GenericClientTest {
|
||||||
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
|
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWithTag() throws Exception {
|
||||||
|
|
||||||
|
Patient p1 = new Patient();
|
||||||
|
p1.addIdentifier("foo:bar", "12345");
|
||||||
|
p1.addName().addFamily("Smith").addGiven("John");
|
||||||
|
TagList list = new TagList();
|
||||||
|
list.addTag("http://hl7.org/fhir/tag", "urn:happytag", "This is a happy resource");
|
||||||
|
ResourceMetadataKeyEnum.TAG_LIST.put(p1, list);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||||
|
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
|
||||||
|
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") });
|
||||||
|
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||||
|
|
||||||
|
MethodOutcome outcome = client.create().resource(p1).execute();
|
||||||
|
assertEquals("44", outcome.getId().getIdPart());
|
||||||
|
assertEquals("22", outcome.getId().getVersionIdPart());
|
||||||
|
|
||||||
|
assertEquals("http://example.com/fhir/Patient", capt.getValue().getURI().toString());
|
||||||
|
assertEquals("POST", capt.getValue().getMethod());
|
||||||
|
Header catH = capt.getValue().getFirstHeader("Category");
|
||||||
|
assertNotNull(Arrays.asList(capt.getValue().getAllHeaders()).toString(), catH);
|
||||||
|
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try fluent options
|
||||||
|
*/
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
client.create().resource(p1).withId("123").execute();
|
||||||
|
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toString());
|
||||||
|
|
||||||
|
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
|
||||||
|
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
client.create().resource(resourceText).withId("123").execute();
|
||||||
|
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(2).getURI().toString());
|
||||||
|
assertEquals(resourceText, IOUtils.toString(((HttpPost)capt.getAllValues().get(2)).getEntity().getContent()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getPatientFeedWithOneResult() {
|
private String getPatientFeedWithOneResult() {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
|
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class InterceptorTest {
|
||||||
|
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
private static FhirContext ourCtx;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLogger() throws Exception {
|
||||||
|
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||||
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
Patient patient = client.read(Patient.class, "1");
|
||||||
|
assertFalse(patient.getIdentifierFirstRep().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
DummyProvider patientProvider = new DummyProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
ourCtx = servlet.getFhirContext();
|
||||||
|
servlet.setResourceProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Read(version = true)
|
||||||
|
public Patient findPatient(@IdParam IdDt theId) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier(theId.getIdPart(), theId.getVersionIdPart());
|
||||||
|
patient.setId("Patient/1/_history/1");
|
||||||
|
return patient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
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.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.AdverseReaction;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.DiagnosticOrder;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class CreateTest {
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static DiagnosticReportProvider ourReportProvider;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreate() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||||
|
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());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateById() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/1234");
|
||||||
|
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());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/1234/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateJson() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("001");
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||||
|
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWithUnprocessableEntity() throws Exception {
|
||||||
|
|
||||||
|
DiagnosticReport report = new DiagnosticReport();
|
||||||
|
report.getIdentifier().setValue("001");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/DiagnosticReport");
|
||||||
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(report), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(422, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
OperationOutcome outcome = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, new StringReader(responseContent));
|
||||||
|
assertEquals("FOOBAR", outcome.getIssueFirstRep().getDetails().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
PatientProvider patientProvider = new PatientProvider();
|
||||||
|
|
||||||
|
ourReportProvider = new DiagnosticReportProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.setResourceProviders(patientProvider, ourReportProvider, new DummyAdverseReactionResourceProvider());
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DiagnosticReportProvider implements IResourceProvider {
|
||||||
|
private TagList myLastTags;
|
||||||
|
private IdDt myLastVersion;
|
||||||
|
|
||||||
|
public TagList getLastTags() {
|
||||||
|
return myLastTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return DiagnosticReport.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome createDiagnosticReport(@ResourceParam DiagnosticReport thePatient) {
|
||||||
|
OperationOutcome outcome = new OperationOutcome();
|
||||||
|
outcome.addIssue().setDetails("FOOBAR");
|
||||||
|
throw new UnprocessableEntityException(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyAdverseReactionResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* *********************
|
||||||
|
* NO NEW METHODS *********************
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome create(@ResourceParam AdverseReaction thePatient) {
|
||||||
|
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
||||||
|
IdDt version = new IdDt(thePatient.getIdentifier().get(1).getValue().getValue());
|
||||||
|
return new MethodOutcome(id, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search()
|
||||||
|
public Collection<AdverseReaction> getAllResources() {
|
||||||
|
ArrayList<AdverseReaction> retVal = new ArrayList<AdverseReaction>();
|
||||||
|
|
||||||
|
AdverseReaction ar1 = new AdverseReaction();
|
||||||
|
ar1.setId("1");
|
||||||
|
retVal.add(ar1);
|
||||||
|
|
||||||
|
AdverseReaction ar2 = new AdverseReaction();
|
||||||
|
ar2.setId("2");
|
||||||
|
retVal.add(ar2);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return AdverseReaction.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||||
|
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
||||||
|
if (thePatient.getId().isEmpty()==false) {
|
||||||
|
id=thePatient.getId();
|
||||||
|
}
|
||||||
|
return new MethodOutcome(id.withVersion("002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -54,33 +54,36 @@ public class PagingTest {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet(base + "/Patient?_format=xml&_pretty=true");
|
HttpGet httpGet = new HttpGet(base + "/Patient?_format=xml&_pretty=true");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||||
assertEquals(5, bundle.getEntries().size());
|
assertEquals(5, bundle.getEntries().size());
|
||||||
assertEquals("0", bundle.getEntries().get(0).getId().getIdPart());
|
assertEquals("0", bundle.getEntries().get(0).getId().getIdPart());
|
||||||
assertEquals("4", bundle.getEntries().get(4).getId().getIdPart());
|
assertEquals("4", bundle.getEntries().get(4).getId().getIdPart());
|
||||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=5&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkNext().getValue());
|
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=5&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkNext()
|
||||||
|
.getValue());
|
||||||
assertNull(bundle.getLinkPrevious().getValue());
|
assertNull(bundle.getLinkPrevious().getValue());
|
||||||
link = bundle.getLinkNext().getValue();
|
link = bundle.getLinkNext().getValue();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet(link);
|
HttpGet httpGet = new HttpGet(link.replace("=xml", "=json"));
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourContext.newJsonParser().parseBundle(responseContent);
|
||||||
assertEquals(5, bundle.getEntries().size());
|
assertEquals(5, bundle.getEntries().size());
|
||||||
assertEquals("5", bundle.getEntries().get(0).getId().getIdPart());
|
assertEquals("5", bundle.getEntries().get(0).getId().getIdPart());
|
||||||
assertEquals("9", bundle.getEntries().get(4).getId().getIdPart());
|
assertEquals("9", bundle.getEntries().get(4).getId().getIdPart());
|
||||||
assertNull(bundle.getLinkNext().getValue());
|
assertNull(bundle.getLinkNext().getValue());
|
||||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkPrevious().getValue());
|
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=5&_format=json&_pretty=true", bundle.getLinkPrevious()
|
||||||
|
.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchInexactOffset() throws Exception {
|
public void testSearchInexactOffset() throws Exception {
|
||||||
when(myPagingProvider.getDefaultPageSize()).thenReturn(5);
|
when(myPagingProvider.getDefaultPageSize()).thenReturn(5);
|
||||||
|
@ -92,7 +95,8 @@ public class PagingTest {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=8&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true");
|
HttpGet httpGet = new HttpGet(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=8&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||||
|
@ -100,7 +104,8 @@ public class PagingTest {
|
||||||
assertEquals("8", bundle.getEntries().get(0).getId().getIdPart());
|
assertEquals("8", bundle.getEntries().get(0).getId().getIdPart());
|
||||||
assertEquals("9", bundle.getEntries().get(1).getId().getIdPart());
|
assertEquals("9", bundle.getEntries().get(1).getId().getIdPart());
|
||||||
assertNull(bundle.getLinkNext().getValue());
|
assertNull(bundle.getLinkNext().getValue());
|
||||||
assertEquals(base + '?'+Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=3&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkPrevious().getValue());
|
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=3&" + Constants.PARAM_COUNT + "=5&_format=xml&_pretty=true", bundle.getLinkPrevious()
|
||||||
|
.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -117,7 +122,8 @@ public class PagingTest {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet(base + "/Patient?_count=2");
|
HttpGet httpGet = new HttpGet(base + "/Patient?_count=2");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||||
|
@ -131,7 +137,8 @@ public class PagingTest {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet(link);
|
HttpGet httpGet = new HttpGet(link);
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||||
|
@ -145,7 +152,6 @@ public class PagingTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
ourServer.stop();
|
ourServer.stop();
|
||||||
|
|
|
@ -109,71 +109,6 @@ public class ResfulServerMethodTest {
|
||||||
assertThat(responseContent, StringContains.containsString("AAAABBBB"));
|
assertThat(responseContent, StringContains.containsString("AAAABBBB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreate() throws Exception {
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addIdentifier().setValue("001");
|
|
||||||
patient.addIdentifier().setValue("002");
|
|
||||||
|
|
||||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
|
||||||
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());
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
|
||||||
|
|
||||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateJson() throws Exception {
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addIdentifier().setValue("001");
|
|
||||||
patient.addIdentifier().setValue("002");
|
|
||||||
|
|
||||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
|
||||||
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
|
||||||
|
|
||||||
HttpResponse status = ourClient.execute(httpPost);
|
|
||||||
|
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
|
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
|
||||||
|
|
||||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
|
||||||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateWithUnprocessableEntity() throws Exception {
|
|
||||||
|
|
||||||
DiagnosticReport report = new DiagnosticReport();
|
|
||||||
report.getIdentifier().setValue("001");
|
|
||||||
|
|
||||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/DiagnosticReport");
|
|
||||||
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(report), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
|
||||||
|
|
||||||
HttpResponse status = ourClient.execute(httpPost);
|
|
||||||
|
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
|
||||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
|
||||||
|
|
||||||
ourLog.info("Response was:\n{}", responseContent);
|
|
||||||
|
|
||||||
assertEquals(422, status.getStatusLine().getStatusCode());
|
|
||||||
|
|
||||||
OperationOutcome outcome = new FhirContext().newXmlParser().parseResource(OperationOutcome.class, new StringReader(responseContent));
|
|
||||||
assertEquals("FOOBAR", outcome.getIssueFirstRep().getDetails().getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDateRangeParam() throws Exception {
|
public void testDateRangeParam() throws Exception {
|
||||||
|
@ -1102,18 +1037,6 @@ public class ResfulServerMethodTest {
|
||||||
*/
|
*/
|
||||||
public static class DummyAdverseReactionResourceProvider implements IResourceProvider {
|
public static class DummyAdverseReactionResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
/*
|
|
||||||
* *********************
|
|
||||||
* NO NEW METHODS *********************
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Create()
|
|
||||||
public MethodOutcome create(@ResourceParam AdverseReaction thePatient) {
|
|
||||||
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
|
||||||
IdDt version = new IdDt(thePatient.getIdentifier().get(1).getValue().getValue());
|
|
||||||
return new MethodOutcome(id, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Search()
|
@Search()
|
||||||
public Collection<AdverseReaction> getAllResources() {
|
public Collection<AdverseReaction> getAllResources() {
|
||||||
ArrayList<AdverseReaction> retVal = new ArrayList<AdverseReaction>();
|
ArrayList<AdverseReaction> retVal = new ArrayList<AdverseReaction>();
|
||||||
|
@ -1148,12 +1071,6 @@ public class ResfulServerMethodTest {
|
||||||
throw new ResourceNotFoundException("AAAABBBB");
|
throw new ResourceNotFoundException("AAAABBBB");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Create()
|
|
||||||
public MethodOutcome createDiagnosticReport(@ResourceParam DiagnosticReport thePatient) {
|
|
||||||
OperationOutcome outcome = new OperationOutcome();
|
|
||||||
outcome.addIssue().setDetails("FOOBAR");
|
|
||||||
throw new UnprocessableEntityException(outcome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
public void deleteDiagnosticReport(@IdParam IdDt theId) {
|
public void deleteDiagnosticReport(@IdParam IdDt theId) {
|
||||||
|
@ -1176,12 +1093,6 @@ public class ResfulServerMethodTest {
|
||||||
*/
|
*/
|
||||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
@Create()
|
|
||||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
|
||||||
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
|
||||||
IdDt version = new IdDt(thePatient.getIdentifier().get(1).getValue().getValue());
|
|
||||||
return new MethodOutcome(id, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
public MethodOutcome deletePatient(@IdParam IdDt theId) {
|
public MethodOutcome deletePatient(@IdParam IdDt theId) {
|
||||||
|
|
|
@ -10,9 +10,11 @@ import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.Header;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpOptions;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
@ -114,6 +116,39 @@ public class ServerFeaturesTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCors() throws Exception {
|
||||||
|
servlet.setCorsAllowDomain("http://foo.com");
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
httpGet.addHeader("Accept", Constants.CT_FHIR_XML);
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
Header origin = status.getFirstHeader(Constants.HEADER_CORS_ALLOW_ORIGIN);
|
||||||
|
assertEquals("http://foo.com", origin.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptions() throws Exception {
|
||||||
|
servlet.setCorsAllowDomain("http://foo.com");
|
||||||
|
|
||||||
|
HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/");
|
||||||
|
httpGet.addHeader("Accept", Constants.CT_FHIR_XML);
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
Header origin = status.getFirstHeader(Constants.HEADER_CORS_ALLOW_ORIGIN);
|
||||||
|
assertEquals("http://foo.com", origin.getValue());
|
||||||
|
|
||||||
|
assertThat(responseContent,StringContains.containsString("<Conformance"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcceptHeaderWithMultiple() throws Exception {
|
public void testAcceptHeaderWithMultiple() throws Exception {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
|
@ -235,8 +270,10 @@ public class ServerFeaturesTest {
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
servlet.setServerAddressStrategy(new IncomingRequestAddressStrategy());
|
servlet.setServerAddressStrategy(new IncomingRequestAddressStrategy());
|
||||||
|
servlet.setCorsAllowDomain(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() throws Exception {
|
public static void beforeClass() throws Exception {
|
||||||
ourPort = RandomServerPortProvider.findFreePort();
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
|
|
@ -1053,6 +1053,11 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
|
|
||||||
if (entity.getId() == null) {
|
if (entity.getId() == null) {
|
||||||
myEntityManager.persist(entity);
|
myEntityManager.persist(entity);
|
||||||
|
|
||||||
|
if (entity.getForcedId() != null) {
|
||||||
|
myEntityManager.persist(entity.getForcedId());
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
entity = myEntityManager.merge(entity);
|
entity = myEntityManager.merge(entity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import javax.persistence.criteria.CriteriaQuery;
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -42,6 +43,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||||
import ca.uhn.fhir.jpa.entity.BaseTag;
|
import ca.uhn.fhir.jpa.entity.BaseTag;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ForcedId;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
||||||
|
@ -403,7 +405,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
|
||||||
|
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
String likeExpression = normalizeString(rawSearchTerm);
|
String likeExpression = normalizeString(rawSearchTerm);
|
||||||
|
@ -461,10 +464,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
}
|
}
|
||||||
|
|
||||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed ("
|
||||||
|
+ ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||||
}
|
}
|
||||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH
|
||||||
|
+ "): " + code);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
||||||
|
@ -533,9 +538,20 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
|
|
||||||
if (theResource.getId().isEmpty() == false) {
|
if (theResource.getId().isEmpty() == false) {
|
||||||
if (isValidPid(theResource.getId())) {
|
if (isValidPid(theResource.getId())) {
|
||||||
throw new UnprocessableEntityException("This server cannot create an entity with a numeric ID - Numeric IDs are server assigned");
|
throw new UnprocessableEntityException(
|
||||||
|
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
|
||||||
}
|
}
|
||||||
createForcedIdIfNeeded(entity, theResource.getId());
|
createForcedIdIfNeeded(entity, theResource.getId());
|
||||||
|
|
||||||
|
if (entity.getForcedId() != null) {
|
||||||
|
try {
|
||||||
|
translateForcedIdToPid(theResource.getId());
|
||||||
|
throw new UnprocessableEntityException("Can not create entity with ID[" + theResource.getId().getValue() + "], constraint violation occurred");
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
// good, this ID doesn't exist so we can create it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEntity(theResource, entity, false, false);
|
updateEntity(theResource, entity, false, false);
|
||||||
|
@ -608,7 +624,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
|
|
||||||
final T current = currentTmp;
|
final T current = currentTmp;
|
||||||
|
|
||||||
String querySring = "SELECT count(h) FROM ResourceHistoryTable h " + "WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE" + " AND h.myUpdated < :END" + (theSince != null ? " AND h.myUpdated >= :SINCE" : "");
|
String querySring = "SELECT count(h) FROM ResourceHistoryTable h " + "WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE" + " AND h.myUpdated < :END"
|
||||||
|
+ (theSince != null ? " AND h.myUpdated >= :SINCE" : "");
|
||||||
TypedQuery<Long> countQuery = myEntityManager.createQuery(querySring, Long.class);
|
TypedQuery<Long> countQuery = myEntityManager.createQuery(querySring, Long.class);
|
||||||
countQuery.setParameter("PID", theId.getIdPartAsLong());
|
countQuery.setParameter("PID", theId.getIdPartAsLong());
|
||||||
countQuery.setParameter("RESTYPE", resourceType);
|
countQuery.setParameter("RESTYPE", resourceType);
|
||||||
|
@ -646,8 +663,9 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
retVal.add(current);
|
retVal.add(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery("SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END " + (theSince != null ? " AND h.myUpdated >= :SINCE" : "")
|
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(
|
||||||
+ " ORDER BY h.myUpdated ASC", ResourceHistoryTable.class);
|
"SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END "
|
||||||
|
+ (theSince != null ? " AND h.myUpdated >= :SINCE" : "") + " ORDER BY h.myUpdated ASC", ResourceHistoryTable.class);
|
||||||
q.setParameter("PID", theId.getIdPartAsLong());
|
q.setParameter("PID", theId.getIdPartAsLong());
|
||||||
q.setParameter("RESTYPE", resourceType);
|
q.setParameter("RESTYPE", resourceType);
|
||||||
q.setParameter("END", end.getValue(), TemporalType.TIMESTAMP);
|
q.setParameter("END", end.getValue(), TemporalType.TIMESTAMP);
|
||||||
|
@ -716,7 +734,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
throw new ConfigurationException("Unknown search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "]");
|
throw new ConfigurationException("Unknown search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "]");
|
||||||
}
|
}
|
||||||
if (sp.getParamType() != SearchParamTypeEnum.TOKEN) {
|
if (sp.getParamType() != SearchParamTypeEnum.TOKEN) {
|
||||||
throw new ConfigurationException("Search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "] is not a token type, only token is supported");
|
throw new ConfigurationException("Search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName
|
||||||
|
+ "] is not a token type, only token is supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,7 +762,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
|
|
||||||
private void validateResourceType(BaseHasResource entity) {
|
private void validateResourceType(BaseHasResource entity) {
|
||||||
if (!myResourceName.equals(entity.getResourceType())) {
|
if (!myResourceName.equals(entity.getResourceType())) {
|
||||||
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type " + entity.getResourceType());
|
throw new ResourceNotFoundException("Resource with ID " + entity.getIdDt().getIdPart() + " exists but it is not of type " + myResourceName + ", found resource of type "
|
||||||
|
+ entity.getResourceType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,7 +787,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
if (theId.hasVersionIdPart()) {
|
if (theId.hasVersionIdPart()) {
|
||||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery("SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class);
|
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(
|
||||||
|
"SELECT t from ResourceHistoryTable t WHERE t.myResourceId = :RID AND t.myResourceType = :RTYP AND t.myResourceVersion = :RVER", ResourceHistoryTable.class);
|
||||||
q.setParameter("RID", theId.getIdPartAsLong());
|
q.setParameter("RID", theId.getIdPartAsLong());
|
||||||
q.setParameter("RTYP", myResourceName);
|
q.setParameter("RTYP", myResourceName);
|
||||||
q.setParameter("RVER", theId.getVersionIdPartAsLong());
|
q.setParameter("RVER", theId.getVersionIdPartAsLong());
|
||||||
|
@ -1048,8 +1069,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to
|
* If set, the given param will be treated as a secondary primary key, and multiple resources will not be able to share the same value.
|
||||||
* share the same value.
|
|
||||||
*/
|
*/
|
||||||
public void setSecondaryPrimaryKeyParamName(String theSecondaryPrimaryKeyParamName) {
|
public void setSecondaryPrimaryKeyParamName(String theSecondaryPrimaryKeyParamName) {
|
||||||
mySecondaryPrimaryKeyParamName = theSecondaryPrimaryKeyParamName;
|
mySecondaryPrimaryKeyParamName = theSecondaryPrimaryKeyParamName;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.EnumType;
|
import javax.persistence.EnumType;
|
||||||
import javax.persistence.Enumerated;
|
import javax.persistence.Enumerated;
|
||||||
|
@ -14,6 +15,8 @@ import javax.persistence.OneToOne;
|
||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.hibernate.validator.cfg.context.Cascadable;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,10 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
||||||
|
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||||
|
@ -36,6 +38,7 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.ExtensionConstants;
|
import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
import ca.uhn.test.jpasrv.ObservationResourceProvider;
|
||||||
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
|
||||||
|
@ -51,6 +54,7 @@ public class CompleteResourceProviderTest {
|
||||||
private static IFhirResourceDao<Questionnaire> questionnaireDao;
|
private static IFhirResourceDao<Questionnaire> questionnaireDao;
|
||||||
private static IGenericClient ourClient;
|
private static IGenericClient ourClient;
|
||||||
private static IFhirResourceDao<Observation> observationDao;
|
private static IFhirResourceDao<Observation> observationDao;
|
||||||
|
|
||||||
// private static JpaConformanceProvider ourConfProvider;
|
// private static JpaConformanceProvider ourConfProvider;
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
|
@ -92,6 +96,32 @@ public class CompleteResourceProviderTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWithId() {
|
||||||
|
Patient p1 = new Patient();
|
||||||
|
p1.addIdentifier().setSystem("urn:system").setValue("testCreateWithId01");
|
||||||
|
IdDt p1Id = ourClient.create().resource(p1).withId("testCreateWithId").execute().getId();
|
||||||
|
|
||||||
|
assertThat(p1Id.getValue(), containsString("Patient/testCreateWithId/_history"));
|
||||||
|
|
||||||
|
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testCreateWithId01")).encodedJson().prettyPrint().execute();
|
||||||
|
assertEquals(1, actual.size());
|
||||||
|
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ensure that trying to create the same ID again fails appropriately
|
||||||
|
*/
|
||||||
|
try {
|
||||||
|
ourClient.create().resource(p1).withId("testCreateWithId").execute().getId();
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
Bundle history = ourClient.history(null, (String)null, null, null);
|
||||||
|
assertEquals(p1Id.getIdPart(), history.getEntries().get(0).getId().getIdPart());
|
||||||
|
assertNotNull(history.getEntries().get(0).getResource());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByIdentifierWithoutSystem() {
|
public void testSearchByIdentifierWithoutSystem() {
|
||||||
|
@ -99,13 +129,13 @@ public class CompleteResourceProviderTest {
|
||||||
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
|
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
|
||||||
IdDt p1Id = ourClient.create(p1).getId();
|
IdDt p1Id = ourClient.create(p1).getId();
|
||||||
|
|
||||||
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute();
|
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
|
||||||
|
.execute();
|
||||||
assertEquals(1, actual.size());
|
assertEquals(1, actual.size());
|
||||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByResourceChain() {
|
public void testSearchByResourceChain() {
|
||||||
Organization o1 = new Organization();
|
Organization o1 = new Organization();
|
||||||
|
@ -137,7 +167,9 @@ public class CompleteResourceProviderTest {
|
||||||
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getId().getIdPart());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertUpdatesConformance() {
|
public void testInsertUpdatesConformance() {
|
||||||
// Conformance conf = ourConfProvider.getServerConformance();
|
// Conformance conf = ourConfProvider.getServerConformance();
|
||||||
|
@ -177,8 +209,6 @@ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger
|
||||||
// assertEquals(initial+1, number.getValueAsInteger());
|
// assertEquals(initial+1, number.getValueAsInteger());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInsertBadReference() {
|
public void testInsertBadReference() {
|
||||||
Patient p1 = new Patient();
|
Patient p1 = new Patient();
|
||||||
|
@ -195,7 +225,6 @@ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSaveAndRetrieveExistingNarrative() {
|
public void testSaveAndRetrieveExistingNarrative() {
|
||||||
Patient p1 = new Patient();
|
Patient p1 = new Patient();
|
||||||
|
@ -251,6 +280,8 @@ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger
|
||||||
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||||
|
|
||||||
IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class);
|
IFhirSystemDao systemDao = (IFhirSystemDao) ourAppCtx.getBean("mySystemDao", IFhirSystemDao.class);
|
||||||
|
JpaSystemProvider systemProv = new JpaSystemProvider(systemDao);
|
||||||
|
restServer.setPlainProviders(systemProv);
|
||||||
|
|
||||||
// ourConfProvider = new JpaConformanceProvider(restServer, systemDao, Collections.singletonList((IFhirResourceDao)patientDao));
|
// ourConfProvider = new JpaConformanceProvider(restServer, systemDao, Collections.singletonList((IFhirResourceDao)patientDao));
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,13 @@
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
||||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||||
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-tester-overlay?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base">
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module archiveName="hapi-fhir-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
|
<dependency-type>uses</dependency-type>
|
||||||
|
</dependent-module>
|
||||||
|
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-testpage-overlay?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
<dependency-type>consumes</dependency-type>
|
<dependency-type>consumes</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&excludes=META-INF/MANIFEST.MF">
|
||||||
|
|
|
@ -77,6 +77,11 @@
|
||||||
<artifactId>jcl-over-slf4j</artifactId>
|
<artifactId>jcl-over-slf4j</artifactId>
|
||||||
<version>${slf4j_version}</version>
|
<version>${slf4j_version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>${slf4j_version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
|
|
|
@ -9,7 +9,6 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.web.context.ContextLoaderListener;
|
import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||||
|
@ -69,6 +68,7 @@ public class TestRestfulServer extends RestfulServer {
|
||||||
setServerConformanceProvider(confProvider);
|
setServerConformanceProvider(confProvider);
|
||||||
|
|
||||||
setUseBrowserFriendlyContentTypes(true);
|
setUseBrowserFriendlyContentTypes(true);
|
||||||
|
setCorsAllowDomain("*");
|
||||||
|
|
||||||
String baseUrl = System.getProperty("fhir.baseurl");
|
String baseUrl = System.getProperty("fhir.baseurl");
|
||||||
if (StringUtils.isBlank(baseUrl)) {
|
if (StringUtils.isBlank(baseUrl)) {
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ca.uhn.fhirtest;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Profile;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Profile.ExtensionDefn;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.ExtensionContextEnum;
|
||||||
|
|
||||||
|
public class PopulateProfiles {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
Profile hapiExtensions = new Profile();
|
||||||
|
|
||||||
|
ExtensionDefn ext = hapiExtensions.addExtensionDefn();
|
||||||
|
ext.addContext("Conformance.rest.resource");
|
||||||
|
ext.getCode().setValue("resourceCount");
|
||||||
|
ext.getContextType().setValueAsEnum(ExtensionContextEnum.RESOURCE);
|
||||||
|
ext.getDisplay().setValue("Resource count on server");
|
||||||
|
ext.getDefinition().addType().setCode(DataTypeEnum.DECIMAL);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -53,6 +53,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.client.GenericClient;
|
import ca.uhn.fhir.rest.client.GenericClient;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.gclient.ICreateTyped;
|
||||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||||
import ca.uhn.fhir.rest.gclient.StringParam;
|
import ca.uhn.fhir.rest.gclient.StringParam;
|
||||||
|
@ -91,10 +92,6 @@ public class Controller {
|
||||||
return "about";
|
return "about";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String logPrefix(ModelMap theModel) {
|
|
||||||
return "[server=" + theModel.get("serverId") + "] - ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = { "/conformance" })
|
@RequestMapping(value = { "/conformance" })
|
||||||
public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||||
addCommonParams(theRequest, theModel);
|
addCommonParams(theRequest, theModel);
|
||||||
|
@ -160,18 +157,6 @@ public class Controller {
|
||||||
return "result";
|
return "result";
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
|
|
||||||
ResultType returnsResource;
|
|
||||||
returnsResource = ResultType.NONE;
|
|
||||||
ourLog.warn("Failed to invoke server", e);
|
|
||||||
|
|
||||||
if (theClient.getLastResponse() == null) {
|
|
||||||
theModel.put("errorMsg", "Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnsResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = { "/get-tags" })
|
@RequestMapping(value = { "/get-tags" })
|
||||||
public String actionGetTags(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
|
public String actionGetTags(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) {
|
||||||
addCommonParams(theRequest, theModel);
|
addCommonParams(theRequest, theModel);
|
||||||
|
@ -527,6 +512,12 @@ public class Controller {
|
||||||
return "result";
|
return "result";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = { "/update" })
|
||||||
|
public String actionUpdate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||||
|
doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "update");
|
||||||
|
return "result";
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(value = { "/validate" })
|
@RequestMapping(value = { "/validate" })
|
||||||
public String actionValidate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
public String actionValidate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||||
doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "validate");
|
doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "validate");
|
||||||
|
@ -614,13 +605,17 @@ public class Controller {
|
||||||
client.validate(resource);
|
client.validate(resource);
|
||||||
} else {
|
} else {
|
||||||
String id = theReq.getParameter("resource-create-id");
|
String id = theReq.getParameter("resource-create-id");
|
||||||
if (isNotBlank(id)) {
|
if ("update".equals(theMethod)) {
|
||||||
outcomeDescription = "Update Resource";
|
outcomeDescription = "Update Resource";
|
||||||
client.update(id, resource);
|
client.update(id, resource);
|
||||||
update = true;
|
update = true;
|
||||||
} else {
|
} else {
|
||||||
outcomeDescription = "Create Resource";
|
outcomeDescription = "Create Resource";
|
||||||
client.create(resource);
|
ICreateTyped create = client.create().resource(body);
|
||||||
|
if (isNotBlank(id)) {
|
||||||
|
create.withId(id);
|
||||||
|
}
|
||||||
|
create.execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -853,6 +848,18 @@ public class Controller {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ResultType handleClientException(GenericClient theClient, Exception e, ModelMap theModel) {
|
||||||
|
ResultType returnsResource;
|
||||||
|
returnsResource = ResultType.NONE;
|
||||||
|
ourLog.warn("Failed to invoke server", e);
|
||||||
|
|
||||||
|
if (theClient.getLastResponse() == null) {
|
||||||
|
theModel.put("errorMsg", "Error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnsResource;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonGenerator theClientCodeJsonWriter) {
|
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonGenerator theClientCodeJsonWriter) {
|
||||||
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
|
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
|
||||||
if (isBlank(nextName)) {
|
if (isBlank(nextName)) {
|
||||||
|
@ -970,6 +977,10 @@ public class Controller {
|
||||||
return conformance;
|
return conformance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String logPrefix(ModelMap theModel) {
|
||||||
|
return "[server=" + theModel.get("serverId") + "] - ";
|
||||||
|
}
|
||||||
|
|
||||||
private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) {
|
private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) {
|
||||||
try {
|
try {
|
||||||
IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody);
|
IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody);
|
||||||
|
|
|
@ -337,12 +337,11 @@
|
||||||
</div>
|
</div>
|
||||||
<br clear="all"/>
|
<br clear="all"/>
|
||||||
|
|
||||||
<!-- Create/Update -->
|
<!-- Create -->
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<b>Create/Update</b> an instance of the resource. If no ID is specified,
|
<b>Create</b> an instance of the resource. Generally you do not need to specify an ID
|
||||||
a new resource will be created. If an ID is specified, the existing
|
but you may force the server to use a specific ID by including one.
|
||||||
resource with that ID will be updated.
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid top-buffer">
|
<div class="row-fluid top-buffer">
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
|
@ -358,7 +357,7 @@
|
||||||
<div class="input-group-addon">
|
<div class="input-group-addon">
|
||||||
ID
|
ID
|
||||||
</div>
|
</div>
|
||||||
<input type="text" class="form-control" id="resource-create-id" placeholder="(add for update)" th:value="${updateResourceId}"/>
|
<input type="text" class="form-control" id="resource-create-id" placeholder="(optional)"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -370,37 +369,16 @@
|
||||||
<span class="loadingStar">*</span>
|
<span class="loadingStar">*</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea class="form-control" id="resource-create-body" style="white-space: nowrap; overflow: auto;" placeholder="(place resource body here)" rows="1">
|
<textarea class="form-control" id="resource-create-body" style="white-space: nowrap; overflow: auto;" placeholder="(place resource body here)" rows="1">
|
||||||
<th:block th:if="${updateResource} != null" th:text="${updateResource}"/>
|
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var buttonChanger = function() {
|
|
||||||
var val = $('#resource-create-id').val();
|
|
||||||
if (val != "") {
|
|
||||||
//$('#resource-create-btn').text("Update");
|
|
||||||
$("#resource-create-btn").fadeOut(function() {
|
|
||||||
$(this).html('<i class="fa fa-pencil"></i> Update').fadeIn();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$("#resource-create-btn").fadeOut(function() {
|
|
||||||
$(this).html('<i class="fa fa-send"></i> Create').fadeIn();
|
|
||||||
});
|
|
||||||
//$('#resource-create-btn').text("Create");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var textAreaChanger = function() {
|
var textAreaChanger = function() {
|
||||||
createBodyOriginalHeight = $('#resource-create-body').height();
|
createBodyOriginalHeight = $('#resource-create-body').height();
|
||||||
$('#resource-create-body').animate({height: "200px"}, 500);
|
$('#resource-create-body').animate({height: "200px"}, 500);
|
||||||
}
|
}
|
||||||
$('#resource-create-id').change(buttonChanger);
|
|
||||||
$('#resource-create-id').keyup(buttonChanger);
|
|
||||||
$('#resource-create-body').focus(textAreaChanger);
|
$('#resource-create-body').focus(textAreaChanger);
|
||||||
/*$('#resource-create-body').blur(
|
|
||||||
function() {
|
|
||||||
$('#resource-create-body').animate({height: "34px"}, 500);
|
|
||||||
});*/
|
|
||||||
$('#resource-create-btn').click(
|
$('#resource-create-btn').click(
|
||||||
function() {
|
function() {
|
||||||
var btn = $(this);
|
var btn = $(this);
|
||||||
|
@ -422,6 +400,72 @@
|
||||||
</div>
|
</div>
|
||||||
<br clear="all"/>
|
<br clear="all"/>
|
||||||
|
|
||||||
|
<!-- Update -->
|
||||||
|
|
||||||
|
<div class="row-fluid">
|
||||||
|
<b>Update</b> an existing instance of the resource by ID.
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid top-buffer">
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<button type="button" id="resource-update-btn"
|
||||||
|
data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
||||||
|
<i class="fa fa-send"></i>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class='col-sm-3'>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class='input-group date'>
|
||||||
|
<div class="input-group-addon">
|
||||||
|
ID
|
||||||
|
<span class="loadingStar">*</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" class="form-control" id="resource-update-id" placeholder="(resource ID)" th:value="${updateResourceId}"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='col-sm-7'>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class='input-group date'>
|
||||||
|
<div class="input-group-addon">
|
||||||
|
Contents
|
||||||
|
<span class="loadingStar">*</span>
|
||||||
|
</div>
|
||||||
|
<textarea class="form-control" id="resource-update-body" style="white-space: nowrap; overflow: auto;" placeholder="(place resource body here)" rows="1">
|
||||||
|
<th:block th:if="${updateResource} != null" th:text="${updateResource}"/>
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var textAreaChanger = function() {
|
||||||
|
updateBodyOriginalHeight = $('#resource-update-body').height();
|
||||||
|
$('#resource-update-body').animate({height: "200px"}, 500);
|
||||||
|
}
|
||||||
|
$('#resource-update-body').focus(textAreaChanger);
|
||||||
|
$('#resource-update-btn').click(
|
||||||
|
function() {
|
||||||
|
var btn = $(this);
|
||||||
|
btn.button('loading');
|
||||||
|
var id = $('#resource-update-id').val();
|
||||||
|
// Note we're using resource-create-id even though this is an update because
|
||||||
|
// the controller expects that...
|
||||||
|
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-create-id', value: id }));
|
||||||
|
var body = $('#resource-update-body').val();
|
||||||
|
btn.append($('<input />', { type: 'hidden', name: 'resource-create-body', value: body }));
|
||||||
|
$("#outerForm").attr("action", "update").submit();
|
||||||
|
});
|
||||||
|
$( document ).ready(function() {
|
||||||
|
if ($('#resource-update-id').val() != "") {
|
||||||
|
buttonChanger();
|
||||||
|
textAreaChanger();
|
||||||
|
$('#resource-update-body').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<br clear="all"/>
|
||||||
|
|
||||||
<!-- Validate -->
|
<!-- Validate -->
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -81,7 +81,7 @@
|
||||||
<maven_site_plugin_version>3.3</maven_site_plugin_version>
|
<maven_site_plugin_version>3.3</maven_site_plugin_version>
|
||||||
<mockito_version>1.9.5</mockito_version>
|
<mockito_version>1.9.5</mockito_version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<slf4j_version>1.7.2</slf4j_version>
|
<slf4j_version>1.7.7</slf4j_version>
|
||||||
<spring_version>4.0.1.RELEASE</spring_version>
|
<spring_version>4.0.1.RELEASE</spring_version>
|
||||||
<thymeleaf-version>2.1.3.RELEASE</thymeleaf-version>
|
<thymeleaf-version>2.1.3.RELEASE</thymeleaf-version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
Loading…
Reference in New Issue