From 897c65e8188ee0e2a611c5e9032e995ce9cd2b77 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 14 May 2017 06:39:23 -0400 Subject: [PATCH] CLI and HttpClient cleanup --- .../uhn/fhir/rest/client/GenericClient.java | 3 +- .../rest/client/RestfulClientFactory.java | 1 + .../rest/client/apache/ApacheHttpClient.java | 203 ++++++------------ .../apache/ApacheRestfulClientFactory.java | 45 ++-- .../rest/client/apache/BaseHttpClient.java | 111 ++++++++++ .../HardcodedServerAddressStrategy.java | 4 + .../java/ca/uhn/fhir/cli/BaseCommand.java | 2 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 11 + .../test/resources/david-bundle-error.json | 1 + .../test/resources/david-bundle-error2.json | 1 + .../java/ca/uhn/fhir/to/BaseController.java | 153 ++++++++----- src/changes/changes.xml | 3 + 12 files changed, 319 insertions(+), 219 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error.json create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error2.json diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index 53d61b986d3..4d306abe369 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -1029,7 +1029,8 @@ public class GenericClient extends BaseClient implements IGenericClient { @Override public Object execute() { ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass()); - HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext()); + FhirContext fhirContext = getFhirContext(); + HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(fhirContext); return super.invoke(null, binding, invocation); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index 00f023e3502..a1c787e47cb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -188,6 +188,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { public synchronized IGenericClient newGenericClient(String theServerBase) { validateConfigured(); IHttpClient httpClient = getHttpClient(theServerBase); + return new GenericClient(myContext, httpClient, theServerBase, this); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java index be1af48e8b0..1e11bddedff 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java @@ -21,38 +21,21 @@ package ca.uhn.fhir.rest.client.apache; */ import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpOptions; -import org.apache.http.client.methods.HttpPatch; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.*; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicNameValuePair; -import org.hl7.fhir.instance.model.api.IBaseBinary; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.RequestTypeEnum; -import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.client.api.Header; -import ca.uhn.fhir.rest.client.api.HttpClientUtil; -import ca.uhn.fhir.rest.client.api.IHttpClient; -import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.*; import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.EncodingEnum; -import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.util.CoverageIgnore; /** * A Http Client based on Apache. This is an adapter around the class @@ -60,127 +43,13 @@ import ca.uhn.fhir.util.CoverageIgnore; * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ -public class ApacheHttpClient implements IHttpClient { +public class ApacheHttpClient extends BaseHttpClient implements IHttpClient { private HttpClient myClient; - private List
myHeaders; - private StringBuilder myUrl; - private Map> myIfNoneExistParams; - private String myIfNoneExistString; - private RequestTypeEnum myRequestType; public ApacheHttpClient(HttpClient theClient, StringBuilder theUrl, Map> theIfNoneExistParams, String theIfNoneExistString, RequestTypeEnum theRequestType, List
theHeaders) { + super(theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, theHeaders); this.myClient = theClient; - this.myUrl = theUrl; - this.myIfNoneExistParams = theIfNoneExistParams; - this.myIfNoneExistString = theIfNoneExistString; - this.myRequestType = theRequestType; - this.myHeaders = theHeaders; - } - - @Override - public IHttpRequest createByteRequest(FhirContext theContext, String theContents, String theContentType, EncodingEnum theEncoding) { - /* - * We aren't using a StringEntity here because the constructors - * supported by Android aren't available in non-Android, and vice versa. - * Since we add the content type header manually, it makes no difference - * which one we use anyhow. - */ - ByteArrayEntity entity = new ByteArrayEntity(theContents.getBytes(Constants.CHARSET_UTF8)); - ApacheHttpRequest retVal = createHttpRequest(entity); - addHeadersToRequest(retVal, theEncoding, theContext); - retVal.addHeader(Constants.HEADER_CONTENT_TYPE, theContentType + Constants.HEADER_SUFFIX_CT_UTF_8); - return retVal; - } - - @Override - public IHttpRequest createParamRequest(FhirContext theContext, Map> theParams, EncodingEnum theEncoding) { - List parameters = new ArrayList(); - for (Entry> nextParam : theParams.entrySet()) { - List value = nextParam.getValue(); - for (String s : value) { - parameters.add(new BasicNameValuePair(nextParam.getKey(), s)); - } - } - UrlEncodedFormEntity entity = createFormEntity(parameters); - ApacheHttpRequest retVal = createHttpRequest(entity); - addHeadersToRequest(retVal, theEncoding, theContext); - return retVal; - } - - @CoverageIgnore - private UrlEncodedFormEntity createFormEntity(List parameters) { - try { - return new UrlEncodedFormEntity(parameters, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new InternalErrorException("Server does not support UTF-8 (should not happen)", e); - } - } - - @Override - public IHttpRequest createBinaryRequest(FhirContext theContext, IBaseBinary theBinary) { - /* - * Note: Be careful about changing which constructor we use for - * ByteArrayEntity, as Android's version of HTTPClient doesn't support - * the newer ones for whatever reason. - */ - ByteArrayEntity entity = new ByteArrayEntity(theBinary.getContent()); - ApacheHttpRequest retVal = createHttpRequest(entity); - addHeadersToRequest(retVal, null, theContext); - retVal.addHeader(Constants.HEADER_CONTENT_TYPE, theBinary.getContentType()); - return retVal; - } - - @Override - public IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding) { - ApacheHttpRequest retVal = createHttpRequest(null); - addHeadersToRequest(retVal, theEncoding, theContext); - return retVal; - } - - public void addHeadersToRequest(ApacheHttpRequest theHttpRequest, EncodingEnum theEncoding, FhirContext theContext) { - if (myHeaders != null) { - for (Header next : myHeaders) { - theHttpRequest.addHeader(next.getName(), next.getValue()); - } - } - - theHttpRequest.addHeader("User-Agent", HttpClientUtil.createUserAgentString(theContext, "apache")); - theHttpRequest.addHeader("Accept-Charset", "utf-8"); - theHttpRequest.addHeader("Accept-Encoding", "gzip"); - - RestfulServerUtils.addAcceptHeaderToRequest(theEncoding, theHttpRequest, theContext); - } - - private ApacheHttpRequest createHttpRequest(HttpEntity theEntity) { - HttpRequestBase request = constructRequestBase(theEntity); - ApacheHttpRequest result = new ApacheHttpRequest(myClient, request); - addHeaderIfNoneExist(result); - return result; - } - - private void addHeaderIfNoneExist(IHttpRequest result) { - if (myIfNoneExistParams != null) { - StringBuilder b = newHeaderBuilder(myUrl); - BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1); - result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString()); - } - - if (myIfNoneExistString != null) { - StringBuilder b = newHeaderBuilder(myUrl); - b.append(b.indexOf("?") == -1 ? '?' : '&'); - b.append(myIfNoneExistString.substring(myIfNoneExistString.indexOf('?') + 1)); - result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString()); - } - } - - private StringBuilder newHeaderBuilder(StringBuilder theUrlBase) { - StringBuilder b = new StringBuilder(); - b.append(theUrlBase); - if (theUrlBase.length() > 0 && theUrlBase.charAt(theUrlBase.length() - 1) == '/') { - b.deleteCharAt(b.length() - 1); - } - return b; } private HttpRequestBase constructRequestBase(HttpEntity theEntity) { @@ -208,4 +77,66 @@ public class ApacheHttpClient implements IHttpClient { } } + + private UrlEncodedFormEntity createFormEntity(List parameters) { + try { + return new UrlEncodedFormEntity(parameters, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new InternalErrorException("Server does not support UTF-8 (should not happen)", e); + } + } + + + protected IHttpRequest createHttpRequest() { + IHttpRequest retVal = createHttpRequest((HttpEntity)null); + return retVal; + } + + protected IHttpRequest createHttpRequest(byte[] content) { + /* + * Note: Be careful about changing which constructor we use for + * ByteArrayEntity, as Android's version of HTTPClient doesn't support + * the newer ones for whatever reason. + */ + ByteArrayEntity entity = new ByteArrayEntity(content); + IHttpRequest retVal = createHttpRequest(entity); + return retVal; + } + + private ApacheHttpRequest createHttpRequest(HttpEntity theEntity) { + HttpRequestBase request = constructRequestBase(theEntity); + ApacheHttpRequest result = new ApacheHttpRequest(myClient, request); + return result; + } + + protected IHttpRequest createHttpRequest(Map> theParams) { + List parameters = new ArrayList(); + for (Entry> nextParam : theParams.entrySet()) { + List value = nextParam.getValue(); + for (String s : value) { + parameters.add(new BasicNameValuePair(nextParam.getKey(), s)); + } + } + + UrlEncodedFormEntity entity = createFormEntity(parameters); + IHttpRequest retVal = createHttpRequest(entity); + return retVal; + } + + + protected IHttpRequest createHttpRequest(String theContents) { + /* + * We aren't using a StringEntity here because the constructors + * supported by Android aren't available in non-Android, and vice versa. + * Since we add the content type header manually, it makes no difference + * which one we use anyhow. + */ + ByteArrayEntity entity = new ByteArrayEntity(theContents.getBytes(Constants.CHARSET_UTF8)); + IHttpRequest retVal = createHttpRequest(entity); + return retVal; + } + + + + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java index 20337994173..b69f0c79d8f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheRestfulClientFactory.java @@ -31,10 +31,7 @@ import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.client.ProxyAuthenticationStrategy; +import org.apache.http.impl.client.*; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import ca.uhn.fhir.context.FhirContext; @@ -70,6 +67,18 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { super(theContext); } + @Override + protected ApacheHttpClient getHttpClient(String theServerBase) { + return new ApacheHttpClient(getNativeHttpClient(), new StringBuilder(theServerBase), null, null, null, null); + } + + @Override + public IHttpClient getHttpClient(StringBuilder theUrl, Map> theIfNoneExistParams, + String theIfNoneExistString, RequestTypeEnum theRequestType, List
theHeaders) { + return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, + theHeaders); + } + public synchronized HttpClient getNativeHttpClient() { if (myHttpClient == null) { @@ -105,19 +114,8 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { } @Override - public IHttpClient getHttpClient(StringBuilder theUrl, Map> theIfNoneExistParams, - String theIfNoneExistString, RequestTypeEnum theRequestType, List
theHeaders) { - return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType, - theHeaders); - } - - @Override - public void setProxy(String theHost, Integer thePort) { - if (theHost != null) { - myProxy = new HttpHost(theHost, thePort, "http"); - } else { - myProxy = null; - } + protected void resetHttpClient() { + this.myHttpClient = null; } /** @@ -130,13 +128,12 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory { } @Override - protected ApacheHttpClient getHttpClient(String theServerBase) { - return new ApacheHttpClient(getNativeHttpClient(), new StringBuilder(theServerBase), null, null, null, null); - } - - @Override - protected void resetHttpClient() { - this.myHttpClient = null; + public void setProxy(String theHost, Integer thePort) { + if (theHost != null) { + myProxy = new HttpHost(theHost, thePort, "http"); + } else { + myProxy = null; + } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java new file mode 100644 index 00000000000..2039a978bc3 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java @@ -0,0 +1,111 @@ +package ca.uhn.fhir.rest.client.apache; + +import java.util.List; +import java.util.Map; + +import org.hl7.fhir.instance.model.api.IBaseBinary; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; +import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.server.*; + +public abstract class BaseHttpClient implements IHttpClient { + + private final List
myHeaders; + private final Map> myIfNoneExistParams; + private final String myIfNoneExistString; + protected final RequestTypeEnum myRequestType; + protected final StringBuilder myUrl; + + /** + * Constructor + */ + public BaseHttpClient(StringBuilder theUrl, Map> theIfNoneExistParams, String theIfNoneExistString, RequestTypeEnum theRequestType, List
theHeaders) { + this.myUrl = theUrl; + this.myIfNoneExistParams = theIfNoneExistParams; + this.myIfNoneExistString = theIfNoneExistString; + this.myRequestType = theRequestType; + this.myHeaders = theHeaders; + } + + private void addHeaderIfNoneExist(IHttpRequest result) { + if (myIfNoneExistParams != null) { + StringBuilder b = newHeaderBuilder(myUrl); + BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1); + result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString()); + } + + if (myIfNoneExistString != null) { + StringBuilder b = newHeaderBuilder(myUrl); + b.append(b.indexOf("?") == -1 ? '?' : '&'); + b.append(myIfNoneExistString.substring(myIfNoneExistString.indexOf('?') + 1)); + result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString()); + } + } + + public void addHeadersToRequest(IHttpRequest theHttpRequest, EncodingEnum theEncoding, FhirContext theContext) { + if (myHeaders != null) { + for (Header next : myHeaders) { + theHttpRequest.addHeader(next.getName(), next.getValue()); + } + } + + theHttpRequest.addHeader("User-Agent", HttpClientUtil.createUserAgentString(theContext, "apache")); + theHttpRequest.addHeader("Accept-Charset", "utf-8"); + theHttpRequest.addHeader("Accept-Encoding", "gzip"); + + addHeaderIfNoneExist(theHttpRequest); + + RestfulServerUtils.addAcceptHeaderToRequest(theEncoding, theHttpRequest, theContext); + } + + @Override + public IHttpRequest createBinaryRequest(FhirContext theContext, IBaseBinary theBinary) { + byte[] content = theBinary.getContent(); + IHttpRequest retVal = createHttpRequest(content); + addHeadersToRequest(retVal, null, theContext); + retVal.addHeader(Constants.HEADER_CONTENT_TYPE, theBinary.getContentType()); + return retVal; + } + + @Override + public IHttpRequest createByteRequest(FhirContext theContext, String theContents, String theContentType, EncodingEnum theEncoding) { + IHttpRequest retVal = createHttpRequest(theContents); + addHeadersToRequest(retVal, theEncoding, theContext); + retVal.addHeader(Constants.HEADER_CONTENT_TYPE, theContentType + Constants.HEADER_SUFFIX_CT_UTF_8); + return retVal; + } + + @Override + public IHttpRequest createGetRequest(FhirContext theContext, EncodingEnum theEncoding) { + IHttpRequest retVal = createHttpRequest(); + addHeadersToRequest(retVal, theEncoding, theContext); + return retVal; + } + + protected abstract IHttpRequest createHttpRequest(); + + protected abstract IHttpRequest createHttpRequest(byte[] theContent); + + protected abstract IHttpRequest createHttpRequest(Map> theParams); + + protected abstract IHttpRequest createHttpRequest(String theContents); + + @Override + public IHttpRequest createParamRequest(FhirContext theContext, Map> theParams, EncodingEnum theEncoding) { + IHttpRequest retVal = createHttpRequest(theParams); + addHeadersToRequest(retVal, theEncoding, theContext); + return retVal; + } + + private StringBuilder newHeaderBuilder(StringBuilder theUrlBase) { + StringBuilder b = new StringBuilder(); + b.append(theUrlBase); + if (theUrlBase.length() > 0 && theUrlBase.charAt(theUrlBase.length() - 1) == '/') { + b.deleteCharAt(b.length() - 1); + } + return b; + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java index c746f6f0ba4..a6219bcd194 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java @@ -23,6 +23,8 @@ package ca.uhn.fhir.rest.server; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.Validate; + /** * Server address strategy which simply returns a hardcoded URL */ @@ -35,6 +37,7 @@ public class HardcodedServerAddressStrategy implements IServerAddressStrategy { } public HardcodedServerAddressStrategy(String theValue) { + Validate.notBlank(theValue, "theValue must not be null or empty"); myValue = theValue; } @@ -43,6 +46,7 @@ public class HardcodedServerAddressStrategy implements IServerAddressStrategy { } public void setValue(String theValue) { + Validate.notBlank(theValue, "theValue must not be null or empty"); myValue = theValue; } diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/BaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/BaseCommand.java index 28bff9a7840..dcd6f3af9dd 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/BaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/BaseCommand.java @@ -11,7 +11,7 @@ import ca.uhn.fhir.rest.client.IGenericClient; public abstract class BaseCommand implements Comparable { - private static final String SPEC_DEFAULT_VERSION = "dstu2"; + private static final String SPEC_DEFAULT_VERSION = "dstu3"; private FhirContext myFhirCtx; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index 296ef426225..2d3162df211 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -95,6 +95,17 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus()); } + @Test + public void testTransaction1() throws IOException { + String inputBundleString = loadClasspath("/david-bundle-error.json"); + Bundle bundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputBundleString); + Bundle resp = mySystemDao.transaction(mySrd, bundle); + + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); + + assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus()); + } + @SuppressWarnings("unchecked") private T find(Bundle theBundle, Class theType, int theIndex) { int count = 0; diff --git a/hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error.json b/hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error.json new file mode 100644 index 00000000000..3e06b307cdb --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error.json @@ -0,0 +1 @@ +{"resourceType":"Bundle","type":"transaction","entry":[{"resource":{"resourceType":"Patient","text":{"status":"generated","div":"
John Doe
"},"id":"cf-1494404982775","name":[{"given":["John","I"],"family":"Doe"}]},"request":{"method":"PUT","url":"Patient/cf-1494404982775"}},{"resource":{"resourceType":"Procedure","text":{"status":"generated","div":""},"id":"cf-1494404998796","subject":{"reference":"Patient/cf-1494404982775"},"status":"in-progress","code":{"coding":[{"system":"http://snomed.info/sct","code":"80146002","display":"Appendectomy (procedure)"}]}},"request":{"method":"PUT","url":"Procedure/cf-1494404998796"}}]} diff --git a/hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error2.json b/hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error2.json new file mode 100644 index 00000000000..1278ab3460f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/david-bundle-error2.json @@ -0,0 +1 @@ +{"resourceType":"Bundle","type":"transaction","entry":[{"resource":{"resourceType":"List","title":"Problems List","entry":[{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond0"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond1"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond2"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond3"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond4"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond5"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond6"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond7"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond8"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond9"}},{"date":"2017-05-11T10:01:09+02:00","item":{"reference":"Condition/cond10"}}],"date":"2017-05-11T10:01:09+02:00","subject":{"reference":"Patient/86938"},"status":"current","mode":"snapshot","code":{"coding":[{"code":"problems","system":"http://hl7.org/fhir/list-example-use-codes"}]}},"request":{"method":"POST","url":"List/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Asthma
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"371923033","system":"http://snomed.info/ct","display":"mild to moderate"}],"text":"mild to moderate"},"code":{"coding":[{"code":"195917001","system":"http://snomed.info/ct","display":"asthma"}],"text":"asthma"},"onsetAge":{"value":"14","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond0"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Diabetes
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"6736007","system":"http://snomed.info/ct","display":"moderate"}],"text":"moderate"},"code":{"coding":[{"code":"73211009","system":"http://snomed.info/ct","display":"diabetes"}],"text":"diabetes"},"onsetAge":{"value":"60","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond1"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Hypertension
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"255604002","system":"http://snomed.info/ct","display":"mild"}],"text":"Mild"},"code":{"coding":[{"code":"38341003","system":"http://snomed.info/ct","display":"hypertension"}],"text":"hypertension"},"onsetAge":{"value":"68","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond2"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Depression
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"6736007","system":"http://snomed.info/ct","display":"moderate"}],"text":"Moderate"},"code":{"coding":[{"code":"35489007","system":"http://snomed.info/ct","display":"depression"}],"text":"depression"},"onsetAge":{"value":"74","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond3"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Neuropathic pain
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"6736007","system":"http://snomed.info/ct","display":"moderate"}],"text":"moderate"},"code":{"coding":[{"code":"247389009","system":"http://snomed.info/ct","display":"neuropathic pain"}],"text":"neuropathic pain"},"onsetAge":{"value":"75","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond4"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Rheumatoid Arthritis
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"6736007","system":"http://snomed.info/ct","display":"moderate"}],"text":"Moderate"},"code":{"coding":[{"code":"69896004","system":"http://snomed.info/ct","display":"rheumatoid arthritis"}],"text":"rheumatoid arthritis - both hands"},"bodySite":[{"coding":[{"code":"12861001","system":"http://snomed.info/ct","display":"both hands"}],"text":"both hands"}],"evidence":[{"code":{"coding":[{"code":"57676002","system":"http://snomed.info/ct","display":"Painful Joint"}],"text":"Painful joint"}}],"onsetAge":{"value":"72","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond5"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Rheumatoid arthritis - left elbow
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"371923033","system":"http://snomed.info/ct","display":"mild to moderate"}],"text":"Diagnosis"},"code":{"coding":[{"code":"69896004","system":"http://snomed.info/ct","display":"rheumatoid arthritis"}],"text":"rheumatoid arthritis - left elbow"},"Site":[{"coding":[{"code":"1927002","system":"http://snomed.info/ct","display":"left elbow"}],"text":"left elbow"}],"evidence":[{"code":{"coding":[{"code":"57676002","system":"http://snomed.info/ct","display":"Painful Joint"}],"text":"Painful joint"}}],"onsetAge":{"value":"74","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond6"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
GERD
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"371923033","system":"http://snomed.info/ct","display":"moderate"}],"text":"Moderate"},"code":{"coding":[{"code":"235595009","system":"http://snomed.info/ct","display":"GERD"}],"text":"GERD"},"onsetAge":{"value":"66","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond7"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Hypercholesterolaemia
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"6736007","system":"http://snomed.info/ct","display":"moderate"}],"text":"Moderate"},"code":{"coding":[{"code":"13644009","system":"http://snomed.info/ct","display":"high cholesterol"}],"text":"high cholesterol"},"onsetAge":{"value":"68","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond8"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Onychomycosis
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"24484000","system":"http://snomed.info/ct","display":"severe"}],"text":"severe"},"code":{"coding":[{"code":"414941998","system":"http://snomed.info/ct","display":"onychomycosis"}],"text":"onychomycosis"},"onsetAge":{"value":"77","code":"a","system":"http://unitsofmeasure.org"},"subject":{"reference":"Patient/86938"},"id":"cond9"},"request":{"method":"POST","url":"Condition/"}},{"resource":{"resourceType":"Condition","text":{"status":"generated","div":"
Angina
"},"clinicalStatus":"active","verificationStatus":"confirmed","category":{"coding":[{"code":"diagnosis","system":"http://hl7.org/fhir/condition-category","display":"diagnosis"},{"code":"14657009","system":"http://snomed.info/sct","display":"Diagnosis"}],"text":"Diagnosis"},"severity":{"coding":[{"code":"371923033","system":"http://snomed.info/ct","display":"moderate"}],"text":"Moderate"},"code":{"coding":[{"code":"194818000","system":"http://snomed.info/ct","display":"angina"}],"text":"angina"},"onsetAge":{"value":"76","code":"a","system":"http://unitsofmeasure.org"},"evidence":[{"code":{"coding":[{"code":"29857009","system":"http://snomed.info/ct","display":"Chest Pain"}],"text":"Chest Pain"}}],"subject":{"reference":"Patient/86938"},"id":"cond10"},"request":{"method":"POST","url":"Condition/"}}]} diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java index 0dc74dd9e46..0f6537f7d01 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java @@ -39,6 +39,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ui.ModelMap; import org.thymeleaf.TemplateEngine; +import com.google.common.base.Charsets; + import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; @@ -54,6 +56,7 @@ import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest; import ca.uhn.fhir.rest.client.apache.ApacheHttpResponse; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.to.model.HomeRequest; @@ -286,7 +289,7 @@ public class BaseController { FhirVersionEnum version = theRequest.getFhirVersion(myConfig); FhirContext retVal = myContexts.get(version); if (retVal == null) { - retVal = new FhirContext(version); + retVal = newContext(version); myContexts.put(version, retVal); } return retVal; @@ -513,6 +516,12 @@ public class BaseController { return "[server=" + theModel.get("serverId") + "] - "; } + protected FhirContext newContext(FhirVersionEnum version) { + FhirContext retVal; + retVal = new FhirContext(version); + return retVal; + } + private String parseNarrative(HomeRequest theRequest, EncodingEnum theCtEnum, String theResultBody) { try { IBaseResource par = theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody); @@ -563,23 +572,51 @@ public class BaseController { protected void processAndAddLastClientInvocation(GenericClient theClient, ResultType theResultType, ModelMap theModelMap, long theLatency, String outcomeDescription, CaptureInterceptor theInterceptor, HomeRequest theRequest) { try { - ApacheHttpRequest lastRequest = theInterceptor.getLastRequest(); - HttpResponse lastResponse = theInterceptor.getLastResponse(); - String requestBody = null; - String requestUrl = lastRequest != null ? lastRequest.getApacheRequest().getURI().toASCIIString() : null; - String action = lastRequest != null ? lastRequest.getApacheRequest().getMethod() : null; - String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null; - String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody()); +// ApacheHttpRequest lastRequest = theInterceptor.getLastRequest(); +// HttpResponse lastResponse = theInterceptor.getLastResponse(); +// String requestBody = null; +// String requestUrl = lastRequest != null ? lastRequest.getApacheRequest().getURI().toASCIIString() : null; +// String action = lastRequest != null ? lastRequest.getApacheRequest().getMethod() : null; +// String resultStatus = lastResponse != null ? lastResponse.getStatusLine().toString() : null; +// String resultBody = StringUtils.defaultString(theInterceptor.getLastResponseBody()); +// +// if (lastRequest instanceof HttpEntityEnclosingRequest) { +// HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity(); +// if (entity.isRepeatable()) { +// requestBody = IOUtils.toString(entity.getContent()); +// } +// } +// +// ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null; +// String mimeType = ct != null ? ct.getMimeType() : null; - if (lastRequest instanceof HttpEntityEnclosingRequest) { - HttpEntity entity = ((HttpEntityEnclosingRequest) lastRequest).getEntity(); - if (entity.isRepeatable()) { - requestBody = IOUtils.toString(entity.getContent()); + + IHttpRequest lastRequest = theInterceptor.getLastRequest(); + IHttpResponse lastResponse = theInterceptor.getLastResponse(); + String requestBody = null; + String requestUrl = null; + String action = null; + String resultStatus = null; + String resultBody = null; + String mimeType = null; + ContentType ct = null; + if (lastRequest != null) { + requestBody = lastRequest.getRequestBodyFromStream(); + requestUrl = lastRequest.getUri(); + action = lastRequest.getHttpVerbName(); + } + if (lastResponse != null) { + resultStatus = lastResponse.getStatus() + ' ' + lastResponse.getStatusInfo(); + lastResponse.bufferEntity(); + resultBody = IOUtils.toString(lastResponse.readEntity(), Charsets.UTF_8); + + List ctStrings = lastResponse.getAllHeaders().get(Constants.HEADER_CONTENT_TYPE); + if (ctStrings != null && ctStrings.isEmpty() == false) { + ct = ContentType.parse(ctStrings.get(0)); + mimeType = ct.getMimeType(); } } - ContentType ct = lastResponse != null ? ContentType.get(lastResponse.getEntity()) : null; - String mimeType = ct != null ? ct.getMimeType() : null; EncodingEnum ctEnum = EncodingEnum.forContentType(mimeType); String narrativeString = ""; @@ -658,67 +695,69 @@ public class BaseController { public static class CaptureInterceptor implements IClientInterceptor { - private ApacheHttpRequest myLastRequest; - private HttpResponse myLastResponse; - private String myResponseBody; + private IHttpRequest myLastRequest; + private IHttpResponse myLastResponse; +// private String myResponseBody; - public ApacheHttpRequest getLastRequest() { + public IHttpRequest getLastRequest() { return myLastRequest; } - public HttpResponse getLastResponse() { + public IHttpResponse getLastResponse() { return myLastResponse; } - public String getLastResponseBody() { - return myResponseBody; - } +// public String getLastResponseBody() { +// return myResponseBody; +// } @Override public void interceptRequest(IHttpRequest theRequest) { assert myLastRequest == null; - myLastRequest = (ApacheHttpRequest) theRequest; + + myLastRequest = theRequest; } @Override public void interceptResponse(IHttpResponse theResponse) throws IOException { assert myLastResponse == null; - myLastResponse = ((ApacheHttpResponse) theResponse).getResponse(); - - HttpEntity respEntity = myLastResponse.getEntity(); - if (respEntity != null) { - final byte[] bytes; - try { - bytes = IOUtils.toByteArray(respEntity.getContent()); - } catch (IllegalStateException e) { - throw new InternalErrorException(e); - } - - myResponseBody = new String(bytes, "UTF-8"); - myLastResponse.setEntity(new MyEntityWrapper(respEntity, bytes)); - } + myLastResponse = theResponse; +// myLastResponse = ((ApacheHttpResponse) theResponse).getResponse(); +// +// HttpEntity respEntity = myLastResponse.getEntity(); +// if (respEntity != null) { +// final byte[] bytes; +// try { +// bytes = IOUtils.toByteArray(respEntity.getContent()); +// } catch (IllegalStateException e) { +// throw new InternalErrorException(e); +// } +// +// myResponseBody = new String(bytes, "UTF-8"); +// myLastResponse.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); - } - - } +// 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); +// } +// +// } } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b63fc1475c8..adb0879ca84 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -75,6 +75,9 @@ could cause you to have old codes that no longer exist in your database. This fix prevents these from blocking you from accesing those resources. + + CLI now defaults to DSTU3 mode if no FHIR version is specified +