Fix #750 - Elements are not preserved in page requests
This commit is contained in:
parent
2b4a492870
commit
59f4177a59
|
@ -43,7 +43,7 @@ public abstract class BaseParser implements IParser {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
|
||||
|
||||
private ContainedResources myContainedResources;
|
||||
|
||||
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||
private FhirContext myContext;
|
||||
private Set<String> myDontEncodeElements;
|
||||
private boolean myDontEncodeElementsIncludesStars;
|
||||
|
@ -556,6 +556,16 @@ public abstract class BaseParser implements IParser {
|
|||
&& theIncludedResource == false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEncodeElementsAppliesToChildResourcesOnly() {
|
||||
return myEncodeElementsAppliesToChildResourcesOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncodeElementsAppliesToChildResourcesOnly(boolean theEncodeElementsAppliesToChildResourcesOnly) {
|
||||
myEncodeElementsAppliesToChildResourcesOnly = theEncodeElementsAppliesToChildResourcesOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOmitResourceId() {
|
||||
return myOmitResourceId;
|
||||
|
@ -1039,7 +1049,13 @@ public abstract class BaseParser implements IParser {
|
|||
}
|
||||
|
||||
private boolean checkIfParentShouldBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
|
||||
return checkIfPathMatchesForEncoding(thePathBuilder, theStarPass, myEncodeElementsAppliesToResourceTypes, myEncodeElements, true);
|
||||
Set<String> encodeElements = myEncodeElements;
|
||||
if (encodeElements != null && encodeElements.isEmpty() == false) {
|
||||
if (isEncodeElementsAppliesToChildResourcesOnly() && !mySubResource) {
|
||||
encodeElements = null;
|
||||
}
|
||||
}
|
||||
return checkIfPathMatchesForEncoding(thePathBuilder, theStarPass, myEncodeElementsAppliesToResourceTypes, encodeElements, true);
|
||||
}
|
||||
|
||||
private boolean checkIfParentShouldNotBeEncodedAndBuildPath(StringBuilder thePathBuilder, boolean theStarPass) {
|
||||
|
@ -1058,6 +1074,9 @@ public abstract class BaseParser implements IParser {
|
|||
} else {
|
||||
thePathBuilder.append(myResDef.getName());
|
||||
}
|
||||
if (theElements == null) {
|
||||
return true;
|
||||
}
|
||||
if (theElements.contains(thePathBuilder.toString())) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -206,6 +206,22 @@ public interface IParser {
|
|||
*/
|
||||
void setEncodeElements(Set<String> theEncodeElements);
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the values supplied
|
||||
* to {@link #setEncodeElements(Set)} will not be applied to the root
|
||||
* resource (typically a Bundle), but will be applied to any sub-resources
|
||||
* contained within it (i.e. search result resources in that bundle)
|
||||
*/
|
||||
void setEncodeElementsAppliesToChildResourcesOnly(boolean theEncodeElementsAppliesToChildResourcesOnly);
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the values supplied
|
||||
* to {@link #setEncodeElements(Set)} will not be applied to the root
|
||||
* resource (typically a Bundle), but will be applied to any sub-resources
|
||||
* contained within it (i.e. search result resources in that bundle)
|
||||
*/
|
||||
boolean isEncodeElementsAppliesToChildResourcesOnly();
|
||||
|
||||
/**
|
||||
* If provided, tells the parse which resource types to apply {@link #setEncodeElements(Set) encode elements} to. Any
|
||||
* resource types not specified here will be encoded completely, with no elements excluded.
|
||||
|
|
|
@ -19,11 +19,12 @@ public class BinaryUtil {
|
|||
public static IBaseReference getSecurityContext(FhirContext theCtx, IBaseBinary theBinary) {
|
||||
RuntimeResourceDefinition def = theCtx.getResourceDefinition("Binary");
|
||||
BaseRuntimeChildDefinition child = def.getChildByName("securityContext");
|
||||
|
||||
List<IBase> values = child.getAccessor().getValues(theBinary);
|
||||
IBaseReference retVal = null;
|
||||
if (values.size() > 0) {
|
||||
retVal = (IBaseReference) values.get(0);
|
||||
if (child != null) {
|
||||
List<IBase> values = child.getAccessor().getValues(theBinary);
|
||||
if (values.size() > 0) {
|
||||
retVal = (IBaseReference) values.get(0);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -86,10 +86,41 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
if (elements != null && elements.size() > 0) {
|
||||
Set<String> newElements = new HashSet<String>();
|
||||
Set<String> newElements = new HashSet<>();
|
||||
for (String next : elements) {
|
||||
newElements.add("*." + next);
|
||||
}
|
||||
|
||||
/*
|
||||
* We try to be smart about what the user is asking for
|
||||
* when they include an _elements parameter. If we're responding
|
||||
* to something that returns a Bundle (e.g. a search) we assume
|
||||
* the elements don't apply to the Bundle itself, unless
|
||||
* the client has explicitly scoped the Bundle
|
||||
* (i.e. with Bundle.total or something like that)
|
||||
*/
|
||||
switch (theRequestDetails.getRestOperationType()) {
|
||||
case SEARCH_SYSTEM:
|
||||
case SEARCH_TYPE:
|
||||
case HISTORY_SYSTEM:
|
||||
case HISTORY_TYPE:
|
||||
case HISTORY_INSTANCE:
|
||||
case GET_PAGE:
|
||||
boolean haveExplicitBundleElement = false;
|
||||
for (String next : newElements) {
|
||||
if (next.startsWith("Bundle.")) {
|
||||
haveExplicitBundleElement = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!haveExplicitBundleElement) {
|
||||
parser.setEncodeElementsAppliesToChildResourcesOnly(true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
parser.setEncodeElements(newElements);
|
||||
parser.setEncodeElementsAppliesToResourceTypes(elementsAppliesTo);
|
||||
}
|
||||
|
@ -147,6 +178,19 @@ public class RestfulServerUtils {
|
|||
b.append(theBundleType.getCode());
|
||||
}
|
||||
|
||||
String paramName = Constants.PARAM_ELEMENTS;
|
||||
String[] params = theRequestParameters.get(paramName);
|
||||
if (params != null) {
|
||||
for (String nextValue : params) {
|
||||
if (isNotBlank(nextValue)) {
|
||||
b.append('&');
|
||||
b.append(paramName);
|
||||
b.append('=');
|
||||
b.append(UrlUtil.escape(nextValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("UTF-8 not supported", e);// should not happen
|
||||
|
@ -587,9 +631,11 @@ public class RestfulServerUtils {
|
|||
response.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;");
|
||||
|
||||
IBaseReference securityContext = BinaryUtil.getSecurityContext(theServer.getFhirContext(), bin);
|
||||
String securityContextRef = securityContext.getReferenceElement().getValue();
|
||||
if (isNotBlank(securityContextRef)) {
|
||||
response.addHeader(Constants.HEADER_X_SECURITY_CONTEXT, securityContextRef);
|
||||
if (securityContext != null) {
|
||||
String securityContextRef = securityContext.getReferenceElement().getValue();
|
||||
if (isNotBlank(securityContextRef)) {
|
||||
response.addHeader(Constants.HEADER_X_SECURITY_CONTEXT, securityContextRef);
|
||||
}
|
||||
}
|
||||
|
||||
return response.sendAttachmentResponse(bin, theStausCode, contentType);
|
||||
|
|
|
@ -332,7 +332,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
}
|
||||
|
||||
bundleFactory.addRootPropertiesToBundle(theResult.getUuid(), serverBase, theLinkSelf, linkPrev, linkNext, theResult.size(), theBundleType, theResult.getPublished());
|
||||
bundleFactory.addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, serverBase, theServer.getBundleInclusionRule(), theIncludes);
|
||||
bundleFactory.addResourcesToBundle(new ArrayList<>(resourceList), theBundleType, serverBase, theServer.getBundleInclusionRule(), theIncludes);
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
int limit;
|
||||
|
|
|
@ -1499,7 +1499,7 @@ public class JsonParserDstu2_1Test {
|
|||
|
||||
String val = ourCtx.newJsonParser().encodeResourceToString(patient);
|
||||
|
||||
String expected = "{\"resourceType\":\"Binary\",\"id\":\"11\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}";
|
||||
String expected = "{\"resourceType\":\"Binary\",\"id\":\"11\",\"meta\":{\"versionId\":\"22\"},\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}";
|
||||
ourLog.info("Expected: {}", expected);
|
||||
ourLog.info("Actual : {}", val);
|
||||
assertEquals(expected, val);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,32 +1,5 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.junit.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
|
@ -38,15 +11,46 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
|||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.*;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SearchR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchR4Test.class);
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private static TokenAndListParam ourIdentifiers;
|
||||
private static String ourLastMethod;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchR4Test.class);
|
||||
private static int ourPort;
|
||||
|
||||
private static Server ourServer;
|
||||
|
@ -57,74 +61,63 @@ public class SearchR4Test {
|
|||
ourIdentifiers = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchNormal() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
|
||||
private Bundle executeAndReturnLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException, ClientProtocolException {
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
Bundle bundle;
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("search", ourLastMethod);
|
||||
|
||||
assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem());
|
||||
assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue());
|
||||
EncodingEnum ct = EncodingEnum.forContentType(status.getEntity().getContentType().getValue().replaceAll(";.*", "").trim());
|
||||
assertEquals(theExpectEncoding, ct);
|
||||
bundle = ct.newParser(ourCtx).parseResource(Bundle.class, responseContent);
|
||||
assertEquals(10, bundle.getEntry().size());
|
||||
String linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertNotNull(linkNext);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidChain() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
OperationOutcome oo = (OperationOutcome) ourCtx.newJsonParser().parseResource(responseContent);
|
||||
assertEquals(
|
||||
"Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)",
|
||||
oo.getIssueFirstRep().getDiagnostics());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPagingPreservesEncodingJson() throws Exception {
|
||||
public void testPagingPreservesElements() throws Exception {
|
||||
HttpGet httpGet;
|
||||
String linkNext;
|
||||
Bundle bundle;
|
||||
String linkSelf;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json");
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_elements=name");
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), not(containsString("active")));
|
||||
linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl();
|
||||
assertThat(linkSelf, containsString("_elements=name"));
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
assertThat(linkNext, containsString("_elements=name"));
|
||||
|
||||
ourLog.info(toJson(bundle));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), not(containsString("active")));
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
assertThat(linkNext, containsString("_elements=name"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), not(containsString("active")));
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
assertThat(linkNext, containsString("_elements=name"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), not(containsString("active")));
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
assertThat(linkNext, containsString("_elements=name"));
|
||||
|
||||
}
|
||||
|
||||
|
@ -161,34 +154,35 @@ public class SearchR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testPagingPreservesEncodingXml() throws Exception {
|
||||
public void testPagingPreservesEncodingJson() throws Exception {
|
||||
HttpGet httpGet;
|
||||
String linkNext;
|
||||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=xml");
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json");
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
assertThat(toJson(bundle), containsString("active"));
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
}
|
||||
|
||||
|
@ -260,26 +254,76 @@ public class SearchR4Test {
|
|||
|
||||
}
|
||||
|
||||
private Bundle executeAndReturnLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException, ClientProtocolException {
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
@Test
|
||||
public void testPagingPreservesEncodingXml() throws Exception {
|
||||
HttpGet httpGet;
|
||||
String linkNext;
|
||||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=xml");
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=xml"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchNormal() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
EncodingEnum ct = EncodingEnum.forContentType(status.getEntity().getContentType().getValue().replaceAll(";.*", "").trim());
|
||||
assertEquals(theExpectEncoding, ct);
|
||||
bundle = ct.newParser(ourCtx).parseResource(Bundle.class, responseContent);
|
||||
assertEquals(10, bundle.getEntry().size());
|
||||
String linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertNotNull(linkNext);
|
||||
|
||||
assertEquals("search", ourLastMethod);
|
||||
|
||||
assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem());
|
||||
assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
return bundle;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidChain() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
OperationOutcome oo = (OperationOutcome) ourCtx.newJsonParser().parseResource(responseContent);
|
||||
assertEquals(
|
||||
"Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)",
|
||||
oo.getIssueFirstRep().getDiagnostics());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithPostAndInvalidParameters() throws Exception {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||
|
@ -293,14 +337,14 @@ public class SearchR4Test {
|
|||
client.registerInterceptor(interceptor);
|
||||
try {
|
||||
client
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.prettyPrint()
|
||||
.usingStyle(SearchStyleEnum.POST)
|
||||
.returnBundle(org.hl7.fhir.r4.model.Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.prettyPrint()
|
||||
.usingStyle(SearchStyleEnum.POST)
|
||||
.returnBundle(org.hl7.fhir.r4.model.Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Invalid request: The FHIR endpoint on this server does not know how to handle POST operation[Patient/_search] with parameters [[_pretty, foo]]"));
|
||||
|
@ -308,6 +352,10 @@ public class SearchR4Test {
|
|||
|
||||
}
|
||||
|
||||
private String toJson(Bundle theBundle) {
|
||||
return ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theBundle);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
|
@ -349,16 +397,17 @@ public class SearchR4Test {
|
|||
@SuppressWarnings("rawtypes")
|
||||
@Search()
|
||||
public List search(
|
||||
@RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
|
||||
@RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
|
||||
ourLastMethod = "search";
|
||||
ourIdentifiers = theIdentifiers;
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
|
||||
|
||||
for (int i = 0; i < 200; i++) {
|
||||
Patient patient = new Patient();
|
||||
patient.addName(new HumanName().setFamily("FAMILY"));
|
||||
patient.setActive(true);
|
||||
patient.getIdElement().setValue("Patient/" + i);
|
||||
retVal.add((Patient) patient);
|
||||
retVal.add(patient);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -175,11 +175,26 @@
|
|||
was not encoded correctly. Thanks to Malcolm McRoberts for the pull
|
||||
request with fix and test case!
|
||||
</action>
|
||||
<action type="add">
|
||||
Bundle resources did not have their version encoded when serializing
|
||||
in FHIR resource (XML/JSON) format.
|
||||
</action>
|
||||
<action type="add">
|
||||
The Binary resource endpoint now supports the `X-Security-Context` header when
|
||||
reading or writing Binary contents using their native Content-Type (i.e exchanging
|
||||
the raw binary with the server, as opposed to exchanging a FHIR resource).
|
||||
</action>
|
||||
<action type="fix">
|
||||
When paging through multiple pages of search results, if the
|
||||
client had requested a subset of resources to be returned using the
|
||||
<![CDATA[<code>_elements</code>]]> parameter, the elements list
|
||||
was lost after the first page of results.
|
||||
In addition, elements will not remove elements from
|
||||
search/history Bundles (i.e. elements from the Bundle itself, as opposed
|
||||
to elements in the entry resources) unless the Bundle elements are
|
||||
explicitly listed, e.g. <![CDATA[<code>_include=Bundle.total</code>]]>.
|
||||
Thanks to @parisni for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.0.0" date="2017-09-27">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue