Allow fluent client to handle return types other than Parameters when

invoking operations
This commit is contained in:
James Agnew 2016-10-07 11:29:53 -04:00
parent 5384af1004
commit 2c4139dc82
10 changed files with 2891 additions and 61 deletions

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.client;
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -243,7 +243,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
return delete(theType, new IdDt(theId)); return delete(theType, new IdDt(theId));
} }
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint, SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) { private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint,
SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements) {
String resName = toResourceName(theType); String resName = toResourceName(theType);
IIdType id = theId; IIdType id = theId;
if (!id.hasBaseUrl()) { if (!id.hasBaseUrl()) {
@ -394,8 +395,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
/** /**
* @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your * @deprecated Use {@link LoggingInterceptor} as a client interceptor registered to your
* client instead, as this provides much more fine-grained control over what is logged. This * client instead, as this provides much more fine-grained control over what is logged. This
* method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16) * method will be removed at some point (deprecated in HAPI 1.6 - 2016-06-16)
*/ */
@Deprecated @Deprecated
public boolean isLogRequestAndResponse() { public boolean isLogRequestAndResponse() {
@ -526,7 +527,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new ArrayList<IBaseResource>(resp.toListOfResources()); return new ArrayList<IBaseResource>(resp.toListOfResources());
} }
@Override @Override
public IPatch patch() { public IPatch patch() {
return new PatchInternal(); return new PatchInternal();
@ -1483,6 +1483,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private RuntimeResourceDefinition myParametersDef; private RuntimeResourceDefinition myParametersDef;
private Class<? extends IBaseResource> myType; private Class<? extends IBaseResource> myType;
private boolean myUseHttpGet; private boolean myUseHttpGet;
private Class myReturnResourceType;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void addParam(String theName, IBase theValue) { private void addParam(String theName, IBase theValue) {
@ -1552,25 +1553,33 @@ public class GenericClient extends BaseClient implements IGenericClient {
BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet); BaseHttpClientInvocation invocation = OperationMethodBinding.createOperationInvocation(myContext, resourceName, id, myOperationName, myParameters, myUseHttpGet);
ResourceResponseHandler handler = new ResourceResponseHandler(); if (myReturnResourceType != null) {
handler.setPreferResponseTypes(getPreferResponseTypes(myType)); ResourceResponseHandler handler;
handler = new ResourceResponseHandler(myReturnResourceType);
Object retVal = invoke(null, handler, invocation); Object retVal = invoke(null, handler, invocation);
if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) {
return retVal; return retVal;
} else { } else {
RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters"); ResourceResponseHandler handler;
IBaseResource parameters = def.newInstance(); handler = new ResourceResponseHandler();
handler.setPreferResponseTypes(getPreferResponseTypes(myType));
BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter"); Object retVal = invoke(null, handler, invocation);
BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter"); if (myContext.getResourceDefinition((IBaseResource) retVal).getName().equals("Parameters")) {
IBase parameter = paramChildElem.newInstance(); return retVal;
paramChild.getMutator().addValue(parameters, parameter); } else {
RuntimeResourceDefinition def = myContext.getResourceDefinition("Parameters");
IBaseResource parameters = def.newInstance();
BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource"); BaseRuntimeChildDefinition paramChild = def.getChildByName("parameter");
resourceElem.getMutator().addValue(parameter, (IBase) retVal); BaseRuntimeElementCompositeDefinition<?> paramChildElem = (BaseRuntimeElementCompositeDefinition<?>) paramChild.getChildByName("parameter");
IBase parameter = paramChildElem.newInstance();
paramChild.getMutator().addValue(parameters, parameter);
return parameters; BaseRuntimeChildDefinition resourceElem = paramChildElem.getChildByName("resource");
resourceElem.getMutator().addValue(parameter, (IBase) retVal);
return parameters;
}
} }
} }
@ -1613,7 +1622,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName()); throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type: " + theOutputParameterType.getName());
} }
if (!"Parameters".equals(def.getName())) { if (!"Parameters".equals(def.getName())) {
throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName() + " is a resource named: " + def.getName()); throw new IllegalArgumentException("theOutputParameterType must refer to a HAPI FHIR Resource type for a resource named " + "Parameters" + " - " + theOutputParameterType.getName()
+ " is a resource named: " + def.getName());
} }
myParameters = (IBaseParameters) def.newInstance(); myParameters = (IBaseParameters) def.newInstance();
return this; return this;
@ -1657,12 +1667,21 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this; return this;
} }
@Override
public IOperationUntypedWithInput returnResourceType(Class theReturnType) {
Validate.notNull(theReturnType, "theReturnType must not be null");
Validate.isTrue(IBaseResource.class.isAssignableFrom(theReturnType), "theReturnType must be a class which extends from IBaseResource");
myReturnResourceType = theReturnType;
return this;
}
} }
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<IBaseOperationOutcome> { private final class OperationOutcomeResponseHandler implements IClientResponseHandler<IBaseOperationOutcome> {
@Override @Override
public IBaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException { public IBaseOperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
throws BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType); EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) { if (respType == null) {
return null; return null;
@ -1858,7 +1877,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws BaseServerResponseException { public List<IBaseResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
throws BaseServerResponseException {
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) { if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass(); Class<? extends IBaseResource> bundleType = myContext.getResourceDefinition("Bundle").getImplementingClass();
ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType); ResourceResponseHandler<IBaseResource> handler = new ResourceResponseHandler<IBaseResource>((Class<IBaseResource>) bundleType);
@ -2011,7 +2031,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) { if (myReturnBundleType == null && myContext.getVersion().getVersion().isRi()) {
throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify " + "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method"); throw new IllegalArgumentException("When using the client with HL7.org structures, you must specify "
+ "the bundle return type for the client by adding \".returnBundle(org.hl7.fhir.instance.model.Bundle.class)\" to your search method call before the \".execute()\" method");
} }
IClientResponseHandler<? extends IBase> binding; IClientResponseHandler<? extends IBase> binding;
@ -2212,7 +2233,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class StringResponseHandler implements IClientResponseHandler<String> { private final class StringResponseHandler implements IClientResponseHandler<String> {
@Override @Override
public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders)
throws IOException, BaseServerResponseException {
return IOUtils.toString(theResponseReader); return IOUtils.toString(theResponseReader);
} }
} }
@ -2367,7 +2389,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
throw new InvalidRequestException("No patch body supplied, cannot invoke server"); throw new InvalidRequestException("No patch body supplied, cannot invoke server");
} }
if (myId == null) { if (myId == null) {
myId = myResource.getIdElement(); myId = myResource.getIdElement();
} }

View File

@ -48,6 +48,17 @@ public interface IOperationUntyped {
* Use chained method calls to construct a Parameters input. This form is a convenience * Use chained method calls to construct a Parameters input. This form is a convenience
* in order to allow simple method chaining to be used to build up a parameters * in order to allow simple method chaining to be used to build up a parameters
* resource for the input of an operation without needing to manually construct one. * resource for the input of an operation without needing to manually construct one.
* <p>
* A sample invocation of this class could look like:<br/>
* <pre>Bundle bundle = client.operation()
* .onInstance(new IdType("Patient/A161443"))
* .named("everything")
* .withParameter(Parameters.class, "_count", new IntegerType(50))
* .useHttpGet()
* .returnResourceType(Bundle.class)
* .execute();
* </pre>
* </p>
* *
* @param theParameterType The type to use for the output parameters (this should be set to * @param theParameterType The type to use for the output parameters (this should be set to
* <code>Parameters.class</code> drawn from the version of the FHIR structures you are using) * <code>Parameters.class</code> drawn from the version of the FHIR structures you are using)

View File

@ -1,28 +1,8 @@
package ca.uhn.fhir.rest.gclient; package ca.uhn.fhir.rest.gclient;
/* import org.hl7.fhir.instance.model.api.IBaseResource;
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IBaseParameters; public interface IOperationUntypedWithInput<T> extends IClientExecutable<IOperationUntypedWithInput<T>, T> {
public interface IOperationUntypedWithInput<T extends IBaseParameters> extends IClientExecutable<IOperationUntypedWithInput<T>, T> {
/** /**
* The client should invoke this method using an HTTP GET instead of an HTTP POST. Note that * The client should invoke this method using an HTTP GET instead of an HTTP POST. Note that
@ -35,4 +15,12 @@ public interface IOperationUntypedWithInput<T extends IBaseParameters> extends I
*/ */
IOperationUntypedWithInput<T> useHttpGet(); IOperationUntypedWithInput<T> useHttpGet();
/**
* If this operation returns a single resource body as its return type instead of a <code>Parameters</code>
* resource, use this method to specify that resource type. This is useful for certain
* operations (e.g. <code>Patient/NNN/$everything</code>) which return a bundle instead of
* a Parameters resource.
*/
<R extends IBaseResource> IOperationUntypedWithInput<R> returnResourceType(Class<R> theReturnType);
} }

View File

@ -27,7 +27,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@ -38,12 +37,8 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean; import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvc; import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
@Configuration @Configuration
@EnableScheduling @EnableScheduling
@ -64,11 +59,10 @@ public class BaseConfig implements SchedulingConfigurer {
@Bean(autowire = Autowire.BY_TYPE) @Bean(autowire = Autowire.BY_TYPE)
public DatabaseBackedPagingProvider databaseBackedPagingProvider() { public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
return new DatabaseBackedPagingProvider(10); DatabaseBackedPagingProvider retVal = new DatabaseBackedPagingProvider(10);
return retVal;
} }
@Bean(autowire=Autowire.BY_TYPE) @Bean(autowire=Autowire.BY_TYPE)
public StaleSearchDeletingSvc staleSearchDeletingSvc() { public StaleSearchDeletingSvc staleSearchDeletingSvc() {
return new StaleSearchDeletingSvc(); return new StaleSearchDeletingSvc();

View File

@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -25,6 +26,9 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hibernate.boot.model.source.spi.IdentifierSourceSimple; import org.hibernate.boot.model.source.spi.IdentifierSourceSimple;
import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem; import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
@ -32,6 +36,7 @@ import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -41,6 +46,7 @@ import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
@ -122,6 +128,54 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertThat(ids, contains(moId.getValue())); assertThat(ids, contains(moId.getValue()));
} }
/**
* Per message from David Hay on Skype
*/
@Test
public void testEverythingWithLargeSet() throws Exception {
String inputString = IOUtils.toString(getClass().getResourceAsStream("/david_big_bundle.json"), StandardCharsets.UTF_8);
Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString);
inputBundle.setType(BundleType.TRANSACTION);
Set<String> allIds = new TreeSet<String>();
for (BundleEntryComponent nextEntry : inputBundle.getEntry()) {
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
nextEntry.getRequest().setUrl(nextEntry.getResource().getId());
allIds.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
mySystemDao.transaction(mySrd, inputBundle);
SearchParameterMap map = new SearchParameterMap();
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
IPrimitiveType<Integer> count = new IntegerType(1000);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, mySrd);
TreeSet<String> ids = new TreeSet<String>(toUnqualifiedVersionlessIdValues(everything));
assertThat(ids, hasItem("List/A161444"));
assertThat(ids, hasItem("List/A161468"));
assertThat(ids, hasItem("List/A161500"));
ourLog.info("Expected {} - {}", allIds.size(), allIds);
ourLog.info("Actual {} - {}", ids.size(), ids);
assertEquals(allIds, ids);
ids = new TreeSet<String>();
for (int i = 0; i < everything.size(); i++) {
for (IBaseResource next : everything.getResources(i, i+1)) {
ids.add(next.getIdElement().toUnqualifiedVersionless().getValue());
}
}
assertThat(ids, hasItem("List/A161444"));
assertThat(ids, hasItem("List/A161468"));
assertThat(ids, hasItem("List/A161500"));
ourLog.info("Expected {} - {}", allIds.size(), allIds);
ourLog.info("Actual {} - {}", ids.size(), ids);
assertEquals(allIds, ids);
}
@Test @Test
public void testCodeSearch() { public void testCodeSearch() {
Subscription subs = new Subscription(); Subscription subs = new Subscription();

View File

@ -7,6 +7,7 @@ import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
@ -28,9 +29,7 @@ import java.net.Socket;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.*;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -57,12 +56,15 @@ import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
@ -73,6 +75,7 @@ import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
@ -115,6 +118,62 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} }
/**
* Per message from David Hay on Skype
*/
@Test
public void testEverythingWithLargeSet() throws Exception {
String inputString = IOUtils.toString(getClass().getResourceAsStream("/david_big_bundle.json"), StandardCharsets.UTF_8);
Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString);
inputBundle.setType(BundleType.TRANSACTION);
Set<String> allIds = new TreeSet<String>();
for (BundleEntryComponent nextEntry : inputBundle.getEntry()) {
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
nextEntry.getRequest().setUrl(nextEntry.getResource().getId());
allIds.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
mySystemDao.transaction(mySrd, inputBundle);
Bundle responseBundle = ourClient
.operation()
.onInstance(new IdType("Patient/A161443"))
.named("everything")
.withParameter(Parameters.class, "_count", new IntegerType(50))
.useHttpGet()
.returnResourceType(Bundle.class)
.execute();
TreeSet<String> ids = new TreeSet<String>();
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
}
String nextUrl = responseBundle.getLink("next").getUrl();
responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
}
assertThat(ids, hasItem("List/A161444"));
assertThat(ids, hasItem("List/A161468"));
assertThat(ids, hasItem("List/A161500"));
assertEquals(null, responseBundle.getLink("next"));
ourLog.info("Expected {} - {}", allIds.size(), allIds);
ourLog.info("Actual {} - {}", ids.size(), ids);
assertEquals(allIds, ids);
}
/** /**
* See #411 * See #411
* *

View File

@ -251,7 +251,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
HttpGet get = new HttpGet(ourServerBase); HttpGet get = new HttpGet(ourServerBase);
// get.addHeader("Accept", "application/xml, text/html"); // get.addHeader("Accept", "application/xml, text/html");
CloseableHttpResponse http = ourHttpClient.execute(get); CloseableHttpResponse http = ourHttpClient.execute(get);
assertThat(http.getFirstHeader("Content-Type").getValue(), containsString("application/json+fhir")); assertThat(http.getFirstHeader("Content-Type").getValue(), containsString("application/fhir+json"));
} }

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3; import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
@ -50,9 +51,20 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
retVal.setAllowExternalReferences(true); retVal.setAllowExternalReferences(true);
retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu3"); retVal.getTreatBaseUrlsAsLocal().add("http://fhirtest.uhn.ca/baseDstu3");
retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu3"); retVal.getTreatBaseUrlsAsLocal().add("https://fhirtest.uhn.ca/baseDstu3");
retVal.setHardSearchLimit(500);
return retVal; return retVal;
} }
@Override
@Bean(autowire = Autowire.BY_TYPE)
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
DatabaseBackedPagingProvider retVal = super.databaseBackedPagingProvider();
retVal.setDefaultPageSize(20);
retVal.setMaximumPageSize(500);
return retVal;
}
@Bean @Bean
public IServerInterceptor securityInterceptor() { public IServerInterceptor securityInterceptor() {
return new PublicSecurityInterceptor(); return new PublicSecurityInterceptor();

View File

@ -158,6 +158,10 @@
interceptors are accessing the parameters and there is are also interceptors are accessing the parameters and there is are also
parameters on the URL. Thanks to Jim Steel for reporting! parameters on the URL. Thanks to Jim Steel for reporting!
</action> </action>
<action type="add">
Fluent client can now return types other than Parameters
when invoking operations.
</action>
</release> </release>
<release version="2.0" date="2016-08-30"> <release version="2.0" date="2016-08-30">
<action type="fix"> <action type="fix">