From 524b16b1e87e1cda0ea277878488a25303cd7208 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Sun, 28 Feb 2016 15:44:10 -0500 Subject: [PATCH] Fix header generation for clients --- .../rest/client/RestfulClientFactory.java | 4 + .../java/ca/uhn/fhir/rest/gclient/IQuery.java | 5 +- .../BaseHttpClientInvocationWithContents.java | 6 +- .../client/JaxRsRestfulClientFactory.java | 2 +- .../rest/client/GenericClientDstu2Test.java | 119 ++++++++++++++---- pom.xml | 34 +++-- src/changes/changes.xml | 13 +- src/site/site.xml | 6 +- src/site/xdoc/hacking_hapi_fhir.xml | 2 + 9 files changed, 150 insertions(+), 41 deletions(-) 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 ccbc2c1be90..c7a5e99199a 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 @@ -45,6 +45,9 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.util.FhirTerser; +/** + * Base class for a REST client factory implementation + */ public abstract class RestfulClientFactory implements IRestfulClientFactory { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulClientFactory.class); @@ -268,6 +271,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory { theBindings, theMethodToLambda, this); } + @SuppressWarnings("unchecked") @Override public void validateServerBase(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) { GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java index a528d6ec4b0..f2c99d66a18 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java @@ -83,7 +83,10 @@ public interface IQuery extends IClientExecutable, T>, IBaseQueryFHIR Specification Section 2.1.11) + * FHIR Search Specification) + *

+ * This can be used to force the use of an HTTP POST instead of an HTTP GET + *

* * @see SearchStyleEnum * @since 0.6 diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java index 1181d48de9e..8895f98b16e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java @@ -193,13 +193,13 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca encoding = MethodUtil.detectEncoding(myContents); } - if (encoding == null) { - encoding = EncodingEnum.XML; - } if (myParams != null) { return httpClient.createParamRequest(getContext(), myParams, encoding); } else { + if (encoding == null) { + encoding = EncodingEnum.XML; + } String contents = parseContents(thePrettyPrint, encoding); String contentType = getContentType(encoding); return httpClient.createByteRequest(getContext(), contents, contentType, encoding); diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java index 8654a73763b..edfcb394251 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java @@ -72,7 +72,7 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory { @Override public void setProxy(String theHost, Integer thePort) { - throw new UnsupportedOperationException("Proxies are not supported yet"); + throw new UnsupportedOperationException("Proxies are not supported yet in JAX-RS client"); } /** diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index fb583c22261..c93ad41f6af 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.rest.client; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; @@ -16,6 +17,7 @@ import java.io.InputStream; import java.io.StringReader; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.List; @@ -68,6 +70,7 @@ 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.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.method.SearchStyleEnum; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; @@ -610,8 +613,7 @@ public class GenericClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z")) .execute(); //@formatter:on - assertThat(capt.getAllValues().get(idx).getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")) - .or(equalTo("http://example.com/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(capt.getAllValues().get(idx).getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://example.com/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); idx++; @@ -664,8 +666,7 @@ public class GenericClientDstu2Test { assertEquals("http://example.com/fhir/Patient/123/$meta-add", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); - assertEquals("", - extractBody(capt, idx)); + assertEquals("", extractBody(capt, idx)); idx++; } @@ -926,7 +927,8 @@ public class GenericClientDstu2Test { Parameters outParams = client.operation().onInstance(new IdDt("Patient", "18066")).named("$everything").withParameters(inParams).execute(); /* - * Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation methods return a Parameters instance however, so HAPI creates a Parameters object + * Note that the $everything operation returns a Bundle instead of a Parameters resource. The client operation + * methods return a Parameters instance however, so HAPI creates a Parameters object * with a single parameter containing the value. */ ca.uhn.fhir.model.dstu2.resource.Bundle responseBundle = (ca.uhn.fhir.model.dstu2.resource.Bundle) outParams.getParameter().get(0).getResource(); @@ -1018,9 +1020,7 @@ public class GenericClientDstu2Test { assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length); assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); - assertEquals( - "", - (extractBody(capt, idx))); + assertEquals("", (extractBody(capt, idx))); idx++; /* @@ -1041,8 +1041,7 @@ public class GenericClientDstu2Test { assertEquals(1, capt.getAllValues().get(idx).getHeaders(Constants.HEADER_CONTENT_TYPE).length); assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); - assertEquals( - "", + assertEquals("", (extractBody(capt, idx))); idx++; @@ -1549,8 +1548,7 @@ public class GenericClientDstu2Test { .execute(); //@formatter:on - assertThat(capt.getValue().getURI().toString(), - either(equalTo("http://example.com/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(capt.getValue().getURI().toString(), either(equalTo("http://example.com/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1762,12 +1760,93 @@ public class GenericClientDstu2Test { .execute(); //@formatter:on - assertThat(capt.getValue().getURI().toString(), - either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(capt.getValue().getURI().toString(), either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); } + @Test + public void testSearchByPost() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor 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.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .execute(); + //@formatter:on + + assertEquals("http://example.com/fhir/Patient/_search?_elements=identifier%2Cname", capt.getValue().getURI().toString()); + + // assertThat(capt.getValue().getURI().toString(), + // either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + ourLog.info(Arrays.asList(capt.getValue().getAllHeaders()).toString()); + ourLog.info(capt.getValue().toString()); + + HttpEntityEnclosingRequestBase v = (HttpEntityEnclosingRequestBase) capt.getValue(); + String req = IOUtils.toString(v.getEntity().getContent(), "UTF-8"); + assertEquals("name=james", req); + + assertEquals("application/x-www-form-urlencoded;charset=utf-8", v.getEntity().getContentType().getValue().replace(" ", "").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON, capt.getValue().getFirstHeader("accept").getValue()); + assertThat(capt.getValue().getFirstHeader("user-agent").getValue(), not(emptyString())); + } + + @Test + public void testSearchByPostUseJson() throws Exception { + String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; + + ArgumentCaptor 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.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"))); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + //@formatter:off + Bundle response = client.search() + .forResource("Patient") + .where(Patient.NAME.matches().value("james")) + .elementsSubset("name", "identifier") + .usingStyle(SearchStyleEnum.POST) + .encodedJson() + .execute(); + //@formatter:on + + assertThat(capt.getValue().getURI().toString(), containsString("http://example.com/fhir/Patient/_search?")); + assertThat(capt.getValue().getURI().toString(), containsString("_elements=identifier%2Cname")); + assertThat(capt.getValue().getURI().toString(), containsString("_format=json")); + + // assertThat(capt.getValue().getURI().toString(), + // either(equalTo("http://example.com/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://example.com/fhir/Patient?name=james&_elements=identifier%2Cname"))); + + assertEquals(Patient.class, response.getEntries().get(0).getResource().getClass()); + + ourLog.info(Arrays.asList(capt.getValue().getAllHeaders()).toString()); + ourLog.info(capt.getValue().toString()); + + HttpEntityEnclosingRequestBase v = (HttpEntityEnclosingRequestBase) capt.getValue(); + String req = IOUtils.toString(v.getEntity().getContent(), "UTF-8"); + assertEquals("name=james", req); + + assertEquals("application/x-www-form-urlencoded;charset=utf-8", v.getEntity().getContentType().getValue().replace(" ", "").toLowerCase()); + assertEquals(Constants.CT_FHIR_JSON, capt.getValue().getFirstHeader("accept").getValue()); + } + @Test public void testSearchWithLastUpdated() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; @@ -2207,9 +2286,7 @@ public class GenericClientDstu2Test { response = client.validate().resource(p).execute(); assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); - assertEquals( - "", - extractBody(capt, idx)); + assertEquals("", extractBody(capt, idx)); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); idx++; @@ -2217,9 +2294,7 @@ public class GenericClientDstu2Test { response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); assertEquals("http://example.com/fhir/Patient/$validate?_format=xml", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); - assertEquals( - "", - extractBody(capt, idx)); + assertEquals("", extractBody(capt, idx)); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); idx++; @@ -2273,9 +2348,7 @@ public class GenericClientDstu2Test { assertEquals("http://example.com/fhir/Patient/$validate", capt.getAllValues().get(idx).getURI().toASCIIString()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); - assertEquals( - "", - extractBody(capt, idx)); + assertEquals("", extractBody(capt, idx)); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); idx++; diff --git a/pom.xml b/pom.xml index ec807d63b4a..fa10dc825e5 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ pom 1.5-SNAPSHOT HAPI-FHIR - http://jamesagnew.github.io/hapi-fhir/ + https://github.com/jamesagnew/hapi-fhir University Health Network @@ -240,20 +240,19 @@ 10.12.1.1 - 2.22.1 + 2.22.2 9.3.7.v20160115 5.1.0.Final - 5.2.3.Final + 5.2.4.Final 2.5.3 1.8 - 3.4 2.4 1.1.8 2.7.1 4.4.4 - 4.2.4.RELEASE + 4.2.5.RELEASE 2.1.4.RELEASE 1.0.1 1.6 @@ -274,7 +273,7 @@ ch.qos.logback logback-classic - 1.1.3 + 1.1.5 com.google.guava @@ -744,7 +743,7 @@ org.apache.maven.plugins maven-source-plugin - 2.4 + 3.0 org.codehaus.plexus @@ -819,7 +818,7 @@ org.apache.maven.plugins maven-site-plugin - 3.4 + 3.5 false true @@ -1299,6 +1298,25 @@ + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index fd7f59a6f0a..496f74f77ca 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -13,6 +13,7 @@
  • Hibernate (JPA, Web Tester): 5.0.7 -> 5.1.0
  • +
  • Spring (JPA, Web Tester): 4.2.4 -> 4.2.5
  • SLF4j (All): 1.7.14 -> 1.7.16
  • ]]> @@ -305,7 +306,7 @@
  • Logback (Core): 1.1.2 -> 1.1.3
  • SLF4j (Core): 1.7.102 -> 1.7.12
  • Springframework (JPA, Web Tester): 4.1.5 -> 4.2.2
  • -
  • Hibernate (JPA, Web Tester): 4.2.17 -> 5.0.3
  • +
  • Hibernate (JPA, Web Tester): 4.2.17 -> 5."
  • Hibernate Validator (JPA, Web Tester): 5.2.1 -> 5.2.2
  • Derby (JPA, CLI, Public Server): 10.11.1.1 -> 10.12.1.1
  • Jetty (JPA, CLI, Public Server): 9.2.6.v20141205 -> 9.3.4.v20151007
  • @@ -1879,8 +1880,12 @@ Support added for deleted-entry by/name, by/email, and comment from Tombstones spec - - - + + + + + + + diff --git a/src/site/site.xml b/src/site/site.xml index 6ecc6ff0c3a..37b1bf3d54f 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -149,6 +149,10 @@ + + + + @@ -193,7 +197,7 @@ Documentation|Get Help|Test Server HAPI FHIR|Test Server - Using HAPI + Using HAPI|Contribute JavaDocs|JXR Get Help|Maven Reports diff --git a/src/site/xdoc/hacking_hapi_fhir.xml b/src/site/xdoc/hacking_hapi_fhir.xml index 1a5a66757ea..a69758eb0ba 100644 --- a/src/site/xdoc/hacking_hapi_fhir.xml +++ b/src/site/xdoc/hacking_hapi_fhir.xml @@ -106,12 +106,14 @@ skip this section.

    + Maven Import
    Import the HAPI projects as Maven Modules by selecing File -> Import... from the File menu. Then select Existing Module Projects as shown below.



    + Select the Projects
    Next, browse to the directory where you checked out the HAPI FHIR sources. You might want to select only the projects you are interested in editing, in order to keep Eclipse's memory use down. You can always come back and