Clean up handling of searches nested in batch and transaction
This commit is contained in:
parent
28a5b92fe2
commit
c9fcef0372
|
@ -1744,9 +1744,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
|
||||
if (tagList != null) {
|
||||
tag = tagList.getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
|
||||
totalMetaCount += tagList.size();
|
||||
}
|
||||
List<IdDt> profileList = ResourceMetadataKeyEnum.PROFILES.get(res);
|
||||
if (profileList != null) {
|
||||
totalMetaCount += profileList.size();
|
||||
}
|
||||
totalMetaCount += tagList.size();
|
||||
totalMetaCount += ResourceMetadataKeyEnum.PROFILES.get(res).size();
|
||||
} else {
|
||||
IAnyResource res = (IAnyResource) theResource;
|
||||
tag = res.getMeta().getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
|
||||
|
|
|
@ -29,6 +29,7 @@ import javax.annotation.PostConstruct;
|
|||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -926,9 +927,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
|
||||
|
||||
if (theRequestDetails.isSubRequest()) {
|
||||
theParams.setLoadSynchronous(true);
|
||||
int max = myDaoConfig.getMaximumSearchResultCountInTransaction();
|
||||
theParams.setLoadSynchronousUpTo(myDaoConfig.getMaximumSearchResultCountInTransaction());
|
||||
Integer max = myDaoConfig.getMaximumSearchResultCountInTransaction();
|
||||
if (max != null) {
|
||||
Validate.inclusiveBetween(1, Integer.MAX_VALUE, max.intValue(), "Maximum search result count in transaction ust be a positive integer");
|
||||
theParams.setLoadSynchronousUpTo(myDaoConfig.getMaximumSearchResultCountInTransaction());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPagingProviderDatabaseBacked(theRequestDetails)) {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class DaoConfig {
|
|||
*
|
||||
* @see #setMaximumSearchResultCountInTransaction(int)
|
||||
*/
|
||||
private static final int DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = 500;
|
||||
private static final Integer DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = null;
|
||||
|
||||
/**
|
||||
* Default value for {@link #setReuseCachedSearchResultsForMillis(Long)}: 60000ms (one minute)
|
||||
|
@ -110,7 +110,7 @@ public class DaoConfig {
|
|||
* update setter javadoc if default changes
|
||||
*/
|
||||
private int myMaximumExpansionSize = 5000;
|
||||
private int myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
|
||||
private Integer myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
|
||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
|
@ -233,14 +233,17 @@ public class DaoConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides the maximum number of results which may be returned by a search within a FHIR <code>transaction</code>
|
||||
* operation. For example, if this value is set to <code>100</code> and a FHIR transaction is processed with a sub-request
|
||||
* for <code>Patient?gender=male</code>, the server will throw an error (and the transaction will fail) if there are more than
|
||||
* Provides the maximum number of results which may be returned by a search (HTTP GET) which
|
||||
* is executed as a sub-operation within within a FHIR <code>transaction</code> or
|
||||
* <code>batch</code> operation. For example, if this value is set to <code>100</code> and
|
||||
* a FHIR transaction is processed with a sub-request for <code>Patient?gender=male</code>,
|
||||
* the server will throw an error (and the transaction will fail) if there are more than
|
||||
* 100 resources on the server which match this query.
|
||||
*
|
||||
* @see #DEFAULT_LOGICAL_BASE_URLS The default value for this setting
|
||||
* <p>
|
||||
* The default value is <code>null</code>, which means that there is no limit.
|
||||
* </p>
|
||||
*/
|
||||
public int getMaximumSearchResultCountInTransaction() {
|
||||
public Integer getMaximumSearchResultCountInTransaction() {
|
||||
return myMaximumSearchResultCountInTransaction;
|
||||
}
|
||||
|
||||
|
@ -699,14 +702,17 @@ public class DaoConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provides the maximum number of results which may be returned by a search within a FHIR <code>transaction</code>
|
||||
* operation. For example, if this value is set to <code>100</code> and a FHIR transaction is processed with a sub-request
|
||||
* for <code>Patient?gender=male</code>, the server will throw an error (and the transaction will fail) if there are more than
|
||||
* Provides the maximum number of results which may be returned by a search (HTTP GET) which
|
||||
* is executed as a sub-operation within within a FHIR <code>transaction</code> or
|
||||
* <code>batch</code> operation. For example, if this value is set to <code>100</code> and
|
||||
* a FHIR transaction is processed with a sub-request for <code>Patient?gender=male</code>,
|
||||
* the server will throw an error (and the transaction will fail) if there are more than
|
||||
* 100 resources on the server which match this query.
|
||||
*
|
||||
* @see #DEFAULT_LOGICAL_BASE_URLS The default value for this setting
|
||||
* <p>
|
||||
* The default value is <code>null</code>, which means that there is no limit.
|
||||
* </p>
|
||||
*/
|
||||
public void setMaximumSearchResultCountInTransaction(int theMaximumSearchResultCountInTransaction) {
|
||||
public void setMaximumSearchResultCountInTransaction(Integer theMaximumSearchResultCountInTransaction) {
|
||||
myMaximumSearchResultCountInTransaction = theMaximumSearchResultCountInTransaction;
|
||||
}
|
||||
|
||||
|
|
|
@ -106,8 +106,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
|
|||
|
||||
Bundle resp = new Bundle();
|
||||
resp.setType(BundleTypeEnum.BATCH_RESPONSE);
|
||||
OperationOutcome ooResp = new OperationOutcome();
|
||||
resp.addEntry().setResource(ooResp);
|
||||
|
||||
/*
|
||||
* For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it doesn't prevent others
|
||||
|
@ -163,7 +161,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
|
|||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info("Batch completed in {}ms", new Object[] { delay });
|
||||
ooResp.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Batch completed in " + delay + "ms");
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
|
|
@ -114,8 +114,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
|
||||
Bundle resp = new Bundle();
|
||||
resp.setType(BundleType.BATCHRESPONSE);
|
||||
OperationOutcome ooResp = new OperationOutcome();
|
||||
resp.addEntry().setResource(ooResp);
|
||||
|
||||
/*
|
||||
* For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it doesn't prevent others
|
||||
|
@ -171,7 +169,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info("Batch completed in {}ms", new Object[] { delay });
|
||||
ooResp.addIssue().setSeverity(IssueSeverity.INFORMATION).setDiagnostics("Batch completed in " + delay + "ms");
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,6 @@ public class DatabaseBackedPagingProvider extends BasePagingProvider implements
|
|||
@Override
|
||||
public synchronized String storeResultList(IBundleProvider theList) {
|
||||
String uuid = theList.getUuid();
|
||||
Validate.notNull(uuid);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,6 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
.multiline()
|
||||
.logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
||||
.countQuery()
|
||||
|
@ -70,7 +69,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
private Properties jpaProperties() {
|
||||
Properties extraProperties = new Properties();
|
||||
extraProperties.put("hibernate.jdbc.batch_size", "50");
|
||||
extraProperties.put("hibernate.format_sql", "true");
|
||||
extraProperties.put("hibernate.format_sql", "false");
|
||||
extraProperties.put("hibernate.show_sql", "false");
|
||||
extraProperties.put("hibernate.hbm2ddl.auto", "update");
|
||||
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");
|
||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||
|
@ -75,7 +76,6 @@ import ca.uhn.fhir.util.TestUtil;
|
|||
@ContextConfiguration(classes= {TestDstu2Config.class, ca.uhn.fhir.jpa.config.WebsocketDstu2DispatcherConfig.class})
|
||||
//@formatter:on
|
||||
public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||
|
||||
@Autowired
|
||||
protected ApplicationContext myAppCtx;
|
||||
@Autowired
|
||||
|
@ -114,18 +114,18 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
@Qualifier("myLocationDaoDstu2")
|
||||
protected IFhirResourceDao<Location> myLocationDao;
|
||||
@Autowired
|
||||
@Qualifier("myMediaDaoDstu2")
|
||||
protected IFhirResourceDao<Media> myMediaDao;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myMediaDaoDstu2")
|
||||
protected IFhirResourceDao<Media> myMediaDao;
|
||||
@Qualifier("myMedicationAdministrationDaoDstu2")
|
||||
protected IFhirResourceDao<MedicationAdministration> myMedicationAdministrationDao;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myMedicationDaoDstu2")
|
||||
protected IFhirResourceDao<Medication> myMedicationDao;
|
||||
@Autowired
|
||||
@Qualifier("myMedicationAdministrationDaoDstu2")
|
||||
protected IFhirResourceDao<MedicationAdministration> myMedicationAdministrationDao;
|
||||
@Autowired
|
||||
@Qualifier("myMedicationOrderDaoDstu2")
|
||||
protected IFhirResourceDao<MedicationOrder> myMedicationOrderDao;
|
||||
@Autowired
|
||||
|
@ -135,6 +135,8 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Qualifier("myOrganizationDaoDstu2")
|
||||
protected IFhirResourceDao<Organization> myOrganizationDao;
|
||||
@Autowired
|
||||
protected DatabaseBackedPagingProvider myPagingProvider;
|
||||
@Autowired
|
||||
@Qualifier("myPatientDaoDstu2")
|
||||
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||
@Autowired
|
||||
|
@ -150,8 +152,12 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Qualifier("myResourceProvidersDstu2")
|
||||
protected Object myResourceProviders;
|
||||
@Autowired
|
||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
@Autowired
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired
|
||||
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||
@Autowired
|
||||
@Qualifier("myStructureDefinitionDaoDstu2")
|
||||
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||
@Autowired
|
||||
|
@ -171,10 +177,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
@Qualifier("myValueSetDaoDstu2")
|
||||
protected IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> myValueSetDao;
|
||||
@Autowired
|
||||
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||
@Autowired
|
||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
|
||||
@Before
|
||||
public void beforeCreateInterceptor() {
|
||||
|
|
|
@ -86,8 +86,7 @@ import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
||||
import ca.uhn.fhir.jpa.search.*;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||
|
@ -108,13 +107,13 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3;
|
||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||
|
||||
// @Autowired
|
||||
// protected HapiWorkerContext myHapiWorkerContext;
|
||||
// @Autowired
|
||||
// protected HapiWorkerContext myHapiWorkerContext;
|
||||
@Autowired
|
||||
@Qualifier("myAllergyIntoleranceDaoDstu3")
|
||||
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
|
||||
@Autowired
|
||||
protected ApplicationContext myAppCtx;
|
||||
protected ApplicationContext myAppCtx;
|
||||
@Autowired
|
||||
@Qualifier("myAppointmentDaoDstu3")
|
||||
protected IFhirResourceDao<Appointment> myAppointmentDao;
|
||||
|
@ -124,7 +123,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
@Qualifier("myBundleDaoDstu3")
|
||||
protected IFhirResourceDao<Bundle> myBundleDao;
|
||||
@Autowired
|
||||
@Autowired
|
||||
@Qualifier("myCarePlanDaoDstu3")
|
||||
protected IFhirResourceDao<CarePlan> myCarePlanDao;
|
||||
@Autowired
|
||||
|
@ -195,6 +194,8 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Qualifier("myOrganizationDaoDstu3")
|
||||
protected IFhirResourceDao<Organization> myOrganizationDao;
|
||||
@Autowired
|
||||
protected DatabaseBackedPagingProvider myPagingProvider;
|
||||
@Autowired
|
||||
@Qualifier("myPatientDaoDstu3")
|
||||
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||
@Autowired
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.dstu2.BaseJpaDstu2Test;
|
||||
import ca.uhn.fhir.jpa.rp.dstu2.*;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.*;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class SystemProviderTransactionSearchDstu2Test extends BaseJpaDstu2Test {
|
||||
|
||||
private static RestfulServer myRestServer;
|
||||
private static IGenericClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderTransactionSearchDstu2Test.class);
|
||||
private static Server ourServer;
|
||||
private static String ourServerBase;
|
||||
private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor;
|
||||
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@After
|
||||
public void after() {
|
||||
myRestServer.setUseBrowserFriendlyContentTypes(true);
|
||||
ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
|
||||
myDaoConfig.setMaximumSearchResultCountInTransaction(new DaoConfig().getMaximumSearchResultCountInTransaction());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
|
||||
ourClient.registerInterceptor(mySimpleHeaderInterceptor);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeStartServer() throws Exception {
|
||||
if (myRestServer == null) {
|
||||
PatientResourceProvider patientRp = new PatientResourceProvider();
|
||||
patientRp.setDao(myPatientDao);
|
||||
|
||||
QuestionnaireResourceProviderDstu2 questionnaireRp = new QuestionnaireResourceProviderDstu2();
|
||||
questionnaireRp.setDao(myQuestionnaireDao);
|
||||
|
||||
ObservationResourceProvider observationRp = new ObservationResourceProvider();
|
||||
observationRp.setDao(myObservationDao);
|
||||
|
||||
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
|
||||
organizationRp.setDao(myOrganizationDao);
|
||||
|
||||
RestfulServer restServer = new RestfulServer(ourCtx);
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
restServer.setPlainProviders(mySystemProvider);
|
||||
|
||||
int myPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(myPort);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(restServer);
|
||||
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
|
||||
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
restServer.setFhirContext(ourCtx);
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
||||
myRestServer = restServer;
|
||||
}
|
||||
|
||||
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
myRestServer.setPagingProvider(myPagingProvider);
|
||||
}
|
||||
|
||||
|
||||
private List<String> create20Patients() {
|
||||
List<String> ids = new ArrayList<String>();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
Patient patient = new Patient();
|
||||
patient.setGender(AdministrativeGenderEnum.MALE);
|
||||
patient.addIdentifier().setSystem("urn:foo").setValue("A");
|
||||
patient.addName().addFamily("abcdefghijklmnopqrstuvwxyz".substring(i, i+1));
|
||||
String id = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getValue();
|
||||
ids.add(id);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchWithGetHardLimitLargeSynchronous() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleTypeEnum.BATCH);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerbEnum.GET)
|
||||
.setUrl("Patient?_count=5");
|
||||
|
||||
myDaoConfig.setMaximumSearchResultCountInTransaction(100);
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertEquals(null, respBundle.getLink("next"));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchWithGetNormalSearch() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleTypeEnum.BATCH);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerbEnum.GET)
|
||||
.setUrl("Patient?_count=5&_sort=name");
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
|
||||
String nextPageLink = respBundle.getLink("next").getUrl();
|
||||
output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute();
|
||||
respBundle = output;
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(5, 10).toArray(new String[0])));
|
||||
}
|
||||
|
||||
/**
|
||||
* 30 searches in one batch! Whoa!
|
||||
*/
|
||||
@Test
|
||||
public void testBatchWithManyGets() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleTypeEnum.BATCH);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerbEnum.GET)
|
||||
.setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i);
|
||||
}
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(30, output.getEntry().size());
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(i).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertThat(respBundle.getLink("next").getUrl(), not(nullValue()));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithGetHardLimitLargeSynchronous() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleTypeEnum.TRANSACTION);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerbEnum.GET)
|
||||
.setUrl("Patient?_count=5");
|
||||
|
||||
myDaoConfig.setMaximumSearchResultCountInTransaction(100);
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertEquals(null, respBundle.getLink("next"));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithGetNormalSearch() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleTypeEnum.TRANSACTION);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerbEnum.GET)
|
||||
.setUrl("Patient?_count=5&_sort=name");
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
|
||||
String nextPageLink = respBundle.getLink("next").getUrl();
|
||||
output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute();
|
||||
respBundle = output;
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(5, 10).toArray(new String[0])));
|
||||
}
|
||||
|
||||
/**
|
||||
* 30 searches in one Transaction! Whoa!
|
||||
*/
|
||||
@Test
|
||||
public void testTransactionWithManyGets() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleTypeEnum.TRANSACTION);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerbEnum.GET)
|
||||
.setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i);
|
||||
}
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(30, output.getEntry().size());
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(i).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertThat(respBundle.getLink("next").getUrl(), not(nullValue()));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> toIds(Bundle theRespBundle) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (Entry next : theRespBundle.getEntry()) {
|
||||
retVal.add(next.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -58,6 +58,7 @@ import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
|||
import ca.uhn.fhir.jpa.rp.dstu3.ObservationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu3.OrganizationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu3.PatientResourceProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||
|
@ -202,7 +203,6 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
organizationRp.setDao(myOrganizationDao);
|
||||
|
||||
RestfulServer restServer = new RestfulServer(ourCtx);
|
||||
restServer.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10));
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
restServer.setPlainProviders(mySystemProvider);
|
||||
|
@ -237,6 +237,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
myRestServer.setPagingProvider(myPagingProvider);
|
||||
}
|
||||
|
||||
@Before
|
||||
|
|
|
@ -0,0 +1,358 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
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.Header;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
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.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.*;
|
||||
import org.hl7.fhir.dstu3.model.DecimalType;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.OperationDefinition;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Organization;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||
import ca.uhn.fhir.jpa.rp.dstu3.ObservationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu3.OrganizationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu3.PatientResourceProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
|
||||
public class SystemProviderTransactionSearchDstu3Test extends BaseJpaDstu3Test {
|
||||
|
||||
private static RestfulServer myRestServer;
|
||||
private static IGenericClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
private static CloseableHttpClient ourHttpClient;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderTransactionSearchDstu3Test.class);
|
||||
private static Server ourServer;
|
||||
private static String ourServerBase;
|
||||
private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor;
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@After
|
||||
public void after() {
|
||||
myRestServer.setUseBrowserFriendlyContentTypes(true);
|
||||
ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
|
||||
myDaoConfig.setMaximumSearchResultCountInTransaction(new DaoConfig().getMaximumSearchResultCountInTransaction());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
|
||||
ourClient.registerInterceptor(mySimpleHeaderInterceptor);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeStartServer() throws Exception {
|
||||
if (myRestServer == null) {
|
||||
PatientResourceProvider patientRp = new PatientResourceProvider();
|
||||
patientRp.setDao(myPatientDao);
|
||||
|
||||
QuestionnaireResourceProviderDstu3 questionnaireRp = new QuestionnaireResourceProviderDstu3();
|
||||
questionnaireRp.setDao(myQuestionnaireDao);
|
||||
|
||||
ObservationResourceProvider observationRp = new ObservationResourceProvider();
|
||||
observationRp.setDao(myObservationDao);
|
||||
|
||||
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
|
||||
organizationRp.setDao(myOrganizationDao);
|
||||
|
||||
RestfulServer restServer = new RestfulServer(ourCtx);
|
||||
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
|
||||
|
||||
restServer.setPlainProviders(mySystemProvider);
|
||||
|
||||
int myPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(myPort);
|
||||
|
||||
ServletContextHandler proxyHandler = new ServletContextHandler();
|
||||
proxyHandler.setContextPath("/");
|
||||
|
||||
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder();
|
||||
servletHolder.setServlet(restServer);
|
||||
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
|
||||
|
||||
ourCtx = FhirContext.forDstu3();
|
||||
restServer.setFhirContext(ourCtx);
|
||||
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourHttpClient = builder.build();
|
||||
|
||||
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
||||
ourClient.setLogRequestAndResponse(true);
|
||||
myRestServer = restServer;
|
||||
}
|
||||
|
||||
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
myRestServer.setPagingProvider(myPagingProvider);
|
||||
}
|
||||
|
||||
|
||||
private List<String> create20Patients() {
|
||||
List<String> ids = new ArrayList<String>();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
Patient patient = new Patient();
|
||||
patient.setGender(AdministrativeGender.MALE);
|
||||
patient.addIdentifier().setSystem("urn:foo").setValue("A");
|
||||
patient.addName().setFamily("abcdefghijklmnopqrstuvwxyz".substring(i, i+1));
|
||||
String id = myPatientDao.create(patient).getId().toUnqualifiedVersionless().getValue();
|
||||
ids.add(id);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchWithGetHardLimitLargeSynchronous() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.BATCH);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.GET)
|
||||
.setUrl("Patient?_count=5");
|
||||
|
||||
myDaoConfig.setMaximumSearchResultCountInTransaction(100);
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertEquals(null, respBundle.getLink("next"));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchWithGetNormalSearch() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.BATCH);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.GET)
|
||||
.setUrl("Patient?_count=5&_sort=name");
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
|
||||
String nextPageLink = respBundle.getLink("next").getUrl();
|
||||
output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute();
|
||||
respBundle = output;
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(5, 10).toArray(new String[0])));
|
||||
}
|
||||
|
||||
/**
|
||||
* 30 searches in one batch! Whoa!
|
||||
*/
|
||||
@Test
|
||||
public void testBatchWithManyGets() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.BATCH);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.GET)
|
||||
.setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i);
|
||||
}
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(30, output.getEntry().size());
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(i).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertThat(respBundle.getLink("next").getUrl(), not(nullValue()));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithGetHardLimitLargeSynchronous() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.TRANSACTION);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.GET)
|
||||
.setUrl("Patient?_count=5");
|
||||
|
||||
myDaoConfig.setMaximumSearchResultCountInTransaction(100);
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertEquals(null, respBundle.getLink("next"));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithGetNormalSearch() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.TRANSACTION);
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.GET)
|
||||
.setUrl("Patient?_count=5&_sort=name");
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(1, output.getEntry().size());
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(0).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
|
||||
String nextPageLink = respBundle.getLink("next").getUrl();
|
||||
output = ourClient.loadPage().byUrl(nextPageLink).andReturnBundle(Bundle.class).execute();
|
||||
respBundle = output;
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(5, 10).toArray(new String[0])));
|
||||
}
|
||||
|
||||
/**
|
||||
* 30 searches in one Transaction! Whoa!
|
||||
*/
|
||||
@Test
|
||||
public void testTransactionWithManyGets() throws Exception {
|
||||
List<String> ids = create20Patients();
|
||||
|
||||
|
||||
Bundle input = new Bundle();
|
||||
input.setType(BundleType.TRANSACTION);
|
||||
for (int i = 0; i < 30; i++) {
|
||||
input
|
||||
.addEntry()
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.GET)
|
||||
.setUrl("Patient?_count=5&identifier=urn:foo|A,AAAAA" + i);
|
||||
}
|
||||
|
||||
Bundle output = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
assertEquals(30, output.getEntry().size());
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Bundle respBundle = (Bundle) output.getEntry().get(i).getResource();
|
||||
assertEquals(5, respBundle.getEntry().size());
|
||||
assertThat(respBundle.getLink("next").getUrl(), not(nullValue()));
|
||||
List<String> actualIds = toIds(respBundle);
|
||||
assertThat(actualIds, contains(ids.subList(0, 5).toArray(new String[0])));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> toIds(Bundle theRespBundle) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (BundleEntryComponent next : theRespBundle.getEntry()) {
|
||||
retVal.add(next.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server.provider.dstu2;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
|
||||
|
@ -10,7 +11,7 @@ package ca.uhn.fhir.rest.server.provider.dstu2;
|
|||
* 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
|
||||
* 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,
|
||||
|
@ -62,6 +63,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
|
||||
public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu2BundleFactory.class);
|
||||
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
|
@ -223,7 +225,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
entry.getRequest().getMethodElement().setValueAsString(httpVerb.getCode());
|
||||
}
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
|
||||
|
||||
BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
|
||||
if (searchMode != null) {
|
||||
entry.getSearch().getModeElement().setValue(searchMode.getCode());
|
||||
|
@ -257,7 +259,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) {
|
||||
|
||||
myBase = theServerBase;
|
||||
|
||||
|
||||
if (myBundle.getId().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
@ -302,7 +304,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
|
||||
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
|
||||
myBase = theServerBase;
|
||||
|
||||
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IBaseResource> resourceList;
|
||||
|
@ -327,7 +329,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
if (numTotalResults != null) {
|
||||
numToReturn = Math.min(numToReturn, numTotalResults - theOffset);
|
||||
}
|
||||
|
||||
|
||||
if (numToReturn > 0) {
|
||||
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
|
||||
} else {
|
||||
|
@ -340,7 +342,9 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
} else {
|
||||
if (numTotalResults == null || numTotalResults > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
if (isBlank(searchId)) {
|
||||
ourLog.info("Found {} results but paging provider did not provide an ID to use for paging", numTotalResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.hl7.fhir.dstu3.hapi.rest.server;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Structures - DSTU2 (FHIR v1.0.0)
|
||||
|
@ -44,7 +45,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
|
||||
public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu3BundleFactory.class);
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
private String myBase;
|
||||
|
@ -336,7 +337,9 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
|||
} else {
|
||||
if (numTotalResults == null || numTotalResults > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
if (isBlank(searchId)) {
|
||||
ourLog.info("Found {} results but paging provider did not provide an ID to use for paging", numTotalResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,23 @@
|
|||
(tags, profiles, and security labels) on an individual resource. The default
|
||||
is 1000.
|
||||
</action>
|
||||
<action type="add">
|
||||
When executing a search (HTTP GET) as a nested operation in in a transaction or
|
||||
batch operation, the search now returns a normal page of results with a link to
|
||||
the next page, like any other search would. Previously the search would return
|
||||
a small number of results with no paging performed, so this change brings transaction
|
||||
and batch processing in line with other types of search.
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA server no longer returns an OperationOutcome resource as the first resource
|
||||
in the Bundle for a response to a batch operation. This behaviour was previously
|
||||
present, but was not specified in the FHIR specification so it caused confusion and
|
||||
was inconsistent with behaviour in other servers.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Fix a regression in HAPI FHIR 2.5 JPA server where executing a search in a
|
||||
transaction or batch operation caused an exception.
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.5" date="2017-06-08">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue