ResourceReferenceDt#loadResource(IRestfulClient) did not

use the client's read functionality, so it did not
				            handle JSON responses or use interceptors. Thanks to
								            JT for reporting!
This commit is contained in:
jamesagnew 2015-10-23 08:21:25 -04:00
parent e93f7542a5
commit eea406e10e
7 changed files with 356 additions and 133 deletions

View File

@ -1,48 +1,23 @@
package ca.uhn.fhir.model.base.composite;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2015 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 static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.IOException;
import java.io.Reader;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.BaseIdentifiableElement;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement implements IBaseDatatype, IBaseReference {
private static final long serialVersionUID = 1L;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReferenceDt.class);
private IBaseResource myResource;
@ -95,42 +70,27 @@ public abstract class BaseResourceReferenceDt extends BaseIdentifiableElement im
* HTTP client to retrieve the resource unless it has already been loaded, or was a contained resource in which case
* it is simply returned.
*/
public IBaseResource loadResource(IRestfulClient theClient) throws IOException {
public IBaseResource loadResource(IRestfulClient theClient) {
if (myResource != null) {
return myResource;
}
IdDt resourceId = getReference();
if (resourceId == null) {
if (resourceId == null || isBlank(resourceId.getValue())) {
throw new IllegalStateException("Reference has no resource ID defined");
}
if (isBlank(resourceId.getBaseUrl()) || isBlank(resourceId.getResourceType())) {
throw new IllegalStateException("Reference is not complete (must be in the form [baseUrl]/[resource type]/[resource ID]) - Reference is: " + resourceId.getValue());
}
String resourceUrl = resourceId.getValue();
ourLog.debug("Loading resource at URL: {}", resourceUrl);
HttpClient httpClient = theClient.getHttpClient();
FhirContext context = theClient.getFhirContext();
if (!resourceUrl.startsWith("http")) {
resourceUrl = theClient.getServerBase() + resourceUrl;
}
HttpGet get = new HttpGet(resourceUrl);
HttpResponse response = httpClient.execute(get);
try {
// TODO: choose appropriate parser based on response CT
IParser parser = context.newXmlParser();
Reader responseReader = BaseClient.createReaderFromResponse(response);
myResource = parser.parseResource(responseReader);
} finally {
if (response instanceof CloseableHttpResponse) {
((CloseableHttpResponse) response).close();
}
}
RuntimeResourceDefinition definition = theClient.getFhirContext().getResourceDefinition(resourceId.getResourceType());
Class<? extends IBaseResource> resourceType = definition.getImplementingClass();
myResource = theClient.fetchResourceFromUrl(resourceType, resourceUrl);
myResource.setId(resourceUrl);
return myResource;
}

View File

@ -46,21 +46,34 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.method.HttpGetClientInvocation;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.IClientResponseHandlerHandlesBinary;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public abstract class BaseClient implements IRestfulClient {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
private final HttpClient myClient;
@ -74,7 +87,7 @@ public abstract class BaseClient implements IRestfulClient {
private Boolean myPrettyPrint = false;
private SummaryEnum mySummary;
private final String myUrlBase;
BaseClient(HttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) {
super();
myClient = theClient;
@ -103,8 +116,8 @@ public abstract class BaseClient implements IRestfulClient {
}
/**
* Returns the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the
* FHIR specification)
* Returns the encoding that will be used on requests. Default is <code>null</code>, which means the client will not
* explicitly request an encoding. (This is standard behaviour according to the FHIR specification)
*/
public EncodingEnum getEncoding() {
return myEncoding;
@ -137,8 +150,9 @@ public abstract class BaseClient implements IRestfulClient {
}
/**
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
* HAPI based servers (and any other servers which might implement it).
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
* servers which might implement it).
*/
public Boolean getPrettyPrint() {
return myPrettyPrint;
@ -168,8 +182,7 @@ public abstract class BaseClient implements IRestfulClient {
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse, null, null);
}
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint,
boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements) {
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint, boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements) {
if (!myDontValidateConformance) {
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
@ -187,7 +200,7 @@ public abstract class BaseClient implements IRestfulClient {
} else if (theEncoding == EncodingEnum.JSON) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
}
if (theSummaryMode != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(theSummaryMode.getCode()));
} else if (mySummary != null) {
@ -197,8 +210,8 @@ public abstract class BaseClient implements IRestfulClient {
if (thePrettyPrint == Boolean.TRUE) {
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
}
if (theSubsetElements != null && theSubsetElements.isEmpty()== false) {
if (theSubsetElements != null && theSubsetElements.isEmpty() == false) {
params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ',')));
}
@ -366,8 +379,9 @@ public abstract class BaseClient implements IRestfulClient {
}
/**
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
* HAPI based servers (and any other servers which might implement it).
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
* servers which might implement it).
*/
public boolean isPrettyPrint() {
return Boolean.TRUE.equals(myPrettyPrint);
@ -397,7 +411,8 @@ public abstract class BaseClient implements IRestfulClient {
}
/**
* This method is an internal part of the HAPI API and may change, use with caution. If you want to disable the loading of conformance statements, use
* This method is an internal part of the HAPI API and may change, use with caution. If you want to disable the
* loading of conformance statements, use
* {@link IRestfulClientFactory#setServerValidationModeEnum(ServerValidationModeEnum)}
*/
public void setDontValidateConformance(boolean theDontValidateConformance) {
@ -405,8 +420,9 @@ public abstract class BaseClient implements IRestfulClient {
}
/**
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is perfectly acceptable behaviour according
* to the FHIR specification. In this case, the server will choose which encoding to return, and the client can handle either XML or JSON)
* Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not
* explicitly request an encoding. (This is perfectly acceptable behaviour according to the FHIR specification. In
* this case, the server will choose which encoding to return, and the client can handle either XML or JSON)
*/
@Override
public void setEncoding(EncodingEnum theEncoding) {
@ -436,8 +452,9 @@ public abstract class BaseClient implements IRestfulClient {
}
/**
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
* HAPI based servers (and any other servers which might implement it).
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note
* that this is currently a non-standard flag (_pretty) which is supported only by HAPI based servers (and any other
* servers which might implement it).
*/
@Override
public void setPrettyPrint(Boolean thePrettyPrint) {
@ -477,4 +494,67 @@ public abstract class BaseClient implements IRestfulClient {
return reader;
}
@Override
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) {
BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(theUrl);
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theResourceType, null, false);
return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null);
}
protected final class ResourceResponseHandler<T extends IBaseResource> implements IClientResponseHandler<T> {
private boolean myAllowHtmlResponse;
private IIdType myId;
private Class<T> myType;
public ResourceResponseHandler(Class<T> theType, IIdType theId) {
myType = theType;
myId = theId;
}
public ResourceResponseHandler(Class<T> theType, IIdType theId, boolean theAllowHtmlResponse) {
myType = theType;
myId = theId;
myAllowHtmlResponse = theAllowHtmlResponse;
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
if (myAllowHtmlResponse && theResponseMimeType.toLowerCase().contains(Constants.CT_HTML) && myType != null) {
return readHtmlResponse(theResponseReader);
}
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
IParser parser = respType.newParser(getFhirContext());
T retVal = parser.parseResource(myType, theResponseReader);
MethodUtil.parseClientRequestResourceHeaders(myId, theHeaders, retVal);
return retVal;
}
@SuppressWarnings("unchecked")
private T readHtmlResponse(Reader theResponseReader) {
RuntimeResourceDefinition resDef = getFhirContext().getResourceDefinition(myType);
IBaseResource instance = resDef.newInstance();
BaseRuntimeChildDefinition textChild = resDef.getChildByName("text");
BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text");
IBase textInstance = textElement.newInstance();
textChild.getMutator().addValue(instance, textInstance);
BaseRuntimeChildDefinition divChild = textElement.getChildByName("div");
BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div");
IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance();
try {
divInstance.setValueAsString(IOUtils.toString(theResponseReader));
} catch (Exception e) {
throw new InvalidResponseException(400, "Failed to process HTML response from server: " + e.getMessage(), e);
}
divChild.getMutator().addValue(textInstance, divInstance);
return (T) instance;
}
}
}

View File

@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.http.client.HttpClient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
@ -57,6 +58,7 @@ class ClientInvocationHandlerFactory {
myMethodToLambda.put(theClientType.getMethod("registerInterceptor", IClientInterceptor.class), new RegisterInterceptorLambda());
myMethodToLambda.put(theClientType.getMethod("unregisterInterceptor", IClientInterceptor.class), new UnregisterInterceptorLambda());
myMethodToLambda.put(theClientType.getMethod("setSummary", SummaryEnum.class), new SetSummaryLambda());
myMethodToLambda.put(theClientType.getMethod("fetchResourceFromUrl", Class.class, String.class), new FetchResourceFromUrlLambda());
} catch (NoSuchMethodException e) {
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
@ -85,7 +87,18 @@ class ClientInvocationHandlerFactory {
return null;
}
}
class FetchResourceFromUrlLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
@SuppressWarnings("unchecked")
Class<? extends IBaseResource> type = (Class<? extends IBaseResource>) theArgs[0];
String url = (String) theArgs[1];
return theTarget.fetchResourceFromUrl(type, url);
}
}
class SetEncodingLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {

View File

@ -50,7 +50,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
@ -74,7 +73,6 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate;
@ -1719,62 +1717,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
}
private final class ResourceResponseHandler<T extends IBaseResource> implements IClientResponseHandler<T> {
private boolean myAllowHtmlResponse;
private IIdType myId;
private Class<T> myType;
public ResourceResponseHandler(Class<T> theType, IIdType theId) {
myType = theType;
myId = theId;
}
public ResourceResponseHandler(Class<T> theType, IIdType theId, boolean theAllowHtmlResponse) {
myType = theType;
myId = theId;
myAllowHtmlResponse = theAllowHtmlResponse;
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
if (myAllowHtmlResponse && theResponseMimeType.toLowerCase().contains(Constants.CT_HTML) && myType != null) {
return readHtmlResponse(theResponseReader);
}
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
}
IParser parser = respType.newParser(myContext);
T retVal = parser.parseResource(myType, theResponseReader);
MethodUtil.parseClientRequestResourceHeaders(myId, theHeaders, retVal);
return retVal;
}
@SuppressWarnings("unchecked")
private T readHtmlResponse(Reader theResponseReader) {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(myType);
IBaseResource instance = resDef.newInstance();
BaseRuntimeChildDefinition textChild = resDef.getChildByName("text");
BaseRuntimeElementCompositeDefinition<?> textElement = (BaseRuntimeElementCompositeDefinition<?>) textChild.getChildByName("text");
IBase textInstance = textElement.newInstance();
textChild.getMutator().addValue(instance, textInstance);
BaseRuntimeChildDefinition divChild = textElement.getChildByName("div");
BaseRuntimeElementDefinition<?> divElement = divChild.getChildByName("div");
IPrimitiveType<?> divInstance = (IPrimitiveType<?>) divElement.newInstance();
try {
divInstance.setValueAsString(IOUtils.toString(theResponseReader));
} catch (Exception e) {
throw new InvalidResponseException(400, "Failed to process HTML response from server: " + e.getMessage(), e);
}
divChild.getMutator().addValue(textInstance, divInstance);
return (T) instance;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private class SearchInternal extends BaseClientExecutable<IQuery<Object>, Object>implements IQuery<Object>, IUntypedQuery {

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.client.api;
import org.apache.http.client.HttpClient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.SummaryEnum;
@ -30,6 +31,17 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
public interface IRestfulClient {
/**
* Retrieve the contents at the given URL and parse them as a resource. This
* method could be used as a low level implementation of a read/vread/search
* operation.
*
* @param theResourceType The resource type to parse
* @param theUrl The URL to load
* @return The parsed resource
*/
<T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl);
/**
* Returns the FHIR context associated with this client
*/
@ -51,7 +63,7 @@ public interface IRestfulClient {
* logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
/**
* Specifies that the client should use the given encoding to do its
* queries. This means that the client will append the "_format" param
@ -62,7 +74,7 @@ public interface IRestfulClient {
* an encoding (which generally implies the use of XML). The default is <code>null</code>.
*/
void setEncoding(EncodingEnum theEncoding);
/**
* Specifies that the client should request that the server respond with "pretty printing"
* enabled. Note that this is a non-standard parameter, not all servers will
@ -71,7 +83,7 @@ public interface IRestfulClient {
* @param thePrettyPrint The pretty print flag to use in the request (default is <code>false</code>)
*/
void setPrettyPrint(Boolean thePrettyPrint);
/**
* If not set to <code>null</code>, specifies a value for the <code>_summary</code> parameter
* to be applied globally on this client.
@ -82,5 +94,5 @@ public interface IRestfulClient {
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
}

View File

@ -0,0 +1,210 @@
package ca.uhn.fhir.model.primitive;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.Charset;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.Constants;
public class BaseResourceReferenceDtTest {
private static FhirContext ourCtx;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@Before
public void before() {
ourCtx = FhirContext.forDstu2();
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
private ArgumentCaptor<HttpUriRequest> fixtureJson() throws IOException, ClientProtocolException {
Patient patient = new Patient();
patient.addName().addFamily("FAM");
final String input = ourCtx.newJsonParser().encodeResourceToString(patient);
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), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") });
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(input), Charset.forName("UTF-8"));
}
});
return capt;
}
private ArgumentCaptor<HttpUriRequest> fixtureXml() throws IOException, ClientProtocolException {
Patient patient = new Patient();
patient.addName().addFamily("FAM");
final String input = ourCtx.newXmlParser().encodeResourceToString(patient);
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), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Sat, 20 Jun 2015 19:32:17 GMT") });
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(input), Charset.forName("UTF-8"));
}
});
return capt;
}
@Test
public void testLoadResourceFromAnnotationClientJson() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = fixtureJson();
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example.com/fhir");
ResourceReferenceDt ref = new ResourceReferenceDt();
ref.setReference("http://domain2.example.com/base/Patient/123");
Patient response = (Patient) ref.loadResource(client);
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://domain2.example.com/base/Patient/123", response.getId().getValue());
}
@Test
public void testErrors() {
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example.com/fhir");
try {
new ResourceReferenceDt().loadResource(client);
fail();
} catch (IllegalStateException e) {
assertEquals("Reference has no resource ID defined", e.getMessage());
}
try {
new ResourceReferenceDt("123").loadResource(client);
fail();
} catch (IllegalStateException e) {
assertEquals("Reference is not complete (must be in the form [baseUrl]/[resource type]/[resource ID]) - Reference is: 123", e.getMessage());
}
try {
new ResourceReferenceDt("Patient/123").loadResource(client);
fail();
} catch (IllegalStateException e) {
assertEquals("Reference is not complete (must be in the form [baseUrl]/[resource type]/[resource ID]) - Reference is: Patient/123", e.getMessage());
}
try {
new ResourceReferenceDt("http://foo/123123").loadResource(client);
fail();
} catch (DataFormatException e) {
assertEquals("Unknown resource name \"Foo\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
}
try {
new ResourceReferenceDt("http://foo/Sometype/123123").loadResource(client);
fail();
} catch (DataFormatException e) {
assertEquals("Unknown resource name \"Sometype\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
}
}
@Test
public void testReturnAlreadyLoadedInstance() throws ClientProtocolException, IOException {
ArgumentCaptor<HttpUriRequest> capt = fixtureJson();
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example4.com/fhir");
Patient pat = new Patient();
ResourceReferenceDt ref = new ResourceReferenceDt();
ref.setReference("http://domain2.example.com/base/Patient/123");
ref.setResource(pat);
Patient response = (Patient) ref.loadResource(client);
assertEquals(0, capt.getAllValues().size());
assertSame(pat, response);
}
@Test
public void testLoadResourceFromAnnotationClientXml() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = fixtureXml();
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example3.com/fhir");
ResourceReferenceDt ref = new ResourceReferenceDt();
ref.setReference("http://domain2.example.com/base/Patient/123");
Patient response = (Patient) ref.loadResource(client);
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testLoadResourceFromGenericClientJson() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = fixtureJson();
IGenericClient client = ourCtx.newRestfulGenericClient("http://example1.com/fhir");
ResourceReferenceDt ref = new ResourceReferenceDt();
ref.setReference("http://domain2.example.com/base/Patient/123");
Patient response = (Patient) ref.loadResource(client);
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
}
@Test
public void testLoadResourceFromGenericClientXml() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = fixtureXml();
IGenericClient client = ourCtx.newRestfulGenericClient("http://example2.com/fhir");
ResourceReferenceDt ref = new ResourceReferenceDt();
ref.setReference("http://domain2.example.com/base/Patient/123");
Patient response = (Patient) ref.loadResource(client);
assertEquals("http://domain2.example.com/base/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
}
public interface IClientType extends IRestfulClient {
}
}

View File

@ -188,6 +188,12 @@
by calling the actual implementing method in the server (previously
the call was simulated, which meant that many features did not work)
</action>
<action type="fix">
ResourceReferenceDt#loadResource(IRestfulClient) did not
use the client's read functionality, so it did not
handle JSON responses or use interceptors. Thanks to
JT for reporting!
</action>
</release>
<release version="1.2" date="2015-09-18">
<action type="add">