Fix #352 - Client should auto download resource if server ignores prefer header

This commit is contained in:
jamesagnew 2016-05-01 09:30:12 -04:00
parent 2bc1950bc1
commit e3e81ff202
4 changed files with 484 additions and 272 deletions

View File

@ -788,7 +788,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
@ -1608,6 +1608,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
private final String myResourceName;
private PreferReturnEnum myPrefer;
private OutcomeResponseHandler(String theResourceName, PreferReturnEnum thePrefer) {
this(theResourceName);
myPrefer = thePrefer;
}
private OutcomeResponseHandler(String theResourceName) {
myResourceName = theResourceName;
@ -1619,6 +1625,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) {
response.setCreated(true);
}
if (myPrefer == PreferReturnEnum.REPRESENTATION) {
if (response.getResource() == null) {
if (response.getId() != null && isNotBlank(response.getId().getValue()) && response.getId().hasBaseUrl()) {
ourLog.info("Server did not return resource for Prefer-representation, going to fetch: {}", response.getId().getValue());
IBaseResource resource = read().resource(response.getId().getResourceType()).withUrl(response.getId()).execute();
response.setResource(resource);
}
}
}
return response;
}
}
@ -2242,7 +2259,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);

View File

@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -781,8 +782,8 @@ public class MethodUtil {
if (reader != null) {
IParser parser = ct.newParser(theContext);
IBaseResource outcome = parser.parseResource(reader);
if (outcome instanceof BaseOperationOutcome) {
retVal.setOperationOutcome((BaseOperationOutcome) outcome);
if (outcome instanceof IBaseOperationOutcome) {
retVal.setOperationOutcome((IBaseOperationOutcome) outcome);
} else {
retVal.setResource(outcome);
}

View File

@ -1,9 +1,7 @@
package ca.uhn.fhir.rest.client;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -16,6 +14,7 @@ import java.util.List;
import org.apache.commons.io.IOUtils;
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.HttpClient;
@ -43,6 +42,8 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.CustomTypeDstu3Test;
import ca.uhn.fhir.parser.CustomTypeDstu3Test.MyCustomPatient;
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.server.Constants;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.VersionUtil;
@ -50,21 +51,21 @@ import ca.uhn.fhir.util.VersionUtil;
public class GenericClientDstu3Test {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu3Test.class);
private int myAnswerCount;
private HttpClient myHttpClient;
private HttpResponse myHttpResponse;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
public void before() {
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
myAnswerCount = 0;
}
private String expectedUserAgent() {
return "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client; FHIR DSTU3; apache)";
}
private byte[] extractBodyAsByteArray(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
@ -77,262 +78,6 @@ public class GenericClientDstu3Test {
return body;
}
@Test
public void testUserAgentForConformance() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
}
@Test
public void testExplicitCustomTypeSearch() throws Exception {
final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false));
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Bundle resp = client
.search()
.forResource(CustomTypeDstu3Test.MyCustomPatient.class)
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeHistoryType() throws Exception {
final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false));
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Bundle resp = client
.history()
.onType(CustomTypeDstu3Test.MyCustomPatient.class)
.andReturnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient/_history", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeLoadPage() throws Exception {
final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false));
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle bundle = new Bundle();
bundle.addLink().setRelation("next").setUrl("http://foo/next");
//@formatter:off
Bundle resp = client
.loadPage()
.next(bundle)
.preferResponseType(MyCustomPatient.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://foo/next", capt.getAllValues().get(0).getURI().toASCIIString());
//@formatter:off
resp = client
.loadPage()
.next(bundle)
.preferResponseTypes(toTypeList(MyCustomPatient.class))
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://foo/next", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeOperation() throws Exception {
Parameters param = new Parameters();
Patient patient = new Patient();
patient.addName().addFamily("FOO");
param.addParameter().setName("foo").setResource(patient);
final String respString = ourCtx.newXmlParser().encodeResourceToString(param);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Parameters resp = client
.operation()
.onServer()
.named("foo")
.withNoParameters(Parameters.class)
.preferResponseType(MyCustomPatient.class)
.execute();
//@formatter:on
assertEquals(1, resp.getParameter().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/$foo", capt.getAllValues().get(0).getURI().toASCIIString());
//@formatter:off
resp = client
.operation()
.onType(MyCustomPatient.class)
.named("foo")
.withNoParameters(Parameters.class)
.execute();
//@formatter:on
assertEquals(1, resp.getParameter().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient/$foo", capt.getAllValues().get(1).getURI().toASCIIString());
}
private List<Class<? extends IBaseResource>> toTypeList(Class<? extends IBaseResource> theClass) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>();
retVal.add(theClass);
return retVal;
}
@Test
public void testUserAgentForBinary() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Binary bin = new Binary();
bin.setContentType("application/foo");
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
client.create().resource(bin).execute();
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue());
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, extractBodyAsByteArray(capt));
}
@Test
public void testBinaryCreateWithNoContentType() throws Exception {
IParser p = ourCtx.newXmlParser();
OperationOutcome conf = new OperationOutcome();
conf.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(conf);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Binary bin = new Binary();
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
client.create().resource(bin).execute();
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
}
@Test
public void testBinaryCreateWithFhirContentType() throws Exception {
IParser p = ourCtx.newXmlParser();
@ -376,6 +121,285 @@ public class GenericClientDstu3Test {
assertEquals("<div>A PATIENT</div>", outputPt.getText().getDivAsString());
}
@Test
public void testBinaryCreateWithNoContentType() throws Exception {
IParser p = ourCtx.newXmlParser();
OperationOutcome conf = new OperationOutcome();
conf.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(conf);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Binary bin = new Binary();
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
client.create().resource(bin).execute();
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
}
@Test
public void testCreateWithPreferRepresentationServerReturnsOO() throws Exception {
final IParser p = ourCtx.newXmlParser();
final OperationOutcome resp0 = new OperationOutcome();
resp0.getText().setDivAsString("OK!");
final Patient resp1 = new Patient();
resp1.getText().setDivAsString("FINAL VALUE");
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.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
@Override
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myAnswerCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.create().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
assertEquals(2, myAnswerCount);
assertNotNull(outcome.getOperationOutcome());
assertNotNull(outcome.getResource());
assertEquals("<div>OK!</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
assertEquals("<div>FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals(myAnswerCount, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
}
@Test
public void testCreateWithPreferRepresentationServerReturnsResource() throws Exception {
final IParser p = ourCtx.newXmlParser();
final Patient resp1 = new Patient();
resp1.getText().setDivAsString("FINAL VALUE");
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.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
@Override
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
myAnswerCount++;
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.create().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
assertEquals(1, myAnswerCount);
assertNull(outcome.getOperationOutcome());
assertNotNull(outcome.getResource());
assertEquals("<div>FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals(myAnswerCount, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeHistoryType() throws Exception {
final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false));
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Bundle resp = client
.history()
.onType(CustomTypeDstu3Test.MyCustomPatient.class)
.andReturnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient/_history", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeLoadPage() throws Exception {
final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false));
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle bundle = new Bundle();
bundle.addLink().setRelation("next").setUrl("http://foo/next");
//@formatter:off
Bundle resp = client
.loadPage()
.next(bundle)
.preferResponseType(MyCustomPatient.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://foo/next", capt.getAllValues().get(0).getURI().toASCIIString());
//@formatter:off
resp = client
.loadPage()
.next(bundle)
.preferResponseTypes(toTypeList(MyCustomPatient.class))
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://foo/next", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeOperation() throws Exception {
Parameters param = new Parameters();
Patient patient = new Patient();
patient.addName().addFamily("FOO");
param.addParameter().setName("foo").setResource(patient);
final String respString = ourCtx.newXmlParser().encodeResourceToString(param);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Parameters resp = client
.operation()
.onServer()
.named("foo")
.withNoParameters(Parameters.class)
.preferResponseType(MyCustomPatient.class)
.execute();
//@formatter:on
assertEquals(1, resp.getParameter().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/$foo", capt.getAllValues().get(0).getURI().toASCIIString());
//@formatter:off
resp = client
.operation()
.onType(MyCustomPatient.class)
.named("foo")
.withNoParameters(Parameters.class)
.execute();
//@formatter:on
assertEquals(1, resp.getParameter().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getParameter().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient/$foo", capt.getAllValues().get(1).getURI().toASCIIString());
}
@Test
public void testExplicitCustomTypeSearch() throws Exception {
final String respString = CustomTypeDstu3Test.createBundle(CustomTypeDstu3Test.createResource(false));
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
Bundle resp = client
.search()
.forResource(CustomTypeDstu3Test.MyCustomPatient.class)
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals(1, resp.getEntry().size());
assertEquals(CustomTypeDstu3Test.MyCustomPatient.class, resp.getEntry().get(0).getResource().getClass());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testUpdateById() throws Exception {
IParser p = ourCtx.newXmlParser();
@ -414,18 +438,181 @@ public class GenericClientDstu3Test {
assertThat(body, containsString("<id value=\"111\"/>"));
}
@Test
public void testUpdateWithPreferRepresentationServerReturnsOO() throws Exception {
final IParser p = ourCtx.newXmlParser();
final OperationOutcome resp0 = new OperationOutcome();
resp0.getText().setDivAsString("OK!");
final Patient resp1 = new Patient();
resp1.getText().setDivAsString("FINAL VALUE");
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.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
@Override
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myAnswerCount++ == 0) {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.setId("Patient/222");
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
assertEquals(2, myAnswerCount);
assertNotNull(outcome.getOperationOutcome());
assertNotNull(outcome.getResource());
assertEquals("<div>OK!</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
assertEquals("<div>FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals(myAnswerCount, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
}
@Test
public void testUpdateWithPreferRepresentationServerReturnsResource() throws Exception {
final IParser p = ourCtx.newXmlParser();
final Patient resp1 = new Patient();
resp1.getText().setDivAsString("FINAL VALUE");
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.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
@Override
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
}
});
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
myAnswerCount++;
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Patient pt = new Patient();
pt.setId("Patient/222");
pt.getText().setDivAsString("A PATIENT");
MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
assertEquals(1, myAnswerCount);
assertNull(outcome.getOperationOutcome());
assertNotNull(outcome.getResource());
assertEquals("<div>FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals(myAnswerCount, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString());
}
@Test
public void testUserAgentForBinary() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Binary bin = new Binary();
bin.setContentType("application/foo");
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
client.create().resource(bin).execute();
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
assertEquals("application/foo", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue());
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, extractBodyAsByteArray(capt));
}
@Test
public void testUserAgentForConformance() throws Exception {
IParser p = ourCtx.newXmlParser();
Conformance conf = new Conformance();
conf.setCopyright("COPY");
final String respString = p.encodeResourceToString(conf);
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.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
@Override
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
}
});
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
client.fetchConformance().ofType(Conformance.class).execute();
assertEquals("http://example.com/fhir/metadata", capt.getAllValues().get(0).getURI().toASCIIString());
validateUserAgent(capt);
}
private List<Class<? extends IBaseResource>> toTypeList(Class<? extends IBaseResource> theClass) {
ArrayList<Class<? extends IBaseResource>> retVal = new ArrayList<Class<? extends IBaseResource>>();
retVal.add(theClass);
return retVal;
}
private void validateUserAgent(ArgumentCaptor<HttpUriRequest> capt) {
assertEquals(1, capt.getAllValues().get(0).getHeaders("User-Agent").length);
assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
}
private String expectedUserAgent() {
return "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client; FHIR DSTU3; apache)";
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
}

View File

@ -56,6 +56,13 @@
if a resource being saved contained references that had a display value but
not an actual reference. Thanks to David Hay for reporting!
</action>
<action type="add" issue="352">
When performing a REST Client create or update with
<![CDATA[<code>Prefer: return=representation</code>]]> set,
if the server does not honour the Prefer header, the client
will automatically fetch the resource before returning. Thanks
to Ewout Kramer for the idea!
</action>
</release>
<release version="1.5" date="2016-04-20">
<action type="fix" issue="339">