diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 96cd4f74f37..e24dca44224 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -469,7 +469,10 @@ public class RestfulServer extends HttpServlet { } } - bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, start, count, thePagingAction, + String linkSelfBase = getServerAddressStrategy().determineServerBase(getServletContext(), theRequest.getServletRequest()); + String linkSelf = linkSelfBase + theRequest.getCompleteUrl().substring(theRequest.getCompleteUrl().indexOf('?')); + + bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, thePagingAction, null, includes); Bundle bundle = bundleFactory.getDstu1Bundle(); @@ -653,6 +656,15 @@ public class RestfulServer extends HttpServlet { String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION); if (getPagingProvider() != null && isNotBlank(pagingAction)) { requestDetails.setOtherOperationType(OtherOperationTypeEnum.GET_PAGE); + if (theRequestType != RequestTypeEnum.GET) { + /* + * We reconstruct the link-self URL using the request parameters, and + * this would break if the parameters came in using a POST. We could + * probably work around that but why bother unless someone comes up with + * a reason for needing it. + */ + throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(RestfulServer.class, "getPagesNonHttpGet")); + } handlePagingRequest(requestDetails, theResponse, pagingAction); return; } diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 89a6db0d248..6c440d9f774 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -24,6 +24,8 @@ ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") ) ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search +ca.uhn.fhir.rest.server.RestfulServer.getPagesNonHttpGet=Requests for _getpages must use HTTP GET + ca.uhn.fhir.validation.FhirValidator.noPhlocWarningOnStartup=Phloc-schematron library not found on classpath, will not attempt to perform schematron validation ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not found on classpath, can not enable perform schematron validation diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java index 2a280bb3e2e..e65c9566b1a 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java @@ -1,10 +1,13 @@ package ca.uhn.fhir.rest.server; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -17,6 +20,8 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -166,7 +171,6 @@ public class SearchTest { public void testReturnLinksWithAddressStrategy() throws Exception { ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("https://blah.com/base")); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findWithLinks"); CloseableHttpResponse status = ourClient.execute(httpGet); @@ -177,17 +181,65 @@ public class SearchTest { ourLog.info(responseContent); - assertEquals(1, bundle.getEntries().size()); + assertEquals(10, bundle.getEntries().size()); assertEquals("https://blah.com/base", bundle.getLinkBase().getValue()); assertEquals("https://blah.com/base/Patient?_query=findWithLinks", bundle.getLinkSelf().getValue()); Patient p = bundle.getResources(Patient.class).get(0); assertEquals("AAANamed", p.getIdentifierFirstRep().getValue().getValue()); assertEquals("http://foo/Patient?_id=1", bundle.getEntries().get(0).getLinkSearch().getValue()); - assertEquals("https://blah.com/base/Patient/9988", bundle.getEntries().get(0).getLinkAlternate().getValue()); + assertEquals("https://blah.com/base/Patient/99881", bundle.getEntries().get(0).getLinkAlternate().getValue()); assertEquals("http://foo/Patient?_id=1", ResourceMetadataKeyEnum.LINK_SEARCH.get(p)); - assertEquals("https://blah.com/base/Patient/9988", ResourceMetadataKeyEnum.LINK_ALTERNATE.get(p)); + assertEquals("https://blah.com/base/Patient/99881", ResourceMetadataKeyEnum.LINK_ALTERNATE.get(p)); + String linkNext = bundle.getLinkNext().getValue(); + ourLog.info(linkNext); + assertThat(linkNext, startsWith("https://blah.com/base?_getpages=")); + + /* + * Load the second page + */ + String urlPart = linkNext.substring(linkNext.indexOf('?')); + String link = "http://localhost:" + ourPort + urlPart; + httpGet = new HttpGet(link); + + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + assertEquals(200, status.getStatusLine().getStatusCode()); + bundle = ourCtx.newXmlParser().parseBundle(responseContent); + + ourLog.info(responseContent); + + assertEquals(10, bundle.getEntries().size()); + assertEquals("https://blah.com/base", bundle.getLinkBase().getValue()); + assertEquals(linkNext, bundle.getLinkSelf().getValue()); + + p = bundle.getResources(Patient.class).get(0); + assertEquals("AAANamed", p.getIdentifierFirstRep().getValue().getValue()); + assertEquals("http://foo/Patient?_id=11", bundle.getEntries().get(0).getLinkSearch().getValue()); + assertEquals("https://blah.com/base/Patient/998811", bundle.getEntries().get(0).getLinkAlternate().getValue()); + assertEquals("http://foo/Patient?_id=11", ResourceMetadataKeyEnum.LINK_SEARCH.get(p)); + assertEquals("https://blah.com/base/Patient/998811", ResourceMetadataKeyEnum.LINK_ALTERNATE.get(p)); + + } + + /** + * Try loading the page as a POST just to make sure we get the right error + */ + @Test + public void testGetPagesWithPost() throws Exception { + + HttpPost httpPost = new HttpPost("http://localhost:" + ourPort); + List parameters = Collections.singletonList(new BasicNameValuePair("_getpages", "AAA")); + httpPost.setEntity(new UrlEncodedFormEntity(parameters)); + + CloseableHttpResponse status = ourClient.execute(httpPost); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(400, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("Requests for _getpages must use HTTP GET")); } @@ -423,6 +475,7 @@ public class SearchTest { ServletHandler proxyHandler = new ServletHandler(); ourServlet = new RestfulServer(); ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)); ourServlet.setResourceProviders(patientProvider, new DummyObservationResourceProvider()); ServletHolder servletHolder = new ServletHolder(ourServlet); @@ -572,12 +625,15 @@ public class SearchTest { public List findWithLinks() { ArrayList retVal = new ArrayList(); - Patient patient = new Patient(); - patient.setId("1"); - patient.addIdentifier("system", "AAANamed"); - ResourceMetadataKeyEnum.LINK_SEARCH.put(patient, ("http://foo/Patient?_id=1")); - ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, ("Patient/9988")); - retVal.add(patient); + for (int i = 1; i <= 20; i++) { + Patient patient = new Patient(); + patient.setId(""+i); + patient.addIdentifier("system", "AAANamed"); + ResourceMetadataKeyEnum.LINK_SEARCH.put(patient, ("http://foo/Patient?_id="+i)); + ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, ("Patient/9988" + i)); + retVal.add(patient); + } + return retVal; }