Correctly populate Bundle.entry.fullUrl on Hl7OrgDstu2 servers
This commit is contained in:
parent
8a37ed3b59
commit
81dec23faf
|
@ -0,0 +1,120 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
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.dstu3.model.HumanName;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class SearchWithGenericListDstu3Test {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu3Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static String ourLastMethod;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourLastMethod = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See #291
|
||||
*/
|
||||
@Test
|
||||
public void testSearch() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("searchByIdentifier", ourLastMethod);
|
||||
assertThat(responseContent, containsString("<family value=\"FAMILY\""));
|
||||
assertThat(responseContent, containsString("<fullUrl value=\"http://localhost:" + ourPort + "/Patient/1\"/>"));
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Search()
|
||||
public List searchByIdentifier(
|
||||
@RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) {
|
||||
ourLastMethod = "searchByIdentifier";
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1"));
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -72,76 +72,78 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
IDomainResource next = (IDomainResource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IBaseResource nextContained : next.getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
IDomainResource next = (IDomainResource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IBaseResource nextContained : next.getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next,
|
||||
IBaseReference.class);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
|
||||
for (IBaseReference nextRef : references) {
|
||||
IBaseResource nextRes = (IBaseResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
for (IBaseReference nextRef : references) {
|
||||
IBaseResource nextRes = (IBaseResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IBaseResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IBaseResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource,
|
||||
IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
|
@ -230,8 +232,10 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
|
|||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
|
||||
// BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
|
||||
// BundleEntrySearchModeEnum searchMode =
|
||||
// ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
|
||||
// if (searchMode != null) {
|
||||
// entry.getSearch().getModeElement().setValue(searchMode.getCode());
|
||||
// }
|
||||
|
@ -352,8 +356,9 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(RestfulServerUtils.createPagingLink(theIncludes,
|
||||
theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT)
|
||||
.setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn,
|
||||
numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
|
|
|
@ -17,24 +17,25 @@ 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.HumanName;
|
||||
import org.hl7.fhir.instance.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class SearchWithGenericListHl7OrgDstu2Test {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListHl7OrgDstu2Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
@ -62,10 +63,13 @@ public class SearchWithGenericListHl7OrgDstu2Test {
|
|||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
}
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
|
@ -94,7 +98,7 @@ public class SearchWithGenericListHl7OrgDstu2Test {
|
|||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
|
@ -105,7 +109,7 @@ public class SearchWithGenericListHl7OrgDstu2Test {
|
|||
@RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) {
|
||||
ourLastMethod = "searchByIdentifier";
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
retVal.add((Patient) new Patient().addName(new HumanNameDt().addFamily("FAMILY")).setId("1"));
|
||||
retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1"));
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
|
|
@ -412,6 +412,10 @@
|
|||
Extensions were placed before any other content, which is incorrect (several
|
||||
elements come first: meta, text, etc.)
|
||||
</action>
|
||||
<action type="fix">
|
||||
In server implementations, the Bundle.entry.fullUrl was not getting correctly
|
||||
populated on Hl7OrgDstu2 servers. Thanks to Christian Ohr for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.4" date="2016-02-04">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue