Fix #116 - Preserve _include links across pages
This commit is contained in:
parent
317251ca4d
commit
94af375e4d
|
@ -206,11 +206,11 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
myBundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
myBundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
myBundle.getLinkPrevious().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
myBundle.getLinkPrevious().setValue(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import ca.uhn.fhir.context.ProvidedResourceScanner;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -456,8 +457,17 @@ public class RestfulServer extends HttpServlet {
|
|||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
|
||||
IVersionSpecificBundleFactory bundleFactory = myFhirContext.newBundleFactory();
|
||||
bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, start, count, thePagingAction,
|
||||
null, IResource.WILDCARD_ALL_SET);
|
||||
|
||||
Set<Include> includes = new HashSet<Include>();
|
||||
String[] reqIncludes = theRequest.getServletRequest().getParameterValues(Constants.PARAM_INCLUDE);
|
||||
if (reqIncludes != null) {
|
||||
for (String nextInclude : reqIncludes) {
|
||||
includes.add(new Include(nextInclude));
|
||||
}
|
||||
}
|
||||
|
||||
bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, start, count, thePagingAction,
|
||||
null, includes);
|
||||
|
||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||
if (bundle != null) {
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.net.URLEncoder;
|
|||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
@ -44,6 +45,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
|
@ -218,38 +220,51 @@ public class RestfulServerUtils {
|
|||
return EncodingEnum.XML;
|
||||
}
|
||||
|
||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
b.append('?');
|
||||
b.append(Constants.PARAM_PAGINGACTION);
|
||||
b.append('=');
|
||||
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||
try {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
b.append('?');
|
||||
b.append(Constants.PARAM_PAGINGACTION);
|
||||
b.append('=');
|
||||
b.append(URLEncoder.encode(theSearchId, "UTF-8"));
|
||||
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PAGINGOFFSET);
|
||||
b.append('=');
|
||||
b.append(theOffset);
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_COUNT);
|
||||
b.append('=');
|
||||
b.append(theCount);
|
||||
if (theResponseEncoding != null) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_FORMAT);
|
||||
b.append('=');
|
||||
b.append(theResponseEncoding.getRequestContentType());
|
||||
}
|
||||
if (thePrettyPrint) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PRETTY);
|
||||
b.append('=');
|
||||
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
|
||||
}
|
||||
|
||||
if (theIncludes != null) {
|
||||
for (Include nextInclude : theIncludes) {
|
||||
if (isNotBlank(nextInclude.getValue())) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_INCLUDE);
|
||||
b.append('=');
|
||||
b.append(URLEncoder.encode(nextInclude.getValue(), "UTF-8"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("UTF-8 not supported", e);// should not happen
|
||||
}
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PAGINGOFFSET);
|
||||
b.append('=');
|
||||
b.append(theOffset);
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_COUNT);
|
||||
b.append('=');
|
||||
b.append(theCount);
|
||||
if (theResponseEncoding != null) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_FORMAT);
|
||||
b.append('=');
|
||||
b.append(theResponseEncoding.getRequestContentType());
|
||||
}
|
||||
if (thePrettyPrint) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PRETTY);
|
||||
b.append('=');
|
||||
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import static org.mockito.Mockito.*;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -25,7 +26,9 @@ import org.junit.Test;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
|
@ -151,6 +154,49 @@ public class PagingTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See #116
|
||||
*/
|
||||
@Test()
|
||||
public void testPagingPreservesIncludes() throws Exception {
|
||||
when(myPagingProvider.getDefaultPageSize()).thenReturn(5);
|
||||
when(myPagingProvider.getMaximumPageSize()).thenReturn(9);
|
||||
when(myPagingProvider.storeResultList(any(IBundleProvider.class))).thenReturn("ABCD");
|
||||
when(myPagingProvider.retrieveResultList(eq("ABCD"))).thenReturn(ourBundleProvider);
|
||||
|
||||
String link;
|
||||
String base = "http://localhost:" + ourPort;
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(base + "/Patient?_count=2&_format=xml&_include=Patient.managingOrganization&_include=foo");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
assertEquals("0", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("1", bundle.getEntries().get(1).getId().getIdPart());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkNext().getValue());
|
||||
assertNull(bundle.getLinkPrevious().getValue());
|
||||
link = bundle.getLinkNext().getValue();
|
||||
}
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(link);
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
assertEquals("2", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("3", bundle.getEntries().get(1).getId().getIdPart());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkSelf().getValue());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2&_format=xml&_include=Patient.managingOrganization&_include=foo", bundle.getLinkPrevious().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchSmallPages() throws Exception {
|
||||
|
@ -243,7 +289,7 @@ public class PagingTest {
|
|||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Search
|
||||
public IBundleProvider findPatient() {
|
||||
public IBundleProvider findPatient(@IncludeParam Set<Include> theIncludes) {
|
||||
return ourBundleProvider;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,12 @@
|
|||
</action>
|
||||
<action type="add">
|
||||
Android JAR now includes servlet-API classes, as the project will not
|
||||
work without them.
|
||||
work without them. Thanks
|
||||
</action>
|
||||
<action type="fix" issue="116">
|
||||
Requested _include values are preserved across paging links when the
|
||||
server returns multiple pages. Thanks to Bill de Beaubien for
|
||||
reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.9" date="2015-Mar-14">
|
||||
|
|
Loading…
Reference in New Issue