mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-09 14:33:32 +00:00
Fix #299 - Don't crash if the client receives extensions in Bundle.entry.search
This commit is contained in:
parent
c7d3f39457
commit
ed5bffba9e
@ -2117,16 +2117,19 @@ class ParserState<T> {
|
||||
public PreResourceStateHapi(BundleEntry theEntry, Class<? extends IBaseResource> theResourceType) {
|
||||
super(theResourceType);
|
||||
myEntry = theEntry;
|
||||
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
|
||||
}
|
||||
|
||||
public PreResourceStateHapi(Class<? extends IBaseResource> theResourceType) {
|
||||
super(theResourceType);
|
||||
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
|
||||
}
|
||||
|
||||
public PreResourceStateHapi(Object theTarget, IMutator theMutator, Class<? extends IBaseResource> theResourceType) {
|
||||
super(theResourceType);
|
||||
myTarget = theTarget;
|
||||
myMutator = theMutator;
|
||||
assert theResourceType == null || IResource.class.isAssignableFrom(theResourceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,21 +25,23 @@ import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
@ -62,6 +64,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
@ -157,32 +160,40 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE: {
|
||||
Bundle bundle;
|
||||
if (myResourceType != null) {
|
||||
bundle = parser.parseBundle(myResourceType, theResponseReader);
|
||||
|
||||
Bundle dstu1bundle = null;
|
||||
IBaseBundle dstu2bundle = null;
|
||||
List<IBaseResource> listOfResources = null;
|
||||
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE || getContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
if (myResourceType != null) {
|
||||
dstu1bundle = parser.parseBundle(myResourceType, theResponseReader);
|
||||
} else {
|
||||
dstu1bundle = parser.parseBundle(theResponseReader);
|
||||
}
|
||||
} else {
|
||||
bundle = parser.parseBundle(theResponseReader);
|
||||
Class<? extends IBaseResource> type = getContext().getResourceDefinition("Bundle").getImplementingClass();
|
||||
dstu2bundle = (IBaseBundle) parser.parseResource(type, theResponseReader);
|
||||
listOfResources = BundleUtil.toListOfResources(getContext(), dstu2bundle);
|
||||
}
|
||||
|
||||
switch (getMethodReturnType()) {
|
||||
case BUNDLE:
|
||||
return bundle;
|
||||
return dstu1bundle;
|
||||
case BUNDLE_RESOURCE:
|
||||
return dstu2bundle;
|
||||
case LIST_OF_RESOURCES:
|
||||
List<IResource> listOfResources;
|
||||
if (myResourceListCollectionType != null) {
|
||||
listOfResources = new ArrayList<IResource>();
|
||||
for (IResource next : bundle.toListOfResources()) {
|
||||
for (Iterator<IBaseResource> iter = listOfResources.iterator(); iter.hasNext(); ) {
|
||||
IBaseResource next = iter.next();
|
||||
if (!myResourceListCollectionType.isAssignableFrom(next.getClass())) {
|
||||
ourLog.debug("Not returning resource of type {} because it is not a subclass or instance of {}", next.getClass(), myResourceListCollectionType);
|
||||
continue;
|
||||
iter.remove();
|
||||
}
|
||||
listOfResources.add(next);
|
||||
}
|
||||
} else {
|
||||
listOfResources = bundle.toListOfResources();
|
||||
}
|
||||
return listOfResources;
|
||||
case RESOURCE:
|
||||
List<IResource> list = bundle.toListOfResources();
|
||||
List<IResource> list = dstu1bundle.toListOfResources();
|
||||
if (list.size() == 0) {
|
||||
return null;
|
||||
} else if (list.size() == 1) {
|
||||
|
@ -37,6 +37,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
@ -130,7 +131,11 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
|
||||
@Override
|
||||
public ReturnTypeEnum getReturnType() {
|
||||
return ReturnTypeEnum.BUNDLE;
|
||||
// if (getContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
return ReturnTypeEnum.BUNDLE;
|
||||
// } else {
|
||||
// return ReturnTypeEnum.RESOURCE;
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,41 @@
|
||||
package ca.uhn.fhir.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
|
||||
/**
|
||||
* Fetch resources from a bundle
|
||||
*/
|
||||
public class BundleUtil {
|
||||
|
||||
/**
|
||||
* Extract all of the resources from a given bundle
|
||||
*/
|
||||
public static List<IBaseResource> toListOfResources(FhirContext theContext, IBaseBundle theBundle) {
|
||||
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
|
||||
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
|
||||
List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry");
|
||||
BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
|
||||
for (IBase nextEntry : entries) {
|
||||
for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) {
|
||||
retVal.add((IBaseResource) next);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ -27,10 +26,8 @@ import org.mockito.stubbing.Answer;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
@ -39,7 +36,6 @@ public class ClientWithProfileDstu2Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientWithProfileDstu2Test.class);
|
||||
private FhirContext ourCtx;
|
||||
private HttpClient ourHttpClient;
|
||||
|
||||
private HttpResponse ourHttpResponse;
|
||||
|
||||
@Before
|
||||
|
@ -0,0 +1,156 @@
|
||||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.input.ReaderInputStream;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.dstu3.model.Extension;
|
||||
import org.hl7.fhir.dstu3.model.Location;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
|
||||
public class SearchClientDstu3Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchClientDstu3Test.class);
|
||||
private FhirContext ourCtx;
|
||||
private HttpClient ourHttpClient;
|
||||
private HttpResponse ourHttpResponse;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourCtx = FhirContext.forDstu3();
|
||||
|
||||
ourHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
|
||||
ourCtx.getRestfulClientFactory().setHttpClient(ourHttpClient);
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
|
||||
ourHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #299
|
||||
*/
|
||||
@Test
|
||||
public void testListResponseWithSearchExtension() throws Exception {
|
||||
|
||||
final String response = createBundleWithSearchExtension();
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
|
||||
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost:8081/hapi-fhir/fhir");
|
||||
|
||||
List<Location> matches = client.getMatches(new StringParam("smith"), 100);
|
||||
assertEquals(1, matches.size());
|
||||
assertEquals("Sample Clinic", matches.get(0).getName());
|
||||
|
||||
HttpGet value = (HttpGet) capt.getValue();
|
||||
assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #299
|
||||
*/
|
||||
@Test
|
||||
public void testBundleResponseWithSearchExtension() throws Exception {
|
||||
|
||||
final String response = createBundleWithSearchExtension();
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse);
|
||||
when(ourHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(ourHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(ourHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(response), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
ILocationClient client = ourCtx.newRestfulClient(ILocationClient.class, "http://localhost:8081/hapi-fhir/fhir");
|
||||
|
||||
Bundle matches = client.getMatchesReturnBundle(new StringParam("smith"), 100);
|
||||
|
||||
assertEquals(1, matches.getEntry().size());
|
||||
BundleEntryComponent entry = matches.getEntry().get(0);
|
||||
assertEquals("Sample Clinic", ((Location)entry.getResource()).getName());
|
||||
|
||||
List<Extension> ext = entry.getSearch().getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/algorithmic-match");
|
||||
assertEquals(1, ext.size());
|
||||
|
||||
HttpGet value = (HttpGet) capt.getValue();
|
||||
assertEquals("http://localhost:8081/hapi-fhir/fhir/Location?_query=match&name=smith&_count=100", value.getURI().toString());
|
||||
}
|
||||
|
||||
private String createBundleWithSearchExtension() {
|
||||
//@formatter:off
|
||||
final String response = "<Bundle xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<id value=\"f61f6ddc-95e8-4ef9-a4cd-17c79bbb74f3\"></id>"
|
||||
+ "<meta><lastUpdated value=\"2016-02-19T12:04:02.616-05:00\"></lastUpdated></meta>"
|
||||
+ "<type value=\"searchset\"></type>"
|
||||
+ "<link><relation value=\"self\"></relation><url value=\"http://localhost:8081/hapi-fhir/fhir/Location?name=Sample+Clinic&_query=match\"></url></link>"
|
||||
+ "<entry>"
|
||||
+ "<resource>"
|
||||
+ "<Location xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<id value=\"1\"></id>"
|
||||
+ "<name value=\"Sample Clinic\"></name>"
|
||||
+ "</Location>"
|
||||
+ "</resource>"
|
||||
+ "<search>"
|
||||
+ "<extension url=\"http://hl7.org/fhir/StructureDefinition/algorithmic-match\">"
|
||||
+ "<valueCode value=\"probable\"></valueCode>"
|
||||
+ "</extension>"
|
||||
+ "<score value=\"0.8000000000000000444089209850062616169452667236328125\">"
|
||||
+ "</score>"
|
||||
+ "</search>"
|
||||
+ "</entry>"
|
||||
+ "</Bundle>";
|
||||
//@formatter:on
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public interface ILocationClient extends IRestfulClient {
|
||||
@Search(queryName = "match")
|
||||
public List<Location> getMatches(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
|
||||
|
||||
@Search(queryName = "match", type=Location.class)
|
||||
public Bundle getMatchesReturnBundle(final @RequiredParam(name = Location.SP_NAME) StringParam name, final @Count Integer count);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user