Reuse bundle ID when returning query cache results
This commit is contained in:
parent
432c511a30
commit
31fb1236a1
|
@ -176,6 +176,7 @@ public class Constants {
|
|||
public static final String URL_TOKEN_METADATA = "metadata";
|
||||
public static final String OO_INFOSTATUS_PROCESSING = "processing";
|
||||
public static final String PARAM_GRAPHQL_QUERY = "query";
|
||||
public static final String HEADER_X_CACHE = "X-Cache";
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
||||
|
|
|
@ -35,7 +35,7 @@ public interface IVersionSpecificBundleFactory {
|
|||
|
||||
void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes);
|
||||
|
||||
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
||||
void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
||||
|
||||
IBaseResource getResourceBundle();
|
||||
|
||||
|
|
|
@ -125,6 +125,11 @@
|
|||
<artifactId>datasource-proxy</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.exparity</groupId>
|
||||
<artifactId>hamcrest-date</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
|
|
|
@ -29,7 +29,6 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
|||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
|
|
|
@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
|||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.*;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||
|
@ -61,6 +62,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -894,6 +896,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
@Override
|
||||
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails) {
|
||||
return search(theParams, theRequestDetails, null);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
@Override
|
||||
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
||||
|
||||
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
|
||||
for (List<List<? extends IQueryParameterType>> nextAnds : theParams.values()) {
|
||||
|
@ -930,7 +938,19 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
cacheControlDirective.parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL));
|
||||
}
|
||||
|
||||
return mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName(), cacheControlDirective);
|
||||
IBundleProvider retVal = mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName(), cacheControlDirective);
|
||||
|
||||
if (retVal instanceof PersistedJpaBundleProvider) {
|
||||
PersistedJpaBundleProvider provider = (PersistedJpaBundleProvider) retVal;
|
||||
if (provider.isCacheHit()) {
|
||||
if (theServletResponse != null && theRequestDetails != null) {
|
||||
String value = "HIT from " + theRequestDetails.getFhirServerBase();
|
||||
theServletResponse.addHeader(Constants.HEADER_X_CACHE, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,6 +33,10 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
||||
|
||||
|
@ -179,6 +183,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
|
||||
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
|
||||
|
||||
@Transactional(propagation = Propagation.SUPPORTS)
|
||||
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse);
|
||||
|
||||
Set<Long> searchForIds(SearchParameterMap theParams);
|
||||
|
||||
/**
|
||||
|
|
|
@ -274,14 +274,6 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated As of HAPI FHIR 2.4 this method no longer does anything
|
||||
*/
|
||||
@Deprecated
|
||||
public void setPersistResults(boolean thePersistResults) {
|
||||
// does nothing as of HAPI FHIR 2.4
|
||||
}
|
||||
|
||||
public void setRevIncludes(Set<Include> theRevIncludes) {
|
||||
myRevIncludes = theRevIncludes;
|
||||
}
|
||||
|
|
|
@ -19,23 +19,32 @@ package ca.uhn.fhir.jpa.search;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.criteria.*;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.entity.*;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.*;
|
||||
|
||||
public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||
|
||||
|
@ -47,6 +56,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
private ISearchDao mySearchDao;
|
||||
private Search mySearchEntity;
|
||||
private String myUuid;
|
||||
private boolean myCacheHit;
|
||||
|
||||
public PersistedJpaBundleProvider(String theSearchUuid, IDao theDao) {
|
||||
myUuid = theSearchUuid;
|
||||
|
@ -179,17 +189,17 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
});
|
||||
|
||||
switch (mySearchEntity.getSearchType()) {
|
||||
case HISTORY:
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
return doHistoryInTransaction(theFromIndex, theToIndex);
|
||||
}
|
||||
});
|
||||
case SEARCH:
|
||||
case EVERYTHING:
|
||||
default:
|
||||
return doSearchOrEverything(theFromIndex, theToIndex);
|
||||
case HISTORY:
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
return doHistoryInTransaction(theFromIndex, theToIndex);
|
||||
}
|
||||
});
|
||||
case SEARCH:
|
||||
case EVERYTHING:
|
||||
default:
|
||||
return doSearchOrEverything(theFromIndex, theToIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +207,14 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
return myUuid;
|
||||
}
|
||||
|
||||
public boolean isCacheHit() {
|
||||
return myCacheHit;
|
||||
}
|
||||
|
||||
public void setCacheHit(boolean theCacheHit) {
|
||||
myCacheHit = theCacheHit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
ensureSearchEntityLoaded();
|
||||
|
|
|
@ -281,6 +281,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
mySearchDao.updateSearchLastReturned(searchToUse.getId(), new Date());
|
||||
|
||||
retVal = new PersistedJpaBundleProvider(searchToUse.getUuid(), theCallingDao);
|
||||
retVal.setCacheHit(true);
|
||||
|
||||
populateBundleProvider(retVal);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,11 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.exparity.hamcrest.date.DateMatchers;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.After;
|
||||
|
@ -14,13 +17,18 @@ import org.junit.Test;
|
|||
import org.springframework.test.util.AopTestUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.hamcrest.Matchers.blankOrNullString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CacheTest.class);
|
||||
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
||||
private CapturingInterceptor myCapturingInterceptor;
|
||||
|
||||
@Override
|
||||
@After
|
||||
|
@ -28,6 +36,8 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
super.after();
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
||||
myDaoConfig.setCacheControlNoStoreMaxResultsUpperLimit(new DaoConfig().getCacheControlNoStoreMaxResultsUpperLimit());
|
||||
|
||||
ourClient.unregisterInterceptor(myCapturingInterceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,6 +45,9 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
super.before();
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
|
||||
|
||||
myCapturingInterceptor = new CapturingInterceptor();
|
||||
ourClient.registerInterceptor(myCapturingInterceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -53,6 +66,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
assertEquals(0, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.addName().setFamily("FAM");
|
||||
|
@ -67,6 +81,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(2, results.getEntry().size());
|
||||
assertEquals(0, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -88,6 +103,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(5, results.getEntry().size());
|
||||
assertEquals(0, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -118,6 +134,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
Bundle results = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
assertEquals(1, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.addName().setFamily("FAM");
|
||||
|
@ -132,6 +149,7 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
.execute();
|
||||
assertEquals(2, results.getEntry().size());
|
||||
assertEquals(2, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -142,18 +160,26 @@ public class ResourceProviderR4CacheTest extends BaseResourceProviderR4Test {
|
|||
pt1.addName().setFamily("FAM");
|
||||
ourClient.create().resource(pt1).execute();
|
||||
|
||||
Bundle results = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
Date beforeFirst = new Date();
|
||||
|
||||
Bundle results1 = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||
assertEquals(1, results1.getEntry().size());
|
||||
assertEquals(1, mySearchEntityDao.count());
|
||||
assertThat(myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE), empty());
|
||||
assertThat(results1.getMeta().getLastUpdated(), DateMatchers.after(beforeFirst));
|
||||
assertThat(results1.getMeta().getLastUpdated(), DateMatchers.before(new Date()));
|
||||
assertThat(results1.getId(), not(blankOrNullString()));
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.addName().setFamily("FAM");
|
||||
ourClient.create().resource(pt2).execute();
|
||||
|
||||
results = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||
assertEquals(1, results.getEntry().size());
|
||||
Bundle results2 = ourClient.search().forResource("Patient").where(Patient.FAMILY.matches().value("FAM")).returnBundle(Bundle.class).execute();
|
||||
assertEquals(1, results2.getEntry().size());
|
||||
assertEquals(1, mySearchEntityDao.count());
|
||||
|
||||
assertEquals("HIT from " + ourServerBase, myCapturingInterceptor.getLastResponse().getHeaders(Constants.HEADER_X_CACHE).get(0));
|
||||
assertEquals(results1.getMeta().getLastUpdated(), results2.getMeta().getLastUpdated());
|
||||
assertEquals(results1.getId(), results2.getId());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
|
@ -349,7 +349,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
}
|
||||
}
|
||||
|
||||
bundleFactory.addRootPropertiesToBundle(null, serverBase, theLinkSelf, linkPrev, linkNext, theResult.size(), theBundleType, theResult.getPublished());
|
||||
bundleFactory.addRootPropertiesToBundle(theResult.getUuid(), serverBase, theLinkSelf, linkPrev, linkNext, theResult.size(), theBundleType, theResult.getPublished());
|
||||
bundleFactory.addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, serverBase, theServer.getBundleInclusionRule(), theIncludes);
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
|
|
|
@ -226,12 +226,15 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
|
||||
myBase = theServerBase;
|
||||
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(theId);
|
||||
}
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
|
|
@ -219,12 +219,15 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
|
||||
myBase = theServerBase;
|
||||
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(theId);
|
||||
}
|
||||
if (myBundle.getId().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
|
|
@ -19,13 +19,6 @@ package org.hl7.fhir.dstu3.hapi.rest.server;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.*;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.api.BundleInclusionRule;
|
||||
|
@ -35,324 +28,340 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
|
||||
import org.hl7.fhir.dstu3.model.DomainResource;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
||||
private String myBase;
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
private String myBase;
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
|
||||
public Dstu3BundleFactory(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
public Dstu3BundleFactory(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
|
||||
for (IBaseReference nextRef : references) {
|
||||
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
for (IBaseReference nextRef : references) {
|
||||
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getId());
|
||||
}
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getId());
|
||||
}
|
||||
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
entry.getRequest().getUrlElement().setValue(next.getId());
|
||||
}
|
||||
}
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
entry.getRequest().getUrlElement().setValue(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
ensureBundle();
|
||||
|
||||
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (isNotBlank(nextContained.getId())) {
|
||||
containedIds.add(nextContained.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
Resource nextAsResource = (Resource) next;
|
||||
IIdType id = populateBundleEntryFullUrl(next, entry);
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
if (id != null) {
|
||||
entry.getRequest().setUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
|
||||
if (searchMode != null) {
|
||||
entry.getSearch().getModeElement().setValueAsString(searchMode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IAnyResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
ensureBundle();
|
||||
}
|
||||
|
||||
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
myBase = theServerBase;
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(theId);
|
||||
}
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
|
||||
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
|
||||
}
|
||||
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (isNotBlank(nextContained.getId())) {
|
||||
containedIds.add(nextContained.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
|
||||
continue;
|
||||
}
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
private void ensureBundle() {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (BundleLinkComponent next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
|
||||
BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
} while (references.isEmpty() == false);
|
||||
myBundle.getMeta().setLastUpdated(new Date());
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
Resource nextAsResource = (Resource) next;
|
||||
IIdType id = populateBundleEntryFullUrl(next, entry);
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
if (id != null) {
|
||||
entry.getRequest().setUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
|
||||
if (searchMode != null) {
|
||||
entry.getSearch().getModeElement().setValueAsString(searchMode);
|
||||
}
|
||||
}
|
||||
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
|
||||
for (IBaseResource nextBaseRes : theResources) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IAnyResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
nextEntry.setResource(next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
|
||||
if (next.getIdElement().isAbsolute()) {
|
||||
nextEntry.getRequest().setUrl(next.getId());
|
||||
} else {
|
||||
String resourceType = myContext.getResourceDefinition(next).getName();
|
||||
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addResourcesForSearch(theResources);
|
||||
}
|
||||
|
||||
}
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
@Override
|
||||
public void initializeWithBundleResource(IBaseResource theBundle) {
|
||||
myBundle = (Bundle) theBundle;
|
||||
}
|
||||
|
||||
myBase = theServerBase;
|
||||
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
IIdType idElement = null;
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
idElement = next.getIdElement();
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
idElement = next.getIdElement();
|
||||
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
}
|
||||
}
|
||||
return idElement;
|
||||
}
|
||||
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
|
||||
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
|
||||
}
|
||||
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
|
||||
}
|
||||
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureBundle() {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (BundleLinkComponent next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
|
||||
BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
myBundle.getMeta().setLastUpdated(new Date());
|
||||
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
|
||||
for (IBaseResource nextBaseRes : theResources) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
nextEntry.setResource(next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
|
||||
if (next.getIdElement().isAbsolute()) {
|
||||
nextEntry.getRequest().setUrl(next.getId());
|
||||
} else {
|
||||
String resourceType = myContext.getResourceDefinition(next).getName();
|
||||
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addResourcesForSearch(theResources);
|
||||
}
|
||||
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeWithBundleResource(IBaseResource theBundle) {
|
||||
myBundle = (Bundle) theBundle;
|
||||
}
|
||||
|
||||
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
IIdType idElement = null;
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
idElement = next.getIdElement();
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
idElement = next.getIdElement();
|
||||
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
}
|
||||
}
|
||||
return idElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BundleEntryComponent next : myBundle.getEntry()) {
|
||||
if (next.getResource() != null) {
|
||||
retVal.add(next.getResource());
|
||||
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
|
||||
IdType id = new IdType(next.getResponse().getLocation());
|
||||
String resourceType = id.getResourceType();
|
||||
if (isNotBlank(resourceType)) {
|
||||
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
|
||||
res.setId(id);
|
||||
retVal.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BundleEntryComponent next : myBundle.getEntry()) {
|
||||
if (next.getResource() != null) {
|
||||
retVal.add(next.getResource());
|
||||
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
|
||||
IdType id = new IdType(next.getResponse().getLocation());
|
||||
String resourceType = id.getResourceType();
|
||||
if (isNotBlank(resourceType)) {
|
||||
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
|
||||
res.setId(id);
|
||||
retVal.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,14 +19,6 @@ package ca.uhn.fhir.rest.server.provider.dstu2hl7org;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.hl7.fhir.instance.model.*;
|
||||
import org.hl7.fhir.instance.model.Bundle.*;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.api.BundleInclusionRule;
|
||||
|
@ -35,311 +27,328 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import org.hl7.fhir.instance.model.Bundle;
|
||||
import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.instance.model.Bundle.BundleLinkComponent;
|
||||
import org.hl7.fhir.instance.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.instance.model.Bundle.SearchEntryMode;
|
||||
import org.hl7.fhir.instance.model.IdType;
|
||||
import org.hl7.fhir.instance.model.InstantType;
|
||||
import org.hl7.fhir.instance.model.Resource;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
|
||||
|
||||
private String myBase;
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
private String myBase;
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
|
||||
public Dstu2Hl7OrgBundleFactory(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
public Dstu2Hl7OrgBundleFactory(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
IDomainResource next = (IDomainResource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IBaseResource nextContained : next.getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
IDomainResource next = (IDomainResource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IBaseResource nextContained : next.getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next,
|
||||
IBaseReference.class);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next,
|
||||
IBaseReference.class);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
|
||||
for (IBaseReference nextRef : references) {
|
||||
IBaseResource nextRes = (IBaseResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
for (IBaseReference nextRef : references) {
|
||||
IBaseResource nextRes = (IBaseResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IBaseResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource,
|
||||
IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IBaseResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource,
|
||||
IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase,
|
||||
BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
ensureBundle();
|
||||
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
|
||||
List<? extends IAnyResource> contained;
|
||||
if (next instanceof IDomainResource) {
|
||||
IDomainResource nextDomain = (IDomainResource) next;
|
||||
contained = nextDomain.getContained();
|
||||
} else {
|
||||
contained = Collections.emptyList();
|
||||
}
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IAnyResource nextContained : contained) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes))
|
||||
continue;
|
||||
|
||||
IBaseResource nextRes = (IBaseResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdType id = (IdType) nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
for (IBaseResource iResource : addedResourcesThisPass) {
|
||||
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
|
||||
// BundleEntrySearchModeEnum searchMode =
|
||||
// ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
|
||||
// if (searchMode != null) {
|
||||
// entry.getSearch().getModeElement().setValue(searchMode.getCode());
|
||||
// }
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
}
|
||||
for (IBaseResource next : includedResources) {
|
||||
myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase,
|
||||
BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
ensureBundle();
|
||||
}
|
||||
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext,
|
||||
Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(theId);
|
||||
}
|
||||
if (isBlank(myBundle.getId())) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (myBundle.getMeta().getLastUpdated() == null) {
|
||||
InstantType instantType = new InstantType();
|
||||
instantType.setValueAsString(theLastUpdated.getValueAsString());
|
||||
myBundle.getMeta().setLastUpdatedElement(instantType);
|
||||
}
|
||||
|
||||
List<? extends IAnyResource> contained;
|
||||
if (next instanceof IDomainResource) {
|
||||
IDomainResource nextDomain = (IDomainResource) next;
|
||||
contained = nextDomain.getContained();
|
||||
} else {
|
||||
contained = Collections.emptyList();
|
||||
}
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkPrev)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
|
||||
}
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IAnyResource nextContained : contained) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
myBase = theServerBase;
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes))
|
||||
continue;
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
IBaseResource nextRes = (IBaseResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
private void ensureBundle() {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
}
|
||||
|
||||
IdType id = (IdType) nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (BundleLinkComponent next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources,
|
||||
String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
ensureBundle();
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
for (IBaseResource iResource : addedResourcesThisPass) {
|
||||
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
} while (references.isEmpty() == false);
|
||||
myBundle.getMeta().setLastUpdatedElement(InstantType.withCurrentTime());
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
// BundleEntrySearchModeEnum searchMode =
|
||||
// ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
|
||||
// if (searchMode != null) {
|
||||
// entry.getSearch().getModeElement().setValue(searchMode.getCode());
|
||||
// }
|
||||
}
|
||||
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
|
||||
for (IBaseResource nextBaseRes : theResources) {
|
||||
IBaseResource next = (IBaseResource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
myBundle.addEntry().setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
}
|
||||
nextEntry.setResource((Resource) next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
|
||||
if (next.getIdElement().isAbsolute()) {
|
||||
nextEntry.getRequest().setUrl(next.getIdElement().getValue());
|
||||
} else {
|
||||
String resourceType = myContext.getResourceDefinition(next).getName();
|
||||
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(),
|
||||
next.getIdElement().getVersionIdPart()).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addResourcesForSearch(theResources);
|
||||
}
|
||||
|
||||
}
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext,
|
||||
Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
@Override
|
||||
public void initializeWithBundleResource(IBaseResource theBundle) {
|
||||
myBundle = (Bundle) theBundle;
|
||||
}
|
||||
|
||||
if (isBlank(myBundle.getId())) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
IIdType id = next.getIdElement().toVersionless();
|
||||
id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myBundle.getMeta().getLastUpdated() == null) {
|
||||
InstantType instantType = new InstantType();
|
||||
instantType.setValueAsString(theLastUpdated.getValueAsString());
|
||||
myBundle.getMeta().setLastUpdatedElement(instantType);
|
||||
}
|
||||
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkPrev)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
|
||||
}
|
||||
|
||||
myBase = theServerBase;
|
||||
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureBundle() {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (BundleLinkComponent next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources,
|
||||
String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
ensureBundle();
|
||||
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
myBundle.getMeta().setLastUpdatedElement(InstantType.withCurrentTime());
|
||||
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
|
||||
for (IBaseResource nextBaseRes : theResources) {
|
||||
IBaseResource next = (IBaseResource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
nextEntry.setResource((Resource) next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
|
||||
if (next.getIdElement().isAbsolute()) {
|
||||
nextEntry.getRequest().setUrl(next.getIdElement().getValue());
|
||||
} else {
|
||||
String resourceType = myContext.getResourceDefinition(next).getName();
|
||||
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(),
|
||||
next.getIdElement().getVersionIdPart()).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addResourcesForSearch(theResources);
|
||||
}
|
||||
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeWithBundleResource(IBaseResource theBundle) {
|
||||
myBundle = (Bundle) theBundle;
|
||||
}
|
||||
|
||||
private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
IIdType id = next.getIdElement().toVersionless();
|
||||
id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BundleEntryComponent next : myBundle.getEntry()) {
|
||||
if (next.getResource() != null) {
|
||||
retVal.add(next.getResource());
|
||||
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
|
||||
IdType id = new IdType(next.getResponse().getLocation());
|
||||
String resourceType = id.getResourceType();
|
||||
if (isNotBlank(resourceType)) {
|
||||
IBaseResource res = (IBaseResource) myContext.getResourceDefinition(resourceType).newInstance();
|
||||
res.setId(id);
|
||||
retVal.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BundleEntryComponent next : myBundle.getEntry()) {
|
||||
if (next.getResource() != null) {
|
||||
retVal.add(next.getResource());
|
||||
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
|
||||
IdType id = new IdType(next.getResponse().getLocation());
|
||||
String resourceType = id.getResourceType();
|
||||
if (isNotBlank(resourceType)) {
|
||||
IBaseResource res = (IBaseResource) myContext.getResourceDefinition(resourceType).newInstance();
|
||||
res.setId(id);
|
||||
retVal.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,13 +19,6 @@ package org.hl7.fhir.r4.hapi.rest.server;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Bundle.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.api.BundleInclusionRule;
|
||||
|
@ -35,324 +28,340 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent;
|
||||
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
|
||||
import org.hl7.fhir.r4.model.DomainResource;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class R4BundleFactory implements IVersionSpecificBundleFactory {
|
||||
private String myBase;
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
private String myBase;
|
||||
private Bundle myBundle;
|
||||
private FhirContext myContext;
|
||||
|
||||
public R4BundleFactory(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
public R4BundleFactory(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
|
||||
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (nextContained.getIdElement().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
|
||||
for (IBaseReference nextRef : references) {
|
||||
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
for (IBaseReference nextRef : references) {
|
||||
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<IBaseReference>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getId());
|
||||
}
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getId());
|
||||
}
|
||||
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
entry.getRequest().getUrlElement().setValue(next.getId());
|
||||
}
|
||||
}
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
entry.getRequest().getUrlElement().setValue(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
ensureBundle();
|
||||
|
||||
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (isNotBlank(nextContained.getId())) {
|
||||
containedIds.add(nextContained.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
Resource nextAsResource = (Resource) next;
|
||||
IIdType id = populateBundleEntryFullUrl(next, entry);
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
if (id != null) {
|
||||
entry.getRequest().setUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
|
||||
if (searchMode != null) {
|
||||
entry.getSearch().getModeElement().setValueAsString(searchMode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IBaseResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (IAnyResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
ensureBundle();
|
||||
}
|
||||
|
||||
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
|
||||
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
myBase = theServerBase;
|
||||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(theId);
|
||||
}
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
|
||||
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
|
||||
}
|
||||
|
||||
if (next instanceof DomainResource) {
|
||||
for (Resource nextContained : ((DomainResource) next).getContained()) {
|
||||
if (isNotBlank(nextContained.getId())) {
|
||||
containedIds.add(nextContained.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
|
||||
continue;
|
||||
}
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getIdElement().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getIdElement().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
private void ensureBundle() {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = nextRes.getIdElement();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = myContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (BundleLinkComponent next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
|
||||
BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
for (IAnyResource iResource : addedResourcesThisPass) {
|
||||
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
} while (references.isEmpty() == false);
|
||||
myBundle.getMeta().setLastUpdated(new Date());
|
||||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
Resource nextAsResource = (Resource) next;
|
||||
IIdType id = populateBundleEntryFullUrl(next, entry);
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
if (id != null) {
|
||||
entry.getRequest().setUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
|
||||
if (searchMode != null) {
|
||||
entry.getSearch().getModeElement().setValueAsString(searchMode);
|
||||
}
|
||||
}
|
||||
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
|
||||
for (IBaseResource nextBaseRes : theResources) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IAnyResource next : includedResources) {
|
||||
BundleEntryComponent entry = myBundle.addEntry();
|
||||
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
}
|
||||
nextEntry.setResource(next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
|
||||
if (next.getIdElement().isAbsolute()) {
|
||||
nextEntry.getRequest().setUrl(next.getId());
|
||||
} else {
|
||||
String resourceType = myContext.getResourceDefinition(next).getName();
|
||||
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addResourcesForSearch(theResources);
|
||||
}
|
||||
|
||||
}
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
ensureBundle();
|
||||
@Override
|
||||
public void initializeWithBundleResource(IBaseResource theBundle) {
|
||||
myBundle = (Bundle) theBundle;
|
||||
}
|
||||
|
||||
myBase = theServerBase;
|
||||
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
IIdType idElement = null;
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
idElement = next.getIdElement();
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
idElement = next.getIdElement();
|
||||
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
}
|
||||
}
|
||||
return idElement;
|
||||
}
|
||||
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
|
||||
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
|
||||
}
|
||||
|
||||
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theLinkSelf)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theLinkSelf);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_NEXT, myBundle) && isNotBlank(theLinkNext)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(theLinkNext);
|
||||
}
|
||||
if (!hasLink(Constants.LINK_PREVIOUS, myBundle) && isNotBlank(theLinkPrev)) {
|
||||
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(theLinkPrev);
|
||||
}
|
||||
|
||||
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
}
|
||||
|
||||
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureBundle() {
|
||||
if (myBundle == null) {
|
||||
myBundle = new Bundle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource getResourceBundle() {
|
||||
return myBundle;
|
||||
}
|
||||
|
||||
private boolean hasLink(String theLinkType, Bundle theBundle) {
|
||||
for (BundleLinkComponent next : theBundle.getLink()) {
|
||||
if (theLinkType.equals(next.getRelation())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
|
||||
BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
|
||||
myBundle.getMeta().setLastUpdated(new Date());
|
||||
|
||||
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
|
||||
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
|
||||
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
|
||||
|
||||
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
|
||||
for (IBaseResource nextBaseRes : theResources) {
|
||||
Resource next = (Resource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
nextEntry.setResource(next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
|
||||
if (next.getIdElement().isAbsolute()) {
|
||||
nextEntry.getRequest().setUrl(next.getId());
|
||||
} else {
|
||||
String resourceType = myContext.getResourceDefinition(next).getName();
|
||||
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addResourcesForSearch(theResources);
|
||||
}
|
||||
|
||||
myBundle.getTotalElement().setValue(theTotalResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeWithBundleResource(IBaseResource theBundle) {
|
||||
myBundle = (Bundle) theBundle;
|
||||
}
|
||||
|
||||
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
IIdType idElement = null;
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
idElement = next.getIdElement();
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
idElement = next.getIdElement();
|
||||
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
}
|
||||
}
|
||||
return idElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BundleEntryComponent next : myBundle.getEntry()) {
|
||||
if (next.getResource() != null) {
|
||||
retVal.add(next.getResource());
|
||||
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
|
||||
IdType id = new IdType(next.getResponse().getLocation());
|
||||
String resourceType = id.getResourceType();
|
||||
if (isNotBlank(resourceType)) {
|
||||
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
|
||||
res.setId(id);
|
||||
retVal.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BundleEntryComponent next : myBundle.getEntry()) {
|
||||
if (next.getResource() != null) {
|
||||
retVal.add(next.getResource());
|
||||
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
|
||||
IdType id = new IdType(next.getResponse().getLocation());
|
||||
String resourceType = id.getResourceType();
|
||||
if (isNotBlank(resourceType)) {
|
||||
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
|
||||
res.setId(id);
|
||||
retVal.add(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -233,11 +233,14 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
public void addRootPropertiesToBundle(String theId, String theServerBase, String theLinkSelf, String theLinkPrev, String theLinkNext, Integer theTotalResults, BundleTypeEnum theBundleType,
|
||||
IPrimitiveType<Date> theLastUpdated) {
|
||||
|
||||
myBase = theServerBase;
|
||||
|
||||
if (myBundle.getIdElement().isEmpty()) {
|
||||
myBundle.setId(theId);
|
||||
}
|
||||
if (myBundle.getId().isEmpty()) {
|
||||
myBundle.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ public class ${className}ResourceProvider extends
|
|||
@Search(allowUnknownParams=true)
|
||||
public ca.uhn.fhir.rest.api.server.IBundleProvider search(
|
||||
javax.servlet.http.HttpServletRequest theServletRequest,
|
||||
|
||||
javax.servlet.http.HttpServletResponse theServletResponse,
|
||||
|
||||
ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails,
|
||||
|
||||
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
|
||||
|
@ -154,7 +155,7 @@ public class ${className}ResourceProvider extends
|
|||
|
||||
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
|
||||
|
||||
ca.uhn.fhir.rest.api.server.IBundleProvider retVal = getDao().search(paramMap, theRequestDetails);
|
||||
ca.uhn.fhir.rest.api.server.IBundleProvider retVal = getDao().search(paramMap, theRequestDetails, theServletResponse);
|
||||
return retVal;
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -808,6 +808,11 @@
|
|||
<artifactId>websocket-server</artifactId>
|
||||
<version>${jetty_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.exparity</groupId>
|
||||
<artifactId>hamcrest-date</artifactId>
|
||||
<version>2.0.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fusesource.jansi</groupId>
|
||||
<artifactId>jansi</artifactId>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<release version="3.1.0" date="TBD">
|
||||
<action type="add">
|
||||
A performance to the JPA server has been made which reduces the number
|
||||
of changes to index tables when updating a resource with contents that
|
||||
only make minor changes
|
||||
of writes to index tables when updating a resource with contents that
|
||||
only make minor changes to the resource content. In many cases this can
|
||||
noticeably improve update performance.
|
||||
</action>
|
||||
<action type="fix">
|
||||
In FHIR DSTU3 the
|
||||
|
@ -49,6 +50,11 @@
|
|||
failure when trying to update this resource further. This has been
|
||||
resolved.
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA Server search/history results now set the ID of the returned Bundle to
|
||||
the ID of the search, meaning that if a search returns results from the Query
|
||||
cache, it will reuse the ID of the previously returned Bundle
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.0.0" date="2017-09-27">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue