Partition aware search cleanup (#4706)
* Partition aware search cleanup * Compile fixes * Build fixes * HAPI FHIR version bump * License * License header * Tests
This commit is contained in:
parent
4fbeeccda4
commit
a2bc9a7212
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -120,8 +120,7 @@ public class SearchConfig {
|
|||
mySearchBuilderFactory,
|
||||
mySynchronousSearchSvc,
|
||||
myPersistedJpaBundleProviderFactory,
|
||||
myRequestPartitionHelperService,
|
||||
mySearchParamRegistry,
|
||||
mySearchParamRegistry,
|
||||
mySearchStrategyFactory,
|
||||
exceptionService(),
|
||||
myBeanFactory
|
||||
|
|
|
@ -249,7 +249,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
Optional<Search> searchOpt = myTxService
|
||||
.withRequest(myRequest)
|
||||
.withRequestPartitionId(myRequestPartitionId)
|
||||
.execute(() -> mySearchCacheSvc.fetchByUuid(myUuid));
|
||||
.execute(() -> mySearchCacheSvc.fetchByUuid(myUuid, myRequestPartitionId));
|
||||
if (!searchOpt.isPresent()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -404,7 +404,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
if (mySearchEntity.getSearchType() == SearchTypeEnum.HISTORY) {
|
||||
return null;
|
||||
} else {
|
||||
return mySearchCoordinatorSvc.getSearchTotal(myUuid, myRequest).orElse(null);
|
||||
return mySearchCoordinatorSvc.getSearchTotal(myUuid, myRequest, myRequestPartitionId).orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
|||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.jpa.search.builder.StorageInterceptorHooksFacade;
|
||||
import ca.uhn.fhir.jpa.search.builder.tasks.SearchContinuationTask;
|
||||
import ca.uhn.fhir.jpa.search.builder.tasks.SearchTask;
|
||||
|
@ -113,7 +112,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
private final BeanFactory myBeanFactory;
|
||||
private final ConcurrentHashMap<String, SearchTask> myIdToSearchTask = new ConcurrentHashMap<>();
|
||||
|
||||
private final Consumer<String> myOnRemoveSearchTask = (theId) -> myIdToSearchTask.remove(theId);
|
||||
private final Consumer<String> myOnRemoveSearchTask = myIdToSearchTask::remove;
|
||||
|
||||
private final StorageInterceptorHooksFacade myStorageInterceptorHooks;
|
||||
private Integer myLoadingThrottleForUnitTests = null;
|
||||
|
@ -135,7 +134,6 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
SearchBuilderFactory<JpaPid> theSearchBuilderFactory,
|
||||
ISynchronousSearchSvc theSynchronousSearchSvc,
|
||||
PersistedJpaBundleProviderFactory thePersistedJpaBundleProviderFactory,
|
||||
IRequestPartitionHelperSvc theRequestPartitionHelperService,
|
||||
ISearchParamRegistry theSearchParamRegistry,
|
||||
SearchStrategyFactory theSearchStrategyFactory,
|
||||
ExceptionService theExceptionSvc,
|
||||
|
@ -245,7 +243,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
}
|
||||
|
||||
Callable<Search> searchCallback = () -> mySearchCacheSvc
|
||||
.fetchByUuid(theUuid)
|
||||
.fetchByUuid(theUuid, theRequestPartitionId)
|
||||
.orElseThrow(() -> myExceptionSvc.newUnknownSearchException(theUuid));
|
||||
search = myTxService
|
||||
.withRequest(theRequestDetails)
|
||||
|
@ -271,7 +269,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
// start a new pass
|
||||
if (search.getStatus() == SearchStatusEnum.PASSCMPLET) {
|
||||
ourLog.trace("Going to try to start next search");
|
||||
Optional<Search> newSearch = mySearchCacheSvc.tryToMarkSearchAsInProgress(search);
|
||||
Optional<Search> newSearch = mySearchCacheSvc.tryToMarkSearchAsInProgress(search, theRequestPartitionId);
|
||||
if (newSearch.isPresent()) {
|
||||
ourLog.trace("Launching new search");
|
||||
search = newSearch.get();
|
||||
|
@ -440,7 +438,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> getSearchTotal(String theUuid, @Nullable RequestDetails theRequestDetails) {
|
||||
public Optional<Integer> getSearchTotal(String theUuid, @Nullable RequestDetails theRequestDetails, RequestPartitionId theRequestPartitionId) {
|
||||
SearchTask task = myIdToSearchTask.get(theUuid);
|
||||
if (task != null) {
|
||||
return Optional.ofNullable(task.awaitInitialSync());
|
||||
|
@ -450,7 +448,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
* In case there is no running search, if the total is listed as accurate we know one is coming
|
||||
* so let's wait a bit for it to show up
|
||||
*/
|
||||
Optional<Search> search = myTxService.withRequest(theRequestDetails).execute(() -> mySearchCacheSvc.fetchByUuid(theUuid));
|
||||
Optional<Search> search = myTxService.withRequest(theRequestDetails).execute(() -> mySearchCacheSvc.fetchByUuid(theUuid, theRequestPartitionId));
|
||||
if (search.isPresent()) {
|
||||
Optional<SearchParameterMap> searchParameterMap = search.get().getSearchParameterMap();
|
||||
if (searchParameterMap.isPresent() && searchParameterMap.get().getSearchTotalMode() == SearchTotalModeEnum.ACCURATE) {
|
||||
|
@ -461,7 +459,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
return Optional.of(search.get().getTotalCount());
|
||||
}
|
||||
}
|
||||
search = mySearchCacheSvc.fetchByUuid(theUuid);
|
||||
search = mySearchCacheSvc.fetchByUuid(theUuid, theRequestPartitionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
*/
|
||||
package ca.uhn.fhir.jpa.search;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
||||
import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs;
|
||||
|
@ -50,7 +51,7 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc, IHas
|
|||
@Override
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
public void pollForStaleSearchesAndDeleteThem() {
|
||||
mySearchCacheSvc.pollForStaleSearchesAndDeleteThem();
|
||||
mySearchCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -474,7 +474,7 @@ public class SearchTask implements Callable<Void> {
|
|||
}
|
||||
|
||||
private void doSaveSearch() {
|
||||
Search newSearch = mySearchCacheSvc.save(mySearch);
|
||||
Search newSearch = mySearchCacheSvc.save(mySearch, myRequestPartitionId);
|
||||
|
||||
// mySearchDao.save is not supposed to return null, but in unit tests
|
||||
// it can if the mock search dao isn't set up to handle that
|
||||
|
|
|
@ -25,6 +25,8 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
|||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
||||
import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.system.HapiSystemProperties;
|
||||
|
@ -38,11 +40,8 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
|
@ -58,7 +57,7 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc {
|
|||
*/
|
||||
public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT = 500;
|
||||
public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS = 20000;
|
||||
public static final long SEARCH_CLEANUP_JOB_INTERVAL_MILLIS = 10 * DateUtils.MILLIS_PER_SECOND;
|
||||
public static final long SEARCH_CLEANUP_JOB_INTERVAL_MILLIS = DateUtils.MILLIS_PER_MINUTE;
|
||||
public static final int DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND = 2000;
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(DatabaseSearchCacheSvcImpl.class);
|
||||
private static int ourMaximumResultsToDeleteInOneStatement = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT;
|
||||
|
@ -78,7 +77,7 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc {
|
|||
@Autowired
|
||||
private ISearchIncludeDao mySearchIncludeDao;
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTxManager;
|
||||
private IHapiTransactionService myTransactionService;
|
||||
@Autowired
|
||||
private JpaStorageSettings myStorageSettings;
|
||||
|
||||
|
@ -87,45 +86,49 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc {
|
|||
myCutoffSlack = theCutoffSlack;
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public Search save(Search theSearch) {
|
||||
Search newSearch = mySearchDao.save(theSearch);
|
||||
return newSearch;
|
||||
public Search save(Search theSearch, RequestPartitionId theRequestPartitionId) {
|
||||
return myTransactionService
|
||||
.withSystemRequestOnPartition(theRequestPartitionId)
|
||||
.execute(() -> mySearchDao.save(theSearch));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public Optional<Search> fetchByUuid(String theUuid) {
|
||||
public Optional<Search> fetchByUuid(String theUuid, RequestPartitionId theRequestPartitionId) {
|
||||
Validate.notBlank(theUuid);
|
||||
return mySearchDao.findByUuidAndFetchIncludes(theUuid);
|
||||
return myTransactionService
|
||||
.withSystemRequestOnPartition(theRequestPartitionId)
|
||||
.execute(() -> mySearchDao.findByUuidAndFetchIncludes(theUuid));
|
||||
}
|
||||
|
||||
void setSearchDaoForUnitTest(ISearchDao theSearchDao) {
|
||||
mySearchDao = theSearchDao;
|
||||
}
|
||||
|
||||
void setTxManagerForUnitTest(PlatformTransactionManager theTxManager) {
|
||||
myTxManager = theTxManager;
|
||||
void setTransactionServiceForUnitTest(IHapiTransactionService theTransactionService) {
|
||||
myTransactionService = theTransactionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Search> tryToMarkSearchAsInProgress(Search theSearch) {
|
||||
public Optional<Search> tryToMarkSearchAsInProgress(Search theSearch, RequestPartitionId theRequestPartitionId) {
|
||||
ourLog.trace("Going to try to change search status from {} to {}", theSearch.getStatus(), SearchStatusEnum.LOADING);
|
||||
try {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
txTemplate.afterPropertiesSet();
|
||||
return txTemplate.execute(t -> {
|
||||
Search search = mySearchDao.findById(theSearch.getId()).orElse(theSearch);
|
||||
|
||||
if (search.getStatus() != SearchStatusEnum.PASSCMPLET) {
|
||||
throw new IllegalStateException(Msg.code(1167) + "Can't change to LOADING because state is " + search.getStatus());
|
||||
}
|
||||
search.setStatus(SearchStatusEnum.LOADING);
|
||||
Search newSearch = mySearchDao.save(search);
|
||||
return Optional.of(newSearch);
|
||||
});
|
||||
return myTransactionService
|
||||
.withSystemRequest()
|
||||
.withRequestPartitionId(theRequestPartitionId)
|
||||
.withPropagation(Propagation.REQUIRES_NEW)
|
||||
.execute(t -> {
|
||||
Search search = mySearchDao.findById(theSearch.getId()).orElse(theSearch);
|
||||
|
||||
if (search.getStatus() != SearchStatusEnum.PASSCMPLET) {
|
||||
throw new IllegalStateException(Msg.code(1167) + "Can't change to LOADING because state is " + search.getStatus());
|
||||
}
|
||||
search.setStatus(SearchStatusEnum.LOADING);
|
||||
Search newSearch = mySearchDao.save(search);
|
||||
return Optional.of(newSearch);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to activate search: {}", e.toString());
|
||||
ourLog.trace("Failed to activate search", e);
|
||||
|
@ -135,6 +138,8 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc {
|
|||
|
||||
@Override
|
||||
public Optional<Search> findCandidatesForReuse(String theResourceType, String theQueryString, Instant theCreatedAfter, RequestPartitionId theRequestPartitionId) {
|
||||
HapiTransactionService.requireTransaction();
|
||||
|
||||
String queryString = Search.createSearchQueryStringForStorage(theQueryString, theRequestPartitionId);
|
||||
|
||||
int hashCode = queryString.hashCode();
|
||||
|
@ -151,9 +156,10 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc {
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.NEVER)
|
||||
@Override
|
||||
public void pollForStaleSearchesAndDeleteThem() {
|
||||
public void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId) {
|
||||
HapiTransactionService.noTransactionAllowed();
|
||||
|
||||
if (!myStorageSettings.isExpireSearchResults()) {
|
||||
return;
|
||||
}
|
||||
|
@ -170,38 +176,49 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc {
|
|||
|
||||
ourLog.debug("Searching for searches which are before {}", cutoff);
|
||||
|
||||
TransactionTemplate tt = new TransactionTemplate(myTxManager);
|
||||
|
||||
// Mark searches as deleted if they should be
|
||||
final Slice<Long> toMarkDeleted = tt.execute(theStatus ->
|
||||
mySearchDao.findWhereCreatedBefore(cutoff, new Date(), PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))
|
||||
);
|
||||
final Slice<Long> toMarkDeleted = myTransactionService
|
||||
.withSystemRequestOnPartition(theRequestPartitionId)
|
||||
.execute(theStatus ->
|
||||
mySearchDao.findWhereCreatedBefore(cutoff, new Date(), PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))
|
||||
);
|
||||
assert toMarkDeleted != null;
|
||||
for (final Long nextSearchToDelete : toMarkDeleted) {
|
||||
ourLog.debug("Deleting search with PID {}", nextSearchToDelete);
|
||||
tt.execute(t -> {
|
||||
mySearchDao.updateDeleted(nextSearchToDelete, true);
|
||||
return null;
|
||||
});
|
||||
myTransactionService
|
||||
.withSystemRequest()
|
||||
.withRequestPartitionId(theRequestPartitionId)
|
||||
.execute(t -> {
|
||||
mySearchDao.updateDeleted(nextSearchToDelete, true);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// Delete searches that are marked as deleted
|
||||
final Slice<Long> toDelete = tt.execute(theStatus ->
|
||||
mySearchDao.findDeleted(PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))
|
||||
);
|
||||
final Slice<Long> toDelete = myTransactionService
|
||||
.withSystemRequestOnPartition(theRequestPartitionId)
|
||||
.execute(theStatus ->
|
||||
mySearchDao.findDeleted(PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))
|
||||
);
|
||||
assert toDelete != null;
|
||||
for (final Long nextSearchToDelete : toDelete) {
|
||||
ourLog.debug("Deleting search with PID {}", nextSearchToDelete);
|
||||
tt.execute(t -> {
|
||||
deleteSearch(nextSearchToDelete);
|
||||
return null;
|
||||
});
|
||||
myTransactionService
|
||||
.withSystemRequest()
|
||||
.withRequestPartitionId(theRequestPartitionId)
|
||||
.execute(t -> {
|
||||
deleteSearch(nextSearchToDelete);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
int count = toDelete.getContent().size();
|
||||
if (count > 0) {
|
||||
if (ourLog.isDebugEnabled() || HapiSystemProperties.isTestModeEnabled()) {
|
||||
Long total = tt.execute(t -> mySearchDao.count());
|
||||
Long total = myTransactionService
|
||||
.withSystemRequest()
|
||||
.withRequestPartitionId(theRequestPartitionId)
|
||||
.execute(t -> mySearchDao.count());
|
||||
ourLog.debug("Deleted {} searches, {} remaining", count, total);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public interface ISearchCacheSvc {
|
|||
* @param theSearch The search to store
|
||||
* @return Returns a copy of the search as it was saved. Callers should use the returned Search object for any further processing.
|
||||
*/
|
||||
Search save(Search theSearch);
|
||||
Search save(Search theSearch, RequestPartitionId theRequestPartitionId);
|
||||
|
||||
/**
|
||||
* Fetch a search using its UUID. The search should be fully loaded when it is returned (i.e. includes are fetched, so that access to its
|
||||
|
@ -43,7 +43,7 @@ public interface ISearchCacheSvc {
|
|||
* @param theUuid The search UUID
|
||||
* @return The search if it exists
|
||||
*/
|
||||
Optional<Search> fetchByUuid(String theUuid);
|
||||
Optional<Search> fetchByUuid(String theUuid, RequestPartitionId theRequestPartitionId);
|
||||
|
||||
/**
|
||||
* TODO: this is perhaps an inappropriate responsibility for this service
|
||||
|
@ -59,7 +59,7 @@ public interface ISearchCacheSvc {
|
|||
* succeeded in marking it). If the search doesn't exist or some other error occurred, an exception will be thrown
|
||||
* instead of {@link Optional#empty()}
|
||||
*/
|
||||
Optional<Search> tryToMarkSearchAsInProgress(Search theSearch);
|
||||
Optional<Search> tryToMarkSearchAsInProgress(Search theSearch, RequestPartitionId theRequestPartitionId);
|
||||
|
||||
/**
|
||||
* Look for any existing searches matching the given resource type and query string.
|
||||
|
@ -82,5 +82,5 @@ public interface ISearchCacheSvc {
|
|||
* if they have some other mechanism for expiring stale results other than manually looking for them
|
||||
* and deleting them.
|
||||
*/
|
||||
void pollForStaleSearchesAndDeleteThem();
|
||||
void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -134,7 +134,6 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
mySearchBuilderFactory,
|
||||
mySynchronousSearchSvc,
|
||||
myPersistedJpaBundleProviderFactory,
|
||||
myPartitionHelperSvc,
|
||||
null, // search param registry
|
||||
mySearchStrategyFactory,
|
||||
myExceptionSvc,
|
||||
|
@ -187,7 +186,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
when(mySearchBuilder.createQuery(any(), any(), any(), nullable(RequestPartitionId.class))).thenReturn(iter);
|
||||
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
|
||||
|
||||
when(mySearchCacheSvc.save(any())).thenAnswer(t -> {
|
||||
when(mySearchCacheSvc.save(any(), any())).thenAnswer(t -> {
|
||||
Search search = t.getArgument(0, Search.class);
|
||||
myCurrentSearch = search;
|
||||
return search;
|
||||
|
@ -207,7 +206,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
assertEquals("799", resources.get(789).getIdElement().getValueAsString());
|
||||
|
||||
ArgumentCaptor<Search> searchCaptor = ArgumentCaptor.forClass(Search.class);
|
||||
verify(mySearchCacheSvc, atLeastOnce()).save(searchCaptor.capture());
|
||||
verify(mySearchCacheSvc, atLeastOnce()).save(searchCaptor.capture(), any());
|
||||
|
||||
assertEquals(790, allResults.size());
|
||||
assertEquals(10, allResults.get(0).getId());
|
||||
|
@ -224,10 +223,10 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
myCurrentSearch.setStatus(SearchStatusEnum.PASSCMPLET);
|
||||
myCurrentSearch.setNumFound(10);
|
||||
|
||||
when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t -> Optional.ofNullable(myCurrentSearch));
|
||||
when(mySearchCacheSvc.fetchByUuid(any(), any())).thenAnswer(t -> Optional.ofNullable(myCurrentSearch));
|
||||
|
||||
when(mySearchCacheSvc.tryToMarkSearchAsInProgress(any())).thenAnswer(t -> {
|
||||
when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t2 -> Optional.empty());
|
||||
when(mySearchCacheSvc.tryToMarkSearchAsInProgress(any(), any())).thenAnswer(t -> {
|
||||
when(mySearchCacheSvc.fetchByUuid(any(), any())).thenAnswer(t2 -> Optional.empty());
|
||||
return Optional.empty();
|
||||
});
|
||||
|
||||
|
@ -248,7 +247,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
myCurrentSearch.setStatus(SearchStatusEnum.PASSCMPLET);
|
||||
myCurrentSearch.setNumFound(10);
|
||||
|
||||
when(mySearchCacheSvc.fetchByUuid(any())).thenAnswer(t -> {
|
||||
when(mySearchCacheSvc.fetchByUuid(any(), any())).thenAnswer(t -> {
|
||||
sleepAtLeast(100);
|
||||
return Optional.ofNullable(myCurrentSearch);
|
||||
});
|
||||
|
@ -373,7 +372,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
List<JpaPid> pids = createPidSequence(800);
|
||||
IResultIterator iter = new SlowIterator(pids.iterator(), 2);
|
||||
when(mySearchBuilder.createQuery(same(params), any(), any(), nullable(RequestPartitionId.class))).thenReturn(iter);
|
||||
when(mySearchCacheSvc.save(any())).thenAnswer(t -> {
|
||||
when(mySearchCacheSvc.save(any(), any())).thenAnswer(t -> {
|
||||
ourLog.info("Saving search");
|
||||
return t.getArgument(0, Search.class);
|
||||
});
|
||||
|
@ -386,7 +385,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
assertEquals(790, result.size());
|
||||
|
||||
ArgumentCaptor<Search> searchCaptor = ArgumentCaptor.forClass(Search.class);
|
||||
verify(mySearchCacheSvc, atLeast(1)).save(searchCaptor.capture());
|
||||
verify(mySearchCacheSvc, atLeast(1)).save(searchCaptor.capture(), any());
|
||||
Search search = searchCaptor.getValue();
|
||||
assertEquals(SearchTypeEnum.SEARCH, search.getSearchType());
|
||||
|
||||
|
@ -450,7 +449,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
search.setStatus(SearchStatusEnum.LOADING);
|
||||
search.setSearchParameterMap(new SearchParameterMap());
|
||||
|
||||
when(mySearchCacheSvc.fetchByUuid(eq(uuid))).thenReturn(Optional.of(search));
|
||||
when(mySearchCacheSvc.fetchByUuid(eq(uuid), any())).thenReturn(Optional.of(search));
|
||||
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
|
||||
|
||||
PersistedJpaBundleProvider provider;
|
||||
|
@ -567,7 +566,7 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
search.setStatus(SearchStatusEnum.FINISHED);
|
||||
search.setNumFound(100);
|
||||
search.setTotalCount(100);
|
||||
when(mySearchCacheSvc.fetchByUuid(eq("0000-1111"))).thenReturn(Optional.of(search));
|
||||
when(mySearchCacheSvc.fetchByUuid(eq("0000-1111"), any())).thenReturn(Optional.of(search));
|
||||
|
||||
when(mySearchResultCacheSvc.fetchResultPids(any(), anyInt(), anyInt(), any(), any())).thenReturn(null);
|
||||
|
||||
|
@ -594,9 +593,9 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc {
|
|||
search.setStatus(SearchStatusEnum.PASSCMPLET);
|
||||
search.setNumFound(5);
|
||||
search.setSearchParameterMap(new SearchParameterMap());
|
||||
when(mySearchCacheSvc.fetchByUuid(eq("0000-1111"))).thenReturn(Optional.of(search));
|
||||
when(mySearchCacheSvc.fetchByUuid(eq("0000-1111"), any())).thenReturn(Optional.of(search));
|
||||
|
||||
when(mySearchCacheSvc.tryToMarkSearchAsInProgress(any())).thenAnswer(t -> {
|
||||
when(mySearchCacheSvc.tryToMarkSearchAsInProgress(any(), any())).thenAnswer(t -> {
|
||||
search.setStatus(SearchStatusEnum.LOADING);
|
||||
return Optional.of(search);
|
||||
});
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4281,7 +4281,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
search.setStatus(SearchStatusEnum.FAILED);
|
||||
search.setFailureCode(500);
|
||||
search.setFailureMessage("FOO");
|
||||
mySearchCacheSvc.save(search);
|
||||
mySearchCacheSvc.save(search, RequestPartitionId.defaultPartition());
|
||||
});
|
||||
|
||||
IBundleProvider results = myEncounterDao.search(map);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
|
@ -85,7 +86,7 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test {
|
|||
assertEquals(30, mySearchResultDao.count());
|
||||
});
|
||||
|
||||
myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem();
|
||||
myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions());
|
||||
runInTransaction(()->{
|
||||
// We should delete up to 10, but 3 don't get deleted since they have too many results to delete in one pass
|
||||
assertEquals(13, mySearchDao.count());
|
||||
|
@ -94,7 +95,7 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test {
|
|||
assertEquals(15, mySearchResultDao.count());
|
||||
});
|
||||
|
||||
myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem();
|
||||
myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions());
|
||||
runInTransaction(()->{
|
||||
// Once again we attempt to delete 10, but the first 3 don't get deleted and still remain
|
||||
// (total is 6 because 3 weren't deleted, and they blocked another 3 that might have been)
|
||||
|
@ -103,7 +104,7 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test {
|
|||
assertEquals(0, mySearchResultDao.count());
|
||||
});
|
||||
|
||||
myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem();
|
||||
myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions());
|
||||
runInTransaction(()->{
|
||||
assertEquals(0, mySearchDao.count());
|
||||
assertEquals(0, mySearchDao.countDeleted());
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.search.cache;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -9,7 +11,6 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -30,14 +31,11 @@ public class DatabaseSearchCacheSvcImplTest {
|
|||
@Mock
|
||||
private ISearchDao mySearchDao;
|
||||
|
||||
@Mock
|
||||
private PlatformTransactionManager myTxManager;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
mySvc = new DatabaseSearchCacheSvcImpl();
|
||||
mySvc.setSearchDaoForUnitTest(mySearchDao);
|
||||
mySvc.setTxManagerForUnitTest(myTxManager);
|
||||
mySvc.setTransactionServiceForUnitTest(new NonTransactionalHapiTransactionService());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -48,7 +46,7 @@ public class DatabaseSearchCacheSvcImplTest {
|
|||
when(mySearchDao.save(any())).thenReturn(updated);
|
||||
|
||||
Search search = new Search();
|
||||
Optional<Search> outcome = mySvc.tryToMarkSearchAsInProgress(search);
|
||||
Optional<Search> outcome = mySvc.tryToMarkSearchAsInProgress(search, RequestPartitionId.allPartitions());
|
||||
assertTrue(outcome.isPresent());
|
||||
|
||||
verify(mySearchDao, times(1)).save(any());
|
||||
|
@ -63,7 +61,7 @@ public class DatabaseSearchCacheSvcImplTest {
|
|||
when(mySearchDao.save(any())).thenThrow(new HibernateException("FOO"));
|
||||
|
||||
Search search = new Search();
|
||||
Optional<Search> outcome = mySvc.tryToMarkSearchAsInProgress(search);
|
||||
Optional<Search> outcome = mySvc.tryToMarkSearchAsInProgress(search, RequestPartitionId.allPartitions());
|
||||
assertFalse(outcome.isPresent());
|
||||
verify(mySearchDao, times(1)).save(any());
|
||||
}
|
||||
|
@ -75,7 +73,7 @@ public class DatabaseSearchCacheSvcImplTest {
|
|||
when(mySearchDao.findById(any())).thenReturn(Optional.of(updated));
|
||||
|
||||
Search search = new Search();
|
||||
Optional<Search> outcome = mySvc.tryToMarkSearchAsInProgress(search);
|
||||
Optional<Search> outcome = mySvc.tryToMarkSearchAsInProgress(search, RequestPartitionId.allPartitions());
|
||||
assertFalse(outcome.isPresent());
|
||||
verify(mySearchDao, never()).save(any());
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -44,6 +44,11 @@
|
|||
<artifactId>httpcore</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
|
||||
<artifactId>owasp-java-html-sanitizer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Spring is added as an optional dependency just so that it
|
||||
can be used for CORS
|
||||
|
|
|
@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.util.NarrativeUtil;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
|
@ -800,28 +801,11 @@ public class ResponseHighlighterInterceptor {
|
|||
}
|
||||
|
||||
/*
|
||||
* FHIR only allows a pretty restricted set of HTML tags and attributes, in order
|
||||
* to avoid any risk of injection attacks. If anything that isn't explicitly allowed
|
||||
* by FHIR is present in the narrative we won't render it and instead we'll explain
|
||||
* what validation problems we found.
|
||||
* Sanitize the narrative so that it's safe to render (strip any
|
||||
* links, potentially unsafe CSS, etc.)
|
||||
*/
|
||||
if (xhtmlNode != null) {
|
||||
List<String> errors = new ArrayList<>();
|
||||
Validate.isTrue(xhtmlNode.getName() == null);
|
||||
xhtmlNode.getFirstElement().validate(errors, "", true, false, false);
|
||||
if (errors.size() > 0) {
|
||||
StringBuilder errorNarrative = new StringBuilder();
|
||||
errorNarrative.append("Can not render narrative due to validation errors:");
|
||||
errorNarrative.append("<ul>");
|
||||
errors.forEach(next -> {
|
||||
errorNarrative.append("<li>");
|
||||
errorNarrative.append(sanitizeUrlPart(next));
|
||||
errorNarrative.append("</li>");
|
||||
});
|
||||
errorNarrative.append("</ul>");
|
||||
return errorNarrative.toString();
|
||||
}
|
||||
|
||||
xhtmlNode = NarrativeUtil.sanitize(xhtmlNode);
|
||||
return xhtmlNode.getValueAsString();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.rest.server.util;
|
||||
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.owasp.html.HtmlPolicyBuilder;
|
||||
import org.owasp.html.PolicyFactory;
|
||||
import org.owasp.html.Sanitizers;
|
||||
|
||||
public class NarrativeUtil {
|
||||
|
||||
/**
|
||||
* Non instantiable
|
||||
*/
|
||||
private NarrativeUtil() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method accepts an Xhtml (generally a narrative) and sanitizes it,
|
||||
* removing unsafe elements. This method leverages the
|
||||
* <a href="https://github.com/OWASP/java-html-sanitizer/blob/master/pom.xml">OWASP Java HTML Sanitizer</a>
|
||||
* to perform this task. The policy allows the following:
|
||||
* <ul>
|
||||
* <li>Block tags are allowed</li>
|
||||
* <li>Tables are allowed</li>
|
||||
* <li>Basic styles are allowed but any styles considered unsafe are removed from the document (e.g. any style declarations that could be used to load external content)</li>
|
||||
* <li>Attributes considered safe are allowed</li>
|
||||
* <li>Any links (<a href="....") are removed although any text inside the link is retained</li>
|
||||
* <li>All other elements and attributes are removed</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static String sanitize(String theHtml) {
|
||||
XhtmlNode node = new XhtmlNode();
|
||||
node.setValueAsString(theHtml);
|
||||
return sanitize(node).getValueAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method accepts an Xhtml (generally a narrative) and sanitizes it,
|
||||
* removing unsafe elements. This method leverages the
|
||||
* <a href="https://github.com/OWASP/java-html-sanitizer/blob/master/pom.xml">OWASP Java HTML Sanitizer</a>
|
||||
* to perform this task. The policy allows the following:
|
||||
* <ul>
|
||||
* <li>Block tags are allowed</li>
|
||||
* <li>Tables are allowed</li>
|
||||
* <li>Basic styles are allowed but any styles considered unsafe are removed from the document (e.g. any style declarations that could be used to load external content)</li>
|
||||
* <li>Attributes considered safe are allowed</li>
|
||||
* <li>Any links (<a href="....") are removed although any text inside the link is retained</li>
|
||||
* <li>All other elements and attributes are removed</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static XhtmlNode sanitize(XhtmlNode theNode) {
|
||||
String html = theNode.getValueAsString();
|
||||
|
||||
PolicyFactory idPolicy = new HtmlPolicyBuilder()
|
||||
.allowAttributes("id").globally()
|
||||
.toFactory();
|
||||
|
||||
PolicyFactory policy = Sanitizers.FORMATTING
|
||||
.and(Sanitizers.BLOCKS)
|
||||
.and(Sanitizers.TABLES)
|
||||
.and(Sanitizers.STYLES)
|
||||
.and(idPolicy);
|
||||
String safeHTML = policy.sanitize(html);
|
||||
|
||||
XhtmlNode retVal = new XhtmlNode();
|
||||
retVal.setValueAsString(safeHTML);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package ca.uhn.fhir.rest.server.util;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class NarrativeUtilTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"<div><SPAN ID=\"foo\">hello</SPAN></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\"><span id=\"foo\">hello</span></div>",
|
||||
"<div><span id=\"foo\">hello</span></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\"><span id=\"foo\">hello</span></div>",
|
||||
"<div><SPAN ONCLICK=\"hello()\">hello</SPAN></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\">hello</div>",
|
||||
"<div><span onclick=\"hello()\">hello</span></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\">hello</div>",
|
||||
"<div><a href=\"http://goodbye\">hello</a></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\">hello</div>",
|
||||
"<div><table><tr><td>hello</td></tr></table></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\"><table><tbody><tr><td>hello</td></tr></tbody></table></div>",
|
||||
"<div><span style=\"font-size: 100px;\">hello</span></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\"><span style=\"font-size:100px\">hello</span></div>",
|
||||
"<div><span style=\"background: url('test.jpg')\">hello</span></div> , <div xmlns=\"http://www.w3.org/1999/xhtml\">hello</div>",
|
||||
"hello , <div xmlns=\"http://www.w3.org/1999/xhtml\">hello</div>",
|
||||
"empty , null",
|
||||
"null , null"
|
||||
})
|
||||
public void testValidateIsCaseInsensitive(String theHtml, String theExpected) {
|
||||
String output = NarrativeUtil.sanitize(fixNull(theHtml));
|
||||
assertEquals(fixNull(theExpected), output);
|
||||
}
|
||||
|
||||
private String fixNull(String theExpected) {
|
||||
if ("null".equals(theExpected)) {
|
||||
return null;
|
||||
}
|
||||
if ("empty".equals(theExpected)) {
|
||||
return "";
|
||||
}
|
||||
return theExpected;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
@ -20,7 +21,8 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -43,6 +43,6 @@ public interface ISearchCoordinatorSvc<T extends IResourcePersistentId> {
|
|||
* Fetch the total number of search results for the given currently executing search, if one is currently executing and
|
||||
* the total is known. Will return empty otherwise
|
||||
*/
|
||||
Optional<Integer> getSearchTotal(String theUuid, @Nullable RequestDetails theRequestDetails);
|
||||
Optional<Integer> getSearchTotal(String theUuid, @Nullable RequestDetails theRequestDetails, RequestPartitionId theRequestPartitionId);
|
||||
|
||||
}
|
||||
|
|
|
@ -54,6 +54,22 @@ public interface IHapiTransactionService {
|
|||
*/
|
||||
IExecutionBuilder withSystemRequest();
|
||||
|
||||
/**
|
||||
* Fluent builder for internal system requests with no external
|
||||
* {@link RequestDetails} associated and a pre-specified partition ID.
|
||||
* This method is sugar for
|
||||
* <pre>
|
||||
* withSystemRequest()
|
||||
* .withRequestPartitionId(thePartitionId);
|
||||
* </pre>
|
||||
*
|
||||
* @since 6.6.0
|
||||
*/
|
||||
default IExecutionBuilder withSystemRequestOnPartition(RequestPartitionId theRequestPartitionId) {
|
||||
return withSystemRequest()
|
||||
.withRequestPartitionId(theRequestPartitionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated It is highly recommended to use {@link #withRequest(RequestDetails)} instead of this method, for increased visibility.
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -990,8 +990,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
HttpGet httpGet = new HttpGet(url);
|
||||
try (CloseableHttpResponse response = ourClient.execute(httpGet)) {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8);
|
||||
assertThat(resp, not(containsString("<thead><tr><th>Header1</th><th>Header2</th></tr></thead>")));
|
||||
assertThat(resp, containsString("Error at div/table: Found attribute table.onclick in a resource"));
|
||||
assertThat(resp, containsString("<table><thead><tr><th>Header1</th>"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
|||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||
import ca.uhn.fhir.rest.client.impl.GenericClient;
|
||||
import ca.uhn.fhir.rest.server.util.NarrativeUtil;
|
||||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
@ -522,7 +523,7 @@ public class BaseController {
|
|||
theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000);
|
||||
theModelMap.put("requestHeaders", requestHeaders);
|
||||
theModelMap.put("responseHeaders", responseHeaders);
|
||||
theModelMap.put("narrative", narrativeString);
|
||||
theModelMap.put("narrative", NarrativeUtil.sanitize(narrativeString));
|
||||
theModelMap.put("latencyMs", theLatency);
|
||||
|
||||
theModelMap.put("config", myConfig);
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
8
pom.xml
8
pom.xml
|
@ -7,7 +7,8 @@
|
|||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<name>HAPI-FHIR</name>
|
||||
<description>An open-source implementation of the FHIR specification in Java.</description>
|
||||
<url>https://hapifhir.io</url>
|
||||
|
@ -1059,6 +1060,11 @@
|
|||
<artifactId>caffeine</artifactId>
|
||||
<version>${caffeine_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
|
||||
<artifactId>owasp-java-html-sanitizer</artifactId>
|
||||
<version>20211018.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.graphql-java</groupId>
|
||||
<artifactId>graphql-java</artifactId>
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.10-SNAPSHOT</version>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
Loading…
Reference in New Issue