Merge remote-tracking branch 'origin/master' into feature_2020_09_02_term_multi_version_support
This commit is contained in:
commit
6f9874468a
|
@ -1620,7 +1620,7 @@ public enum Pointcut {
|
|||
* <p>
|
||||
* Hooks may accept the following parameters:
|
||||
* <ul>
|
||||
* <li>ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
|
||||
* <li>ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
|
||||
* <li>ca.uhn.fhir.empi.model.TransactionLogMessages - This parameter is for informational messages provided by the EMPI module during EMPI procesing. .</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
|
@ -1628,7 +1628,7 @@ public enum Pointcut {
|
|||
* Hooks should return <code>void</code>.
|
||||
* </p>
|
||||
*/
|
||||
EMPI_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"),
|
||||
EMPI_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"),
|
||||
|
||||
/**
|
||||
* <b>Performance Tracing Hook:</b>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2083
|
||||
title: "The JPA search coordinator will now use worker threads sourced from a ThreadPoolTaskExecutor, in order to simplify
|
||||
the addition of decorators to those threads. Thanks to Tue Toft Nørgård for the pull requets!"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2087
|
||||
title: "Added new DaoConfig parameter called maximumTransactionBundleSize that if not-null will throw a
|
||||
PayloadTooLarge exception when the number of resources in a transaction bundle exceeds this size."
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 2097
|
||||
title: "A crash in the JPA server was fixed when performing a search containined two chained search parameters to
|
||||
date target types."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 2099
|
||||
title: "When using an NPM package spec in STORE_AND_INSTALL mode, conformance resources will only be stored if they
|
||||
have a status of `active`. This fixes a bug wgere installing the US Core IG caused searches to stop working due to
|
||||
draft Search Parameters."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2099
|
||||
title: "Stored SearchParameter resources with a status of DRAFT will no longer override and disable existing built-in
|
||||
search parameters. This is done in order to avoid issues caused by uploading NPM packages such as US Core that
|
||||
contain a large number of draft parameters."
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 2101
|
||||
title: "In some circumstances when using a Plain Server, the generated CapabilityStatement could have duplciate
|
||||
Search Parameter definitions. This has been corrected."
|
|
@ -80,6 +80,7 @@ public class DaoConfig {
|
|||
* @see #setMaximumSearchResultCountInTransaction(Integer)
|
||||
*/
|
||||
private static final Integer DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = null;
|
||||
private static final Integer DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE = null;
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(DaoConfig.class);
|
||||
private static final int DEFAULT_EXPUNGE_BATCH_SIZE = 800;
|
||||
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
|
||||
|
@ -126,6 +127,8 @@ public class DaoConfig {
|
|||
private boolean myIndexContainedResources = true;
|
||||
private int myMaximumExpansionSize = DEFAULT_MAX_EXPANSION_SIZE;
|
||||
private Integer myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
|
||||
|
||||
private Integer myMaximumTransactionBundleSize = DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE;
|
||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
|
@ -655,6 +658,31 @@ public class DaoConfig {
|
|||
myMaximumSearchResultCountInTransaction = theMaximumSearchResultCountInTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the maximum number of resources permitted within a single transaction bundle.
|
||||
* If a transaction bundle is submitted with more than this number of resources, it will be
|
||||
* rejected with a PayloadTooLarge exception.
|
||||
* <p>
|
||||
* The default value is <code>null</code>, which means that there is no limit.
|
||||
* </p>
|
||||
*/
|
||||
public Integer getMaximumTransactionBundleSize() {
|
||||
return myMaximumTransactionBundleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the maximum number of resources permitted within a single transaction bundle.
|
||||
* If a transaction bundle is submitted with more than this number of resources, it will be
|
||||
* rejected with a PayloadTooLarge exception.
|
||||
* <p>
|
||||
* The default value is <code>null</code>, which means that there is no limit.
|
||||
* </p>
|
||||
*/
|
||||
public DaoConfig setMaximumTransactionBundleSize(Integer theMaximumTransactionBundleSize) {
|
||||
myMaximumTransactionBundleSize = theMaximumTransactionBundleSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting controls the number of threads allocated to resource reindexing
|
||||
* (which is only ever used if SearchParameters change, or a manual reindex is
|
||||
|
|
|
@ -28,6 +28,7 @@ public class DaoMethodOutcome extends MethodOutcome {
|
|||
|
||||
private IBasePersistedResource myEntity;
|
||||
private IBaseResource myPreviousResource;
|
||||
private boolean myNop;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -36,6 +37,20 @@ public class DaoMethodOutcome extends MethodOutcome {
|
|||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Was this a NO-OP - Typically because of an update to a resource that already matched the contents provided
|
||||
*/
|
||||
public boolean isNop() {
|
||||
return myNop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was this a NO-OP - Typically because of an update to a resource that already matched the contents provided
|
||||
*/
|
||||
public void setNop(boolean theNop) {
|
||||
myNop = theNop;
|
||||
}
|
||||
|
||||
public IBasePersistedResource getEntity() {
|
||||
return myEntity;
|
||||
}
|
||||
|
|
|
@ -262,6 +262,14 @@ public abstract class BaseConfig {
|
|||
return new DatabaseSearchResultCacheSvcImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThreadPoolTaskExecutor searchCoordinatorThreadFactory() {
|
||||
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
|
||||
threadPoolTaskExecutor.setThreadNamePrefix("search_coord_");
|
||||
threadPoolTaskExecutor.initialize();
|
||||
return threadPoolTaskExecutor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskScheduler taskScheduler() {
|
||||
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();
|
||||
|
|
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
|
@ -128,6 +129,12 @@ public abstract class BaseStorageDao {
|
|||
protected DaoMethodOutcome toMethodOutcome(RequestDetails theRequest, @Nonnull final IBasePersistedResource theEntity, @Nonnull IBaseResource theResource) {
|
||||
DaoMethodOutcome outcome = new DaoMethodOutcome();
|
||||
|
||||
if (theEntity instanceof ResourceTable) {
|
||||
if (((ResourceTable) theEntity).isUnchangedInCurrentOperation()) {
|
||||
outcome.setNop(true);
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = null;
|
||||
if (theResource.getIdElement().getValue() != null) {
|
||||
id = theResource.getIdElement();
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.dao.IJpaDao;
|
||||
|
@ -54,6 +55,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding;
|
||||
|
@ -124,6 +126,8 @@ public abstract class BaseTransactionProcessor {
|
|||
private MatchResourceUrlService myMatchResourceUrlService;
|
||||
@Autowired
|
||||
private HapiTransactionService myHapiTransactionService;
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
|
@ -342,7 +346,15 @@ public abstract class BaseTransactionProcessor {
|
|||
throw new InvalidRequestException("Unable to process transaction where incoming Bundle.type = " + transactionType);
|
||||
}
|
||||
|
||||
ourLog.debug("Beginning {} with {} resources", theActionName, myVersionAdapter.getEntries(theRequest).size());
|
||||
int numberOfEntries = myVersionAdapter.getEntries(theRequest).size();
|
||||
|
||||
if (myDaoConfig.getMaximumTransactionBundleSize() != null && numberOfEntries > myDaoConfig.getMaximumTransactionBundleSize()) {
|
||||
throw new PayloadTooLargeException("Transaction Bundle Too large. Transaction bundle contains " +
|
||||
numberOfEntries +
|
||||
" which exceedes the maximum permitted transaction bundle size of " + myDaoConfig.getMaximumTransactionBundleSize());
|
||||
}
|
||||
|
||||
ourLog.debug("Beginning {} with {} resources", theActionName, numberOfEntries);
|
||||
|
||||
final TransactionDetails transactionDetails = new TransactionDetails();
|
||||
final StopWatch transactionStopWatch = new StopWatch();
|
||||
|
@ -350,7 +362,7 @@ public abstract class BaseTransactionProcessor {
|
|||
List<IBase> requestEntries = myVersionAdapter.getEntries(theRequest);
|
||||
|
||||
// Do all entries have a verb?
|
||||
for (int i = 0; i < myVersionAdapter.getEntries(theRequest).size(); i++) {
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
IBase nextReqEntry = requestEntries.get(i);
|
||||
String verb = myVersionAdapter.getEntryRequestVerb(myContext, nextReqEntry);
|
||||
if (verb == null || !isValidVerb(verb)) {
|
||||
|
|
|
@ -49,7 +49,6 @@ import java.util.Map;
|
|||
public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class);
|
||||
|
||||
private Map<String, From<?, ResourceIndexedSearchParamDate>> myJoinMap;
|
||||
|
||||
PredicateBuilderDate(SearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
|
@ -64,15 +63,15 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
|
||||
String paramName = theSearchParam.getName();
|
||||
boolean newJoin = false;
|
||||
if (myJoinMap == null) {
|
||||
myJoinMap = new HashMap<>();
|
||||
}
|
||||
|
||||
Map<String, From<?, ResourceIndexedSearchParamDate>> joinMap = myQueryStack.getJoinMap();
|
||||
String key = theResourceName + " " + paramName;
|
||||
|
||||
From<?, ResourceIndexedSearchParamDate> join = myJoinMap.get(key);
|
||||
From<?, ResourceIndexedSearchParamDate> join = joinMap.get(key);
|
||||
|
||||
if (join == null) {
|
||||
join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, paramName);
|
||||
myJoinMap.put(key, join);
|
||||
joinMap.put(key, join);
|
||||
newJoin = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate.querystack;
|
|||
import ca.uhn.fhir.jpa.dao.predicate.IndexJoins;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
|
||||
import javax.persistence.criteria.AbstractQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
|
@ -37,7 +38,9 @@ import javax.persistence.criteria.Subquery;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
abstract class QueryRootEntry {
|
||||
|
@ -45,6 +48,7 @@ abstract class QueryRootEntry {
|
|||
private final IndexJoins myIndexJoins = new IndexJoins();
|
||||
private final CriteriaBuilder myCriteriaBuilder;
|
||||
private boolean myHasImplicitTypeSelection;
|
||||
private Map<String, From<?, ResourceIndexedSearchParamDate>> myJoinMap;
|
||||
|
||||
QueryRootEntry(CriteriaBuilder theCriteriaBuilder) {
|
||||
myCriteriaBuilder = theCriteriaBuilder;
|
||||
|
@ -108,6 +112,15 @@ abstract class QueryRootEntry {
|
|||
return getQueryRoot();
|
||||
}
|
||||
|
||||
public Map<String, From<?, ResourceIndexedSearchParamDate>> getJoinMap() {
|
||||
Map<String, From<?, ResourceIndexedSearchParamDate>> retVal = myJoinMap;
|
||||
if (retVal==null) {
|
||||
retVal = new HashMap<>();
|
||||
myJoinMap = retVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
abstract void orderBy(List<Order> theOrders);
|
||||
|
||||
abstract Expression<Date> getLastUpdatedColumn();
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate.querystack;
|
|||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
|
@ -38,6 +39,7 @@ import javax.persistence.criteria.Root;
|
|||
import javax.persistence.criteria.Subquery;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
|
||||
|
@ -281,4 +283,9 @@ public class QueryStack {
|
|||
public Predicate addNeverMatchingPredicate() {
|
||||
return top().addNeverMatchingPredicate();
|
||||
}
|
||||
|
||||
public Map<String, From<?, ResourceIndexedSearchParamDate>> getJoinMap() {
|
||||
return top().getJoinMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
|
@ -35,7 +36,6 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.SearchParameterUtil;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -208,6 +208,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
|||
count[i] = resources.size();
|
||||
|
||||
for (IBaseResource next : resources) {
|
||||
|
||||
try {
|
||||
next = isStructureDefinitionWithoutSnapshot(next) ? generateSnapshot(next) : next;
|
||||
create(next, theOutcome);
|
||||
|
@ -215,6 +216,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
|||
ourLog.warn("Failed to upload resource of type {} with ID {} - Error: {}", myFhirContext.getResourceType(next), next.getIdElement().getValue(), e.toString());
|
||||
throw new ImplementationGuideInstallationException(String.format("Error installing IG %s#%s: %s", name, version, e.toString()), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -321,10 +323,11 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
|||
} else {
|
||||
|
||||
ourLog.info("Updating existing resource matching {}", map.toNormalizedQueryString(myFhirContext));
|
||||
theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource));
|
||||
theResource.setId(searchResult.getResources(0,1).get(0).getIdElement().toUnqualifiedVersionless());
|
||||
dao.update(theResource);
|
||||
|
||||
theResource.setId(searchResult.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
|
||||
DaoMethodOutcome outcome = dao.update(theResource);
|
||||
if (!outcome.isNop()) {
|
||||
theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -349,6 +352,13 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
|
|||
}
|
||||
}
|
||||
|
||||
List<IPrimitiveType> statusTypes = myFhirContext.newFhirPath().evaluate(theResource, "status", IPrimitiveType.class);
|
||||
if (statusTypes.size() > 0) {
|
||||
if (!statusTypes.get(0).getValueAsString().equals("active")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
|
@ -54,6 +53,7 @@ import ca.uhn.fhir.rest.api.SummaryEnum;
|
|||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -80,7 +80,7 @@ import org.springframework.data.domain.Sort;
|
|||
import org.springframework.orm.jpa.JpaDialect;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
|
||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
|
@ -111,7 +111,6 @@ import java.util.concurrent.Callable;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
@ -161,9 +160,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchCoordinatorSvcImpl() {
|
||||
CustomizableThreadFactory threadFactory = new CustomizableThreadFactory("search_coord_");
|
||||
myExecutor = Executors.newCachedThreadPool(threadFactory);
|
||||
@Autowired
|
||||
public SearchCoordinatorSvcImpl(ThreadPoolTaskExecutor searchCoordinatorThreadFactory) {
|
||||
myExecutor = searchCoordinatorThreadFactory.getThreadPoolExecutor();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class FhirSystemDaoDstu3SearchTest extends BaseJpaDstu3SystemTest {
|
||||
|
||||
@Test
|
||||
public void testSearchByParans() {
|
||||
// code to come.. just here to prevent a failure
|
||||
}
|
||||
|
||||
/*//@formatter:off
|
||||
* [ERROR] Search parameter action has conflicting types token and reference
|
||||
* [ERROR] Search parameter source has conflicting types token and reference
|
||||
* [ERROR] Search parameter plan has conflicting types reference and token
|
||||
* [ERROR] Search parameter version has conflicting types token and string
|
||||
* [ERROR] Search parameter source has conflicting types reference and uri
|
||||
* [ERROR] Search parameter location has conflicting types reference and uri
|
||||
* [ERROR] Search parameter title has conflicting types string and token
|
||||
* [ERROR] Search parameter manufacturer has conflicting types string and reference
|
||||
* [ERROR] Search parameter address has conflicting types token and string
|
||||
* [ERROR] Search parameter source has conflicting types reference and string
|
||||
* [ERROR] Search parameter destination has conflicting types reference and string
|
||||
* [ERROR] Search parameter responsible has conflicting types reference and string
|
||||
* [ERROR] Search parameter value has conflicting types token and string
|
||||
* [ERROR] Search parameter address has conflicting types token and string
|
||||
* [ERROR] Search parameter address has conflicting types token and string
|
||||
* [ERROR] Search parameter address has conflicting types token and string
|
||||
* [ERROR] Search parameter address has conflicting types token and string
|
||||
* [ERROR] Search parameter action has conflicting types reference and token
|
||||
* [ERROR] Search parameter version has conflicting types token and string
|
||||
* [ERROR] Search parameter address has conflicting types token and string
|
||||
* [ERROR] Search parameter base has conflicting types reference and token
|
||||
* [ERROR] Search parameter target has conflicting types reference and token
|
||||
* [ERROR] Search parameter base has conflicting types reference and uri
|
||||
* [ERROR] Search parameter contact has conflicting types string and token
|
||||
* [ERROR] Search parameter substance has conflicting types token and reference
|
||||
* [ERROR] Search parameter provider has conflicting types reference and token
|
||||
* [ERROR] Search parameter system has conflicting types token and uri
|
||||
* [ERROR] Search parameter reference has conflicting types reference and uri
|
||||
* //@formatter:off
|
||||
*/
|
||||
}
|
|
@ -2888,108 +2888,6 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
assertEquals(1, found.size().intValue());
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Issue #55
|
||||
// */
|
||||
// @Test
|
||||
// public void testTransactionWithCidIds() throws Exception {
|
||||
// Bundle request = new Bundle();
|
||||
//
|
||||
// Patient p1 = new Patient();
|
||||
// p1.setId("cid:patient1");
|
||||
// p1.addIdentifier().setSystem("system").setValue("testTransactionWithCidIds01");
|
||||
// res.add(p1);
|
||||
//
|
||||
// Observation o1 = new Observation();
|
||||
// o1.setId("cid:observation1");
|
||||
// o1.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds02");
|
||||
// o1.setSubject(new Reference("Patient/cid:patient1"));
|
||||
// res.add(o1);
|
||||
//
|
||||
// Observation o2 = new Observation();
|
||||
// o2.setId("cid:observation2");
|
||||
// o2.getIdentifier().setSystem("system").setValue("testTransactionWithCidIds03");
|
||||
// o2.setSubject(new Reference("Patient/cid:patient1"));
|
||||
// res.add(o2);
|
||||
//
|
||||
// ourSystemDao.transaction(res);
|
||||
//
|
||||
// assertTrue(p1.getId().getValue(), p1.getId().getIdPart().matches("^[0-9]+$"));
|
||||
// assertTrue(o1.getId().getValue(), o1.getId().getIdPart().matches("^[0-9]+$"));
|
||||
// assertTrue(o2.getId().getValue(), o2.getId().getIdPart().matches("^[0-9]+$"));
|
||||
//
|
||||
// assertThat(o1.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
|
||||
// assertThat(o2.getSubject().getReference().getValue(), endsWith("Patient/" + p1.getId().getIdPart()));
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testTransactionWithDelete() throws Exception {
|
||||
// Bundle request = new Bundle();
|
||||
//
|
||||
// /*
|
||||
// * Create 3
|
||||
// */
|
||||
//
|
||||
// List<IResource> res;
|
||||
// res = new ArrayList<IResource>();
|
||||
//
|
||||
// Patient p1 = new Patient();
|
||||
// p1.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
|
||||
// res.add(p1);
|
||||
//
|
||||
// Patient p2 = new Patient();
|
||||
// p2.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
|
||||
// res.add(p2);
|
||||
//
|
||||
// Patient p3 = new Patient();
|
||||
// p3.addIdentifier().setSystem("urn:system").setValue("testTransactionWithDelete");
|
||||
// res.add(p3);
|
||||
//
|
||||
// ourSystemDao.transaction(res);
|
||||
//
|
||||
// /*
|
||||
// * Verify
|
||||
// */
|
||||
//
|
||||
// IBundleProvider results = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system",
|
||||
// "testTransactionWithDelete"));
|
||||
// assertEquals(3, results.size());
|
||||
//
|
||||
// /*
|
||||
// * Now delete 2
|
||||
// */
|
||||
//
|
||||
// request = new Bundle();
|
||||
// res = new ArrayList<IResource>();
|
||||
// List<IResource> existing = results.getResources(0, 3);
|
||||
//
|
||||
// p1 = new Patient();
|
||||
// p1.setId(existing.get(0).getId());
|
||||
// ResourceMetadataKeyEnum.DELETED_AT.put(p1, InstantDt.withCurrentTime());
|
||||
// res.add(p1);
|
||||
//
|
||||
// p2 = new Patient();
|
||||
// p2.setId(existing.get(1).getId());
|
||||
// ResourceMetadataKeyEnum.DELETED_AT.put(p2, InstantDt.withCurrentTime());
|
||||
// res.add(p2);
|
||||
//
|
||||
// ourSystemDao.transaction(res);
|
||||
//
|
||||
// /*
|
||||
// * Verify
|
||||
// */
|
||||
//
|
||||
// IBundleProvider results2 = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system",
|
||||
// "testTransactionWithDelete"));
|
||||
// assertEquals(1, results2.size());
|
||||
// List<IResource> existing2 = results2.getResources(0, 1);
|
||||
// assertEquals(existing2.get(0).getId(), existing.get(2).getId());
|
||||
//
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testTransactionWithRelativeOidIds() {
|
||||
Bundle res = new Bundle();
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class FhirSystemDaoTransactionDstu3Test extends BaseJpaDstu3SystemTest {
|
||||
public static final int TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE = 5;
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
myDaoConfig.setMaximumTransactionBundleSize(new DaoConfig().getMaximumTransactionBundleSize());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setMaximumTransactionBundleSize(TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE);
|
||||
}
|
||||
|
||||
private Bundle createInputTransactionWithSize(int theSize) {
|
||||
Bundle retval = new Bundle();
|
||||
retval.setType(BundleType.TRANSACTION);
|
||||
for (int i = 0; i < theSize; ++i) {
|
||||
Observation obs = new Observation();
|
||||
obs.setStatus(Observation.ObservationStatus.FINAL);
|
||||
retval
|
||||
.addEntry()
|
||||
.setFullUrl("urn:uuid:000" + i)
|
||||
.setResource(obs)
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.POST);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionTooBig() {
|
||||
Bundle bundle = createInputTransactionWithSize(TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE + 1);
|
||||
|
||||
try {
|
||||
mySystemDao.transaction(null, bundle);
|
||||
fail();
|
||||
} catch (PayloadTooLargeException e) {
|
||||
assertThat(e.getMessage(), containsString("Transaction Bundle Too large. Transaction bundle contains " +
|
||||
(TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE + 1) +
|
||||
" which exceedes the maximum permitted transaction bundle size of " + TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionSmallEnough() {
|
||||
testTransactionBundleSucceedsWithSize(TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE);
|
||||
testTransactionBundleSucceedsWithSize(TEST_MAXIMUM_TRANSACTION_BUNDLE_SIZE - 1);
|
||||
testTransactionBundleSucceedsWithSize(1);
|
||||
}
|
||||
|
||||
private void testTransactionBundleSucceedsWithSize(int theSize) {
|
||||
Bundle bundle = createInputTransactionWithSize(theSize);
|
||||
Bundle response = mySystemDao.transaction(null, bundle);
|
||||
|
||||
assertEquals(theSize, response.getEntry().size());
|
||||
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
|
||||
assertEquals("201 Created", response.getEntry().get(theSize - 1).getResponse().getStatus());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
|
@ -462,6 +463,24 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuiltInSearchParameterNotReplacedByDraftSearchParameter() {
|
||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||
|
||||
SearchParameter memberSp = new SearchParameter();
|
||||
memberSp.setCode("family");
|
||||
memberSp.addBase("Patient");
|
||||
memberSp.setType(Enumerations.SearchParamType.STRING);
|
||||
memberSp.setExpression("Patient.name.family");
|
||||
memberSp.setStatus(Enumerations.PublicationStatus.DRAFT);
|
||||
mySearchParameterDao.create(memberSp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
RuntimeSearchParam sp = mySearchParamRegistry.getActiveSearchParam("Patient", "family");
|
||||
assertEquals(RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, sp.getStatus());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
|
|
|
@ -72,6 +72,7 @@ import org.hl7.fhir.r4.model.CodeSystem;
|
|||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Communication;
|
||||
import org.hl7.fhir.r4.model.CommunicationRequest;
|
||||
import org.hl7.fhir.r4.model.Condition;
|
||||
import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem;
|
||||
|
@ -5080,6 +5081,34 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
assertThat(toUnqualifiedVersionlessIdValues(outcome), contains(crId));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithTwoChainedDates() {
|
||||
// Matches
|
||||
Encounter e1 = new Encounter();
|
||||
e1.setPeriod(new Period().setStartElement(new DateTimeType("2020-09-14T12:00:00Z")).setEndElement(new DateTimeType("2020-09-14T12:00:00Z")));
|
||||
String e1Id = myEncounterDao.create(e1).getId().toUnqualifiedVersionless().getValue();
|
||||
Communication c1 = new Communication();
|
||||
c1.getEncounter().setReference(e1Id);
|
||||
myCommunicationDao.create(c1);
|
||||
|
||||
// Doesn't match
|
||||
Encounter e2 = new Encounter();
|
||||
e2.setPeriod(new Period().setStartElement(new DateTimeType("2020-02-14T12:00:00Z")).setEndElement(new DateTimeType("2020-02-14T12:00:00Z")));
|
||||
String e2Id = myEncounterDao.create(e2).getId().toUnqualifiedVersionless().getValue();
|
||||
Communication c2 = new Communication();
|
||||
c2.getEncounter().setReference(e2Id);
|
||||
myCommunicationDao.create(c2);
|
||||
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(Communication.SP_ENCOUNTER, new ReferenceParam("ge2020-09-14").setChain("date"));
|
||||
map.add(Communication.SP_ENCOUNTER, new ReferenceParam("le2020-09-15").setChain("date"));
|
||||
|
||||
IBundleProvider outcome = myCommunicationDao.search(map);
|
||||
assertEquals(1, outcome.sizeOrThrowNpe());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCircularReferencesDontBreakRevIncludes() {
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -114,6 +115,20 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.us.core").setVersion("3.1.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL).setFetchDependencies(true);
|
||||
igInstaller.install(spec);
|
||||
|
||||
runInTransaction(()->{
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous(SearchParameter.SP_BASE, new TokenParam("NamingSystem"));
|
||||
IBundleProvider outcome = mySearchParameterDao.search(map);
|
||||
List<IBaseResource> resources = outcome.getResources(0, outcome.sizeOrThrowNpe());
|
||||
for (int i = 0; i < resources.size(); i++) {
|
||||
ourLog.info("**************************************************************************");
|
||||
ourLog.info("**************************************************************************");
|
||||
ourLog.info("Res " + i);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(i)));
|
||||
}
|
||||
});
|
||||
|
||||
igInstaller.install(spec);
|
||||
}
|
||||
|
||||
|
||||
|
@ -162,7 +177,8 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
igInstaller.install(spec);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem"));
|
||||
|
||||
// Be sure no further communication with the server
|
||||
JettyUtil.closeServer(myServer);
|
||||
|
@ -215,11 +231,25 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
runInTransaction(() -> {
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(StructureDefinition.SP_URL, new UriParam("http://hl7.org/fhir/uv/shorthand/CodeSystem/shorthand-code-system"));
|
||||
IBundleProvider outcome = myCodeSystemDao.search(map);
|
||||
assertEquals(1, outcome.sizeOrThrowNpe());
|
||||
IBundleProvider result = myCodeSystemDao.search(map);
|
||||
assertEquals(1, result.sizeOrThrowNpe());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInstallR4Package_DraftResourcesNotInstalled() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
||||
byte[] bytes = loadClasspathBytes("/packages/test-draft-sample.tgz");
|
||||
myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.11.1", bytes);
|
||||
|
||||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.11.1").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
|
||||
PackageInstallOutcomeJson outcome = igInstaller.install(spec);
|
||||
assertEquals(0, outcome.getResourcesInstalled().size(), outcome.getResourcesInstalled().toString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallR4Package_Twice() throws Exception {
|
||||
myDaoConfig.setAllowExternalReferences(true);
|
||||
|
@ -236,6 +266,11 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
igInstaller.install(spec);
|
||||
outcome = igInstaller.install(spec);
|
||||
assertEquals(null, outcome.getResourcesInstalled().get("CodeSystem"));
|
||||
|
||||
// Ensure that we loaded the contents
|
||||
IBundleProvider searchResult = myCodeSystemDao.search(SearchParameterMap.newSynchronous("url", new UriParam("http://hl7.org/fhir/uv/shorthand/CodeSystem/shorthand-code-system")));
|
||||
assertEquals(1, searchResult.sizeOrThrowNpe());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,9 +292,6 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
assertEquals(null, pkg.description());
|
||||
assertEquals("UK.Core.r4", pkg.name());
|
||||
|
||||
// Ensure that we loaded the contents
|
||||
IBundleProvider searchResult = myStructureDefinitionDao.search(SearchParameterMap.newSynchronous("url", new UriParam("https://fhir.nhs.uk/R4/StructureDefinition/UKCore-Patient")));
|
||||
assertEquals(1, searchResult.sizeOrThrowNpe());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -384,6 +416,7 @@ public class NpmTestR4 extends BaseJpaR4Test {
|
|||
PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY);
|
||||
igInstaller.install(spec);
|
||||
|
||||
|
||||
runInTransaction(() -> {
|
||||
NpmPackageVersionEntity versionEntity = myPackageVersionDao.findByPackageIdAndVersion("hl7.fhir.uv.shorthand", "0.12.0").orElseThrow(() -> new IllegalArgumentException());
|
||||
assertEquals(true, versionEntity.isCurrentVersion());
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.config.dstu3.BaseDstu3Config;
|
||||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
|
@ -124,7 +125,7 @@ public class SearchCoordinatorSvcImplTest {
|
|||
|
||||
myCurrentSearch = null;
|
||||
|
||||
mySvc = new SearchCoordinatorSvcImpl();
|
||||
mySvc = new SearchCoordinatorSvcImpl(new BaseDstu3Config().searchCoordinatorThreadFactory());
|
||||
mySvc.setEntityManagerForUnitTest(myEntityManager);
|
||||
mySvc.setTransactionManagerForUnitTest(myTxManager);
|
||||
mySvc.setContextForUnitTest(ourCtx);
|
||||
|
|
Binary file not shown.
|
@ -33,6 +33,7 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
|||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.rest.server.TransactionLogMessages;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -93,16 +94,20 @@ public class EmpiMessageHandler implements MessageHandler {
|
|||
}catch (Exception e) {
|
||||
log(empiContext, "Failure during EMPI processing: " + e.getMessage());
|
||||
} finally {
|
||||
|
||||
// Interceptor call: EMPI_AFTER_PERSISTED_RESOURCE_CHECKED
|
||||
ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, theMsg.getPayload(myFhirContext), theMsg.getOperationType());
|
||||
outgoingMsg.setTransactionId(theMsg.getTransactionId());
|
||||
|
||||
HookParams params = new HookParams()
|
||||
.add(ResourceModifiedMessage.class, theMsg)
|
||||
.add(ResourceOperationMessage.class, outgoingMsg)
|
||||
.add(TransactionLogMessages.class, empiContext.getTransactionLogMessages());
|
||||
myInterceptorBroadcaster.callHooks(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, params);
|
||||
}
|
||||
}
|
||||
|
||||
private EmpiTransactionContext createEmpiContext(ResourceModifiedMessage theMsg) {
|
||||
TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theMsg.getParentTransactionGuid());
|
||||
TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theMsg.getTransactionId());
|
||||
EmpiTransactionContext.OperationType empiOperation;
|
||||
switch (theMsg.getOperationType()) {
|
||||
case CREATE:
|
||||
|
|
|
@ -291,6 +291,9 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry {
|
|||
if (runtimeSp == null) {
|
||||
continue;
|
||||
}
|
||||
if (runtimeSp.getStatus() == RuntimeSearchParam.RuntimeSearchParamStatusEnum.DRAFT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String nextBaseName : SearchParameterUtil.getBaseAsStrings(myFhirContext, nextSp)) {
|
||||
if (isBlank(nextBaseName)) {
|
||||
|
|
|
@ -64,7 +64,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel
|
|||
|
||||
protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, IChannelProducer theChannelProducer, IBaseResource thePayloadResource) {
|
||||
ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType());
|
||||
payload.setParentTransactionGuid(theMsg.getParentTransactionGuid());
|
||||
payload.setTransactionId(theMsg.getTransactionId());
|
||||
ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(payload);
|
||||
theChannelProducer.send(message);
|
||||
ourLog.debug("Delivering {} message payload {} for {}", theMsg.getOperationType(), theMsg.getPayloadId(), theSubscription.getIdElement(myFhirContext).toUnqualifiedVersionless().getValue());
|
||||
|
|
|
@ -37,11 +37,14 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class DaoSubscriptionMatcher implements ISubscriptionMatcher {
|
||||
private Logger ourLog = LoggerFactory.getLogger(DaoSubscriptionMatcher.class);
|
||||
|
||||
@Autowired
|
||||
DaoRegistry myDaoRegistry;
|
||||
|
||||
@Autowired
|
||||
MatchUrlService myMatchUrlService;
|
||||
private Logger ourLog = LoggerFactory.getLogger(DaoSubscriptionMatcher.class);
|
||||
|
||||
@Autowired
|
||||
private FhirContext myCtx;
|
||||
|
||||
|
|
|
@ -23,13 +23,13 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionConstants;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.SubscriptionUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
|
|
@ -86,7 +86,6 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
|
|||
|
||||
ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload();
|
||||
matchActiveSubscriptionsAndDeliver(msg);
|
||||
|
||||
}
|
||||
|
||||
public void matchActiveSubscriptionsAndDeliver(ResourceModifiedMessage theMsg) {
|
||||
|
@ -164,7 +163,7 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
|
|||
deliveryMsg.setPayload(myFhirContext, payload, encoding);
|
||||
deliveryMsg.setSubscription(subscription);
|
||||
deliveryMsg.setOperationType(theMsg.getOperationType());
|
||||
deliveryMsg.setParentTransactionGuid(theMsg.getParentTransactionGuid());
|
||||
deliveryMsg.setTransactionId(theMsg.getTransactionId());
|
||||
deliveryMsg.copyAdditionalPropertiesFrom(theMsg);
|
||||
|
||||
// Interceptor call: SUBSCRIPTION_RESOURCE_MATCHED
|
||||
|
|
|
@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.server.messaging.json.BaseJsonMessage;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
|
||||
import ca.uhn.fhir.rest.server.messaging.IResourceMessage;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
@ -38,22 +40,10 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes
|
|||
private CanonicalSubscription mySubscription;
|
||||
@JsonProperty("payload")
|
||||
private String myPayloadString;
|
||||
@JsonIgnore
|
||||
private transient IBaseResource myPayload;
|
||||
@JsonProperty("payloadId")
|
||||
private String myPayloadId;
|
||||
@JsonProperty("parentTransactionGuid")
|
||||
private String myParentTransactionGuid;
|
||||
@JsonProperty("operationType")
|
||||
private ResourceModifiedMessage.OperationTypeEnum myOperationType;
|
||||
|
||||
public String getParentTransactionGuid() {
|
||||
return myParentTransactionGuid;
|
||||
}
|
||||
|
||||
public void setParentTransactionGuid(String theParentTransactionGuid) {
|
||||
myParentTransactionGuid = theParentTransactionGuid;
|
||||
}
|
||||
@JsonIgnore
|
||||
private transient IBaseResource myPayloadDecoded;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -62,20 +52,12 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes
|
|||
super();
|
||||
}
|
||||
|
||||
public ResourceModifiedMessage.OperationTypeEnum getOperationType() {
|
||||
return myOperationType;
|
||||
}
|
||||
|
||||
public void setOperationType(ResourceModifiedMessage.OperationTypeEnum theOperationType) {
|
||||
myOperationType = theOperationType;
|
||||
}
|
||||
|
||||
public IBaseResource getPayload(FhirContext theCtx) {
|
||||
IBaseResource retVal = myPayload;
|
||||
IBaseResource retVal = myPayloadDecoded;
|
||||
if (retVal == null && isNotBlank(myPayloadString)) {
|
||||
IParser parser = EncodingEnum.detectEncoding(myPayloadString).newParser(theCtx);
|
||||
retVal = parser.parseResource(myPayloadString);
|
||||
myPayload = retVal;
|
||||
myPayloadDecoded = retVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
@ -133,9 +115,9 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes
|
|||
return new ToStringBuilder(this)
|
||||
.append("mySubscription", mySubscription)
|
||||
.append("myPayloadString", myPayloadString)
|
||||
.append("myPayload", myPayload)
|
||||
.append("myPayload", myPayloadDecoded)
|
||||
.append("myPayloadId", myPayloadId)
|
||||
.append("myOperationType", myOperationType)
|
||||
.append("myOperationType", getOperationType())
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.server.messaging.json.BaseJsonMessage;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
|
|
|
@ -21,40 +21,24 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IModelJson;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* Most of this class has been moved to ResourceModifiedMessage in the hapi-fhir-server project, for a reusable channel ResourceModifiedMessage
|
||||
* that doesn't require knowledge of subscriptions.
|
||||
*/
|
||||
public class ResourceModifiedMessage extends BaseResourceModifiedMessage {
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ResourceModifiedMessage extends BaseResourceMessage implements IResourceMessage, IModelJson {
|
||||
|
||||
@JsonProperty("resourceId")
|
||||
private String myId;
|
||||
@JsonProperty("operationType")
|
||||
private OperationTypeEnum myOperationType;
|
||||
/**
|
||||
* This will only be set if the resource is being triggered for a specific
|
||||
* subscription
|
||||
*/
|
||||
@JsonProperty(value = "subscriptionId", required = false)
|
||||
private String mySubscriptionId;
|
||||
@JsonProperty("payload")
|
||||
private String myPayload;
|
||||
@JsonProperty("payloadId")
|
||||
private String myPayloadId;
|
||||
@JsonProperty("parentTransactionGuid")
|
||||
private String myParentTransactionGuid;
|
||||
@JsonIgnore
|
||||
private transient IBaseResource myPayloadDecoded;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -64,25 +48,13 @@ public class ResourceModifiedMessage extends BaseResourceMessage implements IRes
|
|||
}
|
||||
|
||||
public ResourceModifiedMessage(FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) {
|
||||
this();
|
||||
setId(theResource.getIdElement());
|
||||
setOperationType(theOperationType);
|
||||
if (theOperationType != OperationTypeEnum.DELETE) {
|
||||
setNewPayload(theFhirContext, theResource);
|
||||
}
|
||||
super(theFhirContext, theResource, theOperationType);
|
||||
}
|
||||
|
||||
public ResourceModifiedMessage(FhirContext theFhirContext, IBaseResource theNewResource, OperationTypeEnum theOperationType, RequestDetails theRequest) {
|
||||
this(theFhirContext, theNewResource, theOperationType);
|
||||
if (theRequest != null) {
|
||||
setParentTransactionGuid(theRequest.getTransactionGuid());
|
||||
}
|
||||
super(theFhirContext, theNewResource, theOperationType, theRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayloadId() {
|
||||
return myPayloadId;
|
||||
}
|
||||
|
||||
public String getSubscriptionId() {
|
||||
return mySubscriptionId;
|
||||
|
@ -92,98 +64,6 @@ public class ResourceModifiedMessage extends BaseResourceMessage implements IRes
|
|||
mySubscriptionId = theSubscriptionId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public IIdType getId(FhirContext theCtx) {
|
||||
IIdType retVal = null;
|
||||
if (myId != null) {
|
||||
retVal = theCtx.getVersion().newIdType().setValue(myId);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public IBaseResource getNewPayload(FhirContext theCtx) {
|
||||
if (myPayloadDecoded == null && isNotBlank(myPayload)) {
|
||||
myPayloadDecoded = theCtx.newJsonParser().parseResource(myPayload);
|
||||
}
|
||||
return myPayloadDecoded;
|
||||
}
|
||||
|
||||
public OperationTypeEnum getOperationType() {
|
||||
return myOperationType;
|
||||
}
|
||||
|
||||
public void setOperationType(OperationTypeEnum theOperationType) {
|
||||
myOperationType = theOperationType;
|
||||
}
|
||||
|
||||
public void setId(IIdType theId) {
|
||||
myId = null;
|
||||
if (theId != null) {
|
||||
myId = theId.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public String getParentTransactionGuid() {
|
||||
return myParentTransactionGuid;
|
||||
}
|
||||
|
||||
public void setParentTransactionGuid(String theParentTransactionGuid) {
|
||||
myParentTransactionGuid = theParentTransactionGuid;
|
||||
}
|
||||
|
||||
private void setNewPayload(FhirContext theCtx, IBaseResource theNewPayload) {
|
||||
/*
|
||||
* References with placeholders would be invalid by the time we get here, and
|
||||
* would be caught before we even get here. This check is basically a last-ditch
|
||||
* effort to make sure nothing has broken in the various safeguards that
|
||||
* should prevent this from happening (hence it only being an assert as
|
||||
* opposed to something executed all the time).
|
||||
*/
|
||||
assert payloadContainsNoPlaceholderReferences(theCtx, theNewPayload);
|
||||
|
||||
/*
|
||||
* Note: Don't set myPayloadDecoded in here- This is a false optimization since
|
||||
* it doesn't actually get used if anyone is doing subscriptions at any
|
||||
* scale using a queue engine, and not going through the serialize/deserialize
|
||||
* as we would in a queue engine can mask bugs.
|
||||
* -JA
|
||||
*/
|
||||
myPayload = theCtx.newJsonParser().encodeResourceToString(theNewPayload);
|
||||
myPayloadId = theNewPayload.getIdElement().toUnqualified().getValue();
|
||||
}
|
||||
|
||||
public enum OperationTypeEnum {
|
||||
CREATE,
|
||||
UPDATE,
|
||||
DELETE,
|
||||
MANUALLY_TRIGGERED
|
||||
}
|
||||
|
||||
private static boolean payloadContainsNoPlaceholderReferences(FhirContext theCtx, IBaseResource theNewPayload) {
|
||||
List<ResourceReferenceInfo> refs = theCtx.newTerser().getAllResourceReferences(theNewPayload);
|
||||
for (ResourceReferenceInfo next : refs) {
|
||||
String ref = next.getResourceReference().getReferenceElement().getValue();
|
||||
if (isBlank(ref)) {
|
||||
IBaseResource resource = next.getResourceReference().getResource();
|
||||
if (resource != null) {
|
||||
ref = resource.getIdElement().getValue();
|
||||
}
|
||||
}
|
||||
if (isNotBlank(ref)) {
|
||||
if (ref.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (ref.startsWith("urn:uuid:")) {
|
||||
throw new AssertionError("Reference at " + next.getName() + " is invalid: " + ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
|
|
|
@ -26,7 +26,6 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
||||
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
|
||||
import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
|
||||
|
@ -38,6 +37,7 @@ import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
|||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
|
|
|
@ -25,8 +25,8 @@ import ca.uhn.fhir.interceptor.api.Interceptor;
|
|||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
|
@ -3,12 +3,12 @@ package ca.uhn.fhir.jpa.subscription.match.deliver;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryJsonMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber;
|
||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
|
||||
|
@ -18,18 +18,20 @@ import org.hl7.fhir.r4.model.Patient;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class BaseSubscriptionDeliverySubscriberTest {
|
||||
|
|
|
@ -33,7 +33,6 @@ public class SubscriptionRegistrySharedTest extends BaseSubscriptionRegistryTest
|
|||
return "shared";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -10,7 +10,26 @@ import ca.uhn.fhir.jpa.subscription.module.BaseSubscriptionDstu3Test;
|
|||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.BodySite;
|
||||
import org.hl7.fhir.dstu3.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.CommunicationRequest;
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.Dosage;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations;
|
||||
import org.hl7.fhir.dstu3.model.EpisodeOfCare;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Location;
|
||||
import org.hl7.fhir.dstu3.model.MedicationRequest;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.Procedure;
|
||||
import org.hl7.fhir.dstu3.model.ProcedureRequest;
|
||||
import org.hl7.fhir.dstu3.model.Provenance;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||
import org.hl7.fhir.dstu3.model.Timing;
|
||||
import org.hl7.fhir.dstu3.model.codesystems.MedicationRequestCategory;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -89,10 +108,8 @@ public class InMemorySubscriptionMatcherR3Test extends BaseSubscriptionDstu3Test
|
|||
pr.setSubject(new Reference("Patient/"));
|
||||
assertMatched(pr, "ProcedureRequest?intent=original-order");
|
||||
assertNotMatched(pr, "ProcedureRequest?subject=Patient/123");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResourceById() {
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ public class PersonHelper {
|
|||
if (theAllowOverwriting || person.getName().isEmpty()) {
|
||||
person.setName(patient.getName());
|
||||
}
|
||||
if (theAllowOverwriting || person.getName().isEmpty()) {
|
||||
if (theAllowOverwriting || person.getAddress().isEmpty()) {
|
||||
person.setAddress(patient.getAddress());
|
||||
}
|
||||
if (theAllowOverwriting || person.getTelecom().isEmpty()) {
|
||||
|
|
|
@ -73,8 +73,12 @@
|
|||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-messaging</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.subscription.model;
|
||||
package ca.uhn.fhir.rest.server.messaging;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Subscription Server
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
|
@ -20,10 +20,13 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import ca.uhn.fhir.model.api.IModelJson;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
@ -31,9 +34,15 @@ import java.util.Optional;
|
|||
@SuppressWarnings("WeakerAccess")
|
||||
public abstract class BaseResourceMessage implements IResourceMessage, IModelJson {
|
||||
|
||||
@JsonProperty("operationType")
|
||||
protected BaseResourceModifiedMessage.OperationTypeEnum myOperationType;
|
||||
|
||||
@JsonProperty("attributes")
|
||||
private Map<String, String> myAttributes;
|
||||
|
||||
@JsonProperty("transactionId")
|
||||
private String myTransactionId;
|
||||
|
||||
/**
|
||||
* Returns an attribute stored in this message.
|
||||
* <p>
|
||||
|
@ -94,4 +103,52 @@ public abstract class BaseResourceMessage implements IResourceMessage, IModelJso
|
|||
myAttributes.putAll(theMsg.myAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OperationTypeEnum} that is occurring to the Resource of the message
|
||||
*
|
||||
* @return the operation type.
|
||||
*/
|
||||
public BaseResourceModifiedMessage.OperationTypeEnum getOperationType() {
|
||||
return myOperationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OperationTypeEnum} occuring to the resource of the message.
|
||||
*
|
||||
* @param theOperationType The operation type to set.
|
||||
*/
|
||||
public void setOperationType(BaseResourceModifiedMessage.OperationTypeEnum theOperationType) {
|
||||
myOperationType = theOperationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction ID related to this message.
|
||||
*
|
||||
* @return the transaction ID, or null.
|
||||
*/
|
||||
@Nullable
|
||||
public String getTransactionId() {
|
||||
return myTransactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a transaction ID to this message. This ID can be used for many purposes. For example, performing tracing
|
||||
* across asynchronous hooks, tying data together, or downstream logging purposes.
|
||||
*
|
||||
* One current internal implementation uses this field to tie back EMPI processing results (which are asynchronous)
|
||||
* to the original transaction log that caused the EMPI processing to occur.
|
||||
*
|
||||
* @param theTransactionId An ID representing a transaction of relevance to this message.
|
||||
*/
|
||||
public void setTransactionId(String theTransactionId) {
|
||||
myTransactionId = theTransactionId;
|
||||
}
|
||||
|
||||
public enum OperationTypeEnum {
|
||||
CREATE,
|
||||
UPDATE,
|
||||
DELETE,
|
||||
MANUALLY_TRIGGERED
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package ca.uhn.fhir.rest.server.messaging;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IModelJson;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public abstract class BaseResourceModifiedMessage extends BaseResourceMessage implements IResourceMessage, IModelJson {
|
||||
|
||||
@JsonProperty("resourceId")
|
||||
protected String myId;
|
||||
@JsonProperty("payload")
|
||||
protected String myPayload;
|
||||
@JsonProperty("payloadId")
|
||||
protected String myPayloadId;
|
||||
@JsonIgnore
|
||||
protected transient IBaseResource myPayloadDecoded;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BaseResourceModifiedMessage() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BaseResourceModifiedMessage(FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) {
|
||||
this();
|
||||
setId(theResource.getIdElement());
|
||||
setOperationType(theOperationType);
|
||||
if (theOperationType != OperationTypeEnum.DELETE) {
|
||||
setNewPayload(theFhirContext, theResource);
|
||||
}
|
||||
}
|
||||
|
||||
public BaseResourceModifiedMessage(FhirContext theFhirContext, IBaseResource theNewResource, OperationTypeEnum theOperationType, RequestDetails theRequest) {
|
||||
this(theFhirContext, theNewResource, theOperationType);
|
||||
if (theRequest != null) {
|
||||
setTransactionId(theRequest.getTransactionGuid());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayloadId() {
|
||||
return myPayloadId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public IIdType getId(FhirContext theCtx) {
|
||||
IIdType retVal = null;
|
||||
if (myId != null) {
|
||||
retVal = theCtx.getVersion().newIdType().setValue(myId);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public IBaseResource getNewPayload(FhirContext theCtx) {
|
||||
if (myPayloadDecoded == null && isNotBlank(myPayload)) {
|
||||
myPayloadDecoded = theCtx.newJsonParser().parseResource(myPayload);
|
||||
}
|
||||
return myPayloadDecoded;
|
||||
}
|
||||
|
||||
public IBaseResource getPayload(FhirContext theCtx) {
|
||||
IBaseResource retVal = myPayloadDecoded;
|
||||
if (retVal == null && isNotBlank(myPayload)) {
|
||||
IParser parser = EncodingEnum.detectEncoding(myPayload).newParser(theCtx);
|
||||
retVal = parser.parseResource(myPayload);
|
||||
myPayloadDecoded = retVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public String getPayloadString() {
|
||||
if (this.myPayload != null) {
|
||||
return this.myPayload;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setId(IIdType theId) {
|
||||
myId = null;
|
||||
if (theId != null) {
|
||||
myId = theId.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setNewPayload(FhirContext theCtx, IBaseResource theNewPayload) {
|
||||
/*
|
||||
* References with placeholders would be invalid by the time we get here, and
|
||||
* would be caught before we even get here. This check is basically a last-ditch
|
||||
* effort to make sure nothing has broken in the various safeguards that
|
||||
* should prevent this from happening (hence it only being an assert as
|
||||
* opposed to something executed all the time).
|
||||
*/
|
||||
assert payloadContainsNoPlaceholderReferences(theCtx, theNewPayload);
|
||||
|
||||
/*
|
||||
* Note: Don't set myPayloadDecoded in here- This is a false optimization since
|
||||
* it doesn't actually get used if anyone is doing subscriptions at any
|
||||
* scale using a queue engine, and not going through the serialize/deserialize
|
||||
* as we would in a queue engine can mask bugs.
|
||||
* -JA
|
||||
*/
|
||||
myPayload = theCtx.newJsonParser().encodeResourceToString(theNewPayload);
|
||||
myPayloadId = theNewPayload.getIdElement().toUnqualified().getValue();
|
||||
}
|
||||
|
||||
protected static boolean payloadContainsNoPlaceholderReferences(FhirContext theCtx, IBaseResource theNewPayload) {
|
||||
List<ResourceReferenceInfo> refs = theCtx.newTerser().getAllResourceReferences(theNewPayload);
|
||||
for (ResourceReferenceInfo next : refs) {
|
||||
String ref = next.getResourceReference().getReferenceElement().getValue();
|
||||
if (isBlank(ref)) {
|
||||
IBaseResource resource = next.getResourceReference().getResource();
|
||||
if (resource != null) {
|
||||
ref = resource.getIdElement().getValue();
|
||||
}
|
||||
}
|
||||
if (isNotBlank(ref)) {
|
||||
if (ref.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (ref.startsWith("urn:uuid:")) {
|
||||
throw new AssertionError("Reference at " + next.getName() + " is invalid: " + ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("myId", myId)
|
||||
.append("myOperationType", myOperationType)
|
||||
// .append("myPayload", myPayload)
|
||||
.append("myPayloadId", myPayloadId)
|
||||
// .append("myPayloadDecoded", myPayloadDecoded)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.subscription.model;
|
||||
package ca.uhn.fhir.rest.server.messaging;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Subscription Server
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
|
||||
public interface IResourceMessage {
|
||||
String getPayloadId();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package ca.uhn.fhir.rest.server.messaging;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
public class ResourceOperationMessage extends BaseResourceModifiedMessage {
|
||||
public ResourceOperationMessage() {
|
||||
}
|
||||
|
||||
public ResourceOperationMessage(FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) {
|
||||
super(theFhirContext, theResource, theOperationType);
|
||||
}
|
||||
|
||||
public ResourceOperationMessage(FhirContext theFhirContext, IBaseResource theNewResource, OperationTypeEnum theOperationType, RequestDetails theRequest) {
|
||||
super(theFhirContext, theNewResource, theOperationType, theRequest);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.subscription.model;
|
||||
package ca.uhn.fhir.rest.server.messaging.json;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Subscription Server
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.subscription.model;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
import ca.uhn.fhir.model.api.IModelJson;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.messaging.Message;
|
||||
|
@ -29,21 +30,32 @@ public abstract class BaseJsonMessage<T> implements Message<T>, IModelJson {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@JsonProperty("headers")
|
||||
private MessageHeaders myHeaders;
|
||||
private HapiMessageHeaders myHeaders;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BaseJsonMessage() {
|
||||
super();
|
||||
setDefaultRetryHeaders();
|
||||
}
|
||||
|
||||
protected void setDefaultRetryHeaders() {
|
||||
HapiMessageHeaders messageHeaders = new HapiMessageHeaders();
|
||||
setHeaders(messageHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageHeaders getHeaders() {
|
||||
return myHeaders.toMessageHeaders();
|
||||
}
|
||||
|
||||
public HapiMessageHeaders getHapiHeaders() {
|
||||
return myHeaders;
|
||||
}
|
||||
|
||||
public void setHeaders(MessageHeaders theHeaders) {
|
||||
|
||||
public void setHeaders(HapiMessageHeaders theHeaders) {
|
||||
myHeaders = theHeaders;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ca.uhn.fhir.rest.server.messaging.json;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.model.api.IModelJson;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class is for holding headers for BaseJsonMessages. Any serializable data can be thrown into
|
||||
* the header map. There are also three special headers, defined by the constants in this class, which are for use
|
||||
* in message handling retrying. There are also matching helper functions for fetching those special variables; however
|
||||
* they can also be accessed in standard map fashion with a `get` on the map.
|
||||
*/
|
||||
public class HapiMessageHeaders implements IModelJson {
|
||||
public static final String RETRY_COUNT_KEY = "retryCount";
|
||||
public static final String FIRST_FAILURE_KEY = "firstFailureTimestamp";
|
||||
public static final String LAST_FAILURE_KEY = "lastFailureTimestamp";
|
||||
|
||||
@JsonProperty(RETRY_COUNT_KEY)
|
||||
private Integer myRetryCount = 0;
|
||||
@JsonProperty(FIRST_FAILURE_KEY)
|
||||
private Long myFirstFailureTimestamp;
|
||||
@JsonProperty(LAST_FAILURE_KEY)
|
||||
private Long myLastFailureTimestamp;
|
||||
|
||||
@JsonProperty("customHeaders")
|
||||
private final Map<String, Object> headers;
|
||||
|
||||
public HapiMessageHeaders(Map<String, Object> theHeaders) {
|
||||
headers = theHeaders;
|
||||
}
|
||||
|
||||
public HapiMessageHeaders() {
|
||||
headers = new HashMap<>();
|
||||
}
|
||||
|
||||
public Integer getRetryCount() {
|
||||
return this.myRetryCount;
|
||||
}
|
||||
|
||||
public Long getFirstFailureDate() {
|
||||
return this.myFirstFailureTimestamp;
|
||||
}
|
||||
|
||||
public Long getLastFailureDate() {
|
||||
return this.myLastFailureTimestamp;
|
||||
}
|
||||
|
||||
public void setRetryCount(Integer theRetryCount) {
|
||||
this.myRetryCount = theRetryCount;
|
||||
}
|
||||
|
||||
public void setLastFailureDate(Long theLastFailureDate) {
|
||||
this.myLastFailureTimestamp = theLastFailureDate;
|
||||
}
|
||||
|
||||
public void setFirstFailureDate(Long theFirstFailureDate) {
|
||||
this.myFirstFailureTimestamp = theFirstFailureDate;
|
||||
}
|
||||
|
||||
public Map<String, Object> getCustomHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
public MessageHeaders toMessageHeaders() {
|
||||
Map<String, Object> returnedHeaders = new HashMap<>(this.headers);
|
||||
returnedHeaders.put(RETRY_COUNT_KEY, myRetryCount);
|
||||
returnedHeaders.put(FIRST_FAILURE_KEY, myFirstFailureTimestamp);
|
||||
returnedHeaders.put(LAST_FAILURE_KEY, myLastFailureTimestamp);
|
||||
return new MessageHeaders(returnedHeaders);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package ca.uhn.fhir.rest.server.messaging.json;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
public class ResourceOperationJsonMessage extends BaseJsonMessage<ResourceOperationMessage> {
|
||||
|
||||
|
||||
@JsonProperty("payload")
|
||||
private ResourceOperationMessage myPayload;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceOperationJsonMessage() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceOperationJsonMessage(ResourceOperationMessage thePayload) {
|
||||
myPayload = thePayload;
|
||||
setDefaultRetryHeaders();
|
||||
}
|
||||
|
||||
public ResourceOperationJsonMessage(HapiMessageHeaders theRetryMessageHeaders, ResourceOperationMessage thePayload) {
|
||||
myPayload = thePayload;
|
||||
setHeaders(theRetryMessageHeaders);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceOperationMessage getPayload() {
|
||||
return myPayload;
|
||||
}
|
||||
|
||||
public void setPayload(ResourceOperationMessage thePayload) {
|
||||
myPayload = thePayload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
.append("myPayload", myPayload)
|
||||
.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package ca.uhn.fhir.rest.server.messaging;
|
||||
|
||||
import ca.uhn.fhir.rest.server.messaging.json.ResourceOperationJsonMessage;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static ca.uhn.fhir.rest.server.messaging.json.HapiMessageHeaders.FIRST_FAILURE_KEY;
|
||||
import static ca.uhn.fhir.rest.server.messaging.json.HapiMessageHeaders.LAST_FAILURE_KEY;
|
||||
import static ca.uhn.fhir.rest.server.messaging.json.HapiMessageHeaders.RETRY_COUNT_KEY;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
class ResourceOperationMessageTest {
|
||||
|
||||
@Test
|
||||
public void testSerializationAndDeserializationOfResourceModifiedMessage() throws JsonProcessingException {
|
||||
ResourceOperationJsonMessage jsonMessage = new ResourceOperationJsonMessage();
|
||||
jsonMessage.setPayload(new ResourceOperationMessage());
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String serialized = mapper.writeValueAsString(jsonMessage);
|
||||
jsonMessage = mapper.readValue(serialized, ResourceOperationJsonMessage.class);
|
||||
|
||||
assertThat(jsonMessage.getHapiHeaders().getRetryCount(), is(equalTo(0)));
|
||||
assertThat(jsonMessage.getHapiHeaders().getFirstFailureDate(), is(equalTo(null)));
|
||||
assertThat(jsonMessage.getHapiHeaders().getLastFailureDate(), is(equalTo(null)));
|
||||
|
||||
assertThat(jsonMessage.getHeaders().get(RETRY_COUNT_KEY), is(equalTo(0)));
|
||||
assertThat(jsonMessage.getHeaders().get(FIRST_FAILURE_KEY), is(equalTo(null)));
|
||||
assertThat(jsonMessage.getHeaders().get(LAST_FAILURE_KEY), is(equalTo(null)));
|
||||
}
|
||||
}
|
|
@ -332,6 +332,7 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
|
|||
sortSearchParameters(searchParameters);
|
||||
if (!searchParameters.isEmpty()) {
|
||||
|
||||
Set<String> paramNames = new HashSet<>();
|
||||
for (SearchParameter nextParameter : searchParameters) {
|
||||
|
||||
if (nextParameter.getParamType() == null) {
|
||||
|
@ -346,6 +347,10 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
|
|||
nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
|
||||
}
|
||||
|
||||
if (!paramNames.add(nextParamUnchainedName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextParamDescription = nextParameter.getDescription();
|
||||
|
||||
/*
|
||||
|
@ -425,6 +430,8 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
|
|||
param.setMin(nextParam.isRequired() ? 1 : 0);
|
||||
param.setMax("1");
|
||||
param.setName(nextParam.getName());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,26 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
|
||||
|
@ -27,7 +42,8 @@ import ca.uhn.fhir.validation.ValidationResult;
|
|||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent;
|
||||
|
@ -35,10 +51,18 @@ import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResource
|
|||
import org.hl7.fhir.r4.model.CapabilityStatement.ConditionalDeleteStatus;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.SystemRestfulInteraction;
|
||||
import org.hl7.fhir.r4.model.CapabilityStatement.TypeRestfulInteraction;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.DiagnosticReport;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationKind;
|
||||
import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -49,6 +73,7 @@ import java.util.Collection;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
|
@ -65,8 +90,12 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
public class ServerCapabilityStatementProviderR4Test {
|
||||
|
||||
private static FhirContext ourCtx;
|
||||
public static final String PATIENT_SUB = "PatientSub";
|
||||
public static final String PATIENT_SUB_SUB = "PatientSubSub";
|
||||
public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2";
|
||||
public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProviderR4Test.class);
|
||||
private static FhirContext ourCtx;
|
||||
private static FhirValidator ourValidator;
|
||||
|
||||
static {
|
||||
|
@ -259,7 +288,9 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertNull(res.getConditionalUpdateElement().getValue());
|
||||
}
|
||||
|
||||
/** See #379 */
|
||||
/**
|
||||
* See #379
|
||||
*/
|
||||
@Test
|
||||
public void testOperationAcrossMultipleTypes() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
|
@ -513,10 +544,10 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
for (ResourceBinding resourceBinding : resourceBindings) {
|
||||
if (resourceBinding.getResourceName().equals("Patient")) {
|
||||
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
|
||||
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
|
||||
SearchParameter param = (SearchParameter) binding.getParameters().get(25);
|
||||
assertEquals("The organization at which this person is a patient", param.getDescription());
|
||||
found = true;
|
||||
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
|
||||
SearchParameter param = (SearchParameter) binding.getParameters().get(25);
|
||||
assertEquals("The organization at which this person is a patient", param.getDescription());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
|
@ -569,7 +600,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Test
|
||||
public void testSearchReferenceParameterWithList() throws Exception {
|
||||
|
||||
RestfulServer rsNoType = new RestfulServer(ourCtx){
|
||||
RestfulServer rsNoType = new RestfulServer(ourCtx) {
|
||||
@Override
|
||||
public RestfulServerConfiguration createConfiguration() {
|
||||
RestfulServerConfiguration retVal = super.createConfiguration();
|
||||
|
@ -586,7 +617,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
String confNoType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(confNoType);
|
||||
|
||||
RestfulServer rsWithType = new RestfulServer(ourCtx){
|
||||
RestfulServer rsWithType = new RestfulServer(ourCtx) {
|
||||
@Override
|
||||
public RestfulServerConfiguration createConfiguration() {
|
||||
RestfulServerConfiguration retVal = super.createConfiguration();
|
||||
|
@ -645,6 +676,46 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertThat(conf, containsString("<interaction><code value=\"" + TypeRestfulInteraction.HISTORYTYPE.toCode() + "\"/></interaction>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticIncludeChains() throws Exception {
|
||||
|
||||
class MyProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<DiagnosticReport> getResourceType() {
|
||||
return DiagnosticReport.class;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<IBaseResource> search(@RequiredParam(name = DiagnosticReport.SP_PATIENT + "." + Patient.SP_FAMILY) StringParam lastName,
|
||||
@RequiredParam(name = DiagnosticReport.SP_PATIENT + "." + Patient.SP_GIVEN) StringParam firstName,
|
||||
@RequiredParam(name = DiagnosticReport.SP_PATIENT + "." + Patient.SP_BIRTHDATE) DateParam dob,
|
||||
@OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam range) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
rs.setProviders(new MyProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
|
||||
};
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement opDef = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
|
||||
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(opDef);
|
||||
ourLog.info(conf);
|
||||
|
||||
CapabilityStatementRestResourceComponent resource = opDef.getRest().get(0).getResource().get(0);
|
||||
assertEquals("DiagnosticReport", resource.getType());
|
||||
List<String> searchParamNames = resource.getSearchParam().stream().map(t -> t.getName()).collect(Collectors.toList());
|
||||
assertThat(searchParamNames, containsInAnyOrder("patient", "date"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemLevelNamedQueryWithParameters() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
|
@ -729,8 +800,8 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertThat(param.getUse(), is(OperationParameterUse.IN));
|
||||
|
||||
CapabilityStatementRestResourceComponent patientResource = restComponent.getResource().stream()
|
||||
.filter(r -> patientResourceName.equals(r.getType()))
|
||||
.findAny().get();
|
||||
.filter(r -> patientResourceName.equals(r.getType()))
|
||||
.findAny().get();
|
||||
assertThat("Named query parameters should not appear in the resource search params", patientResource.getSearchParam(), is(empty()));
|
||||
}
|
||||
|
||||
|
@ -762,25 +833,25 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertThat(opDef.getInstance(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProfiledResourceStructureDefinitionLinks() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider());
|
||||
@Test
|
||||
public void testProfiledResourceStructureDefinitionLinks() throws Exception {
|
||||
RestfulServer rs = new RestfulServer(ourCtx);
|
||||
rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider());
|
||||
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
rs.setServerConformanceProvider(sc);
|
||||
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider();
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(createServletConfig());
|
||||
rs.init(createServletConfig());
|
||||
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance));
|
||||
|
||||
List<CapabilityStatementRestResourceComponent> resources = conformance.getRestFirstRep().getResource();
|
||||
CapabilityStatementRestResourceComponent patientResource = resources.stream()
|
||||
.filter(resource -> "Patient".equals(resource.getType()))
|
||||
.findFirst().get();
|
||||
assertThat(patientResource.getProfile(), containsString(PATIENT_SUB));
|
||||
}
|
||||
List<CapabilityStatementRestResourceComponent> resources = conformance.getRestFirstRep().getResource();
|
||||
CapabilityStatementRestResourceComponent patientResource = resources.stream()
|
||||
.filter(resource -> "Patient".equals(resource.getType()))
|
||||
.findFirst().get();
|
||||
assertThat(patientResource.getProfile(), containsString(PATIENT_SUB));
|
||||
}
|
||||
|
||||
private List<String> toOperationIdParts(List<CapabilityStatementRestResourceOperationComponent> theOperation) {
|
||||
ArrayList<String> retVal = Lists.newArrayList();
|
||||
|
@ -817,11 +888,6 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
assertTrue(result.isSuccessful(), outcome);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class ConditionalProvider implements IResourceProvider {
|
||||
|
||||
|
@ -866,7 +932,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Search(type = Patient.class)
|
||||
public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier,
|
||||
@Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) {
|
||||
@Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -942,7 +1008,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@SuppressWarnings("unused")
|
||||
public static class PlainProviderWithExtendedOperationOnNoType {
|
||||
|
||||
@Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) })
|
||||
@Operation(name = "plain", idempotent = true, returnParameters = {@OperationParam(min = 1, max = 2, name = "out1", type = StringType.class)})
|
||||
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart,
|
||||
@OperationParam(name = "end") DateType theEnd) {
|
||||
return null;
|
||||
|
@ -972,8 +1038,8 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
@Description(shortDefinition = "This is a search for stuff!")
|
||||
@Search
|
||||
public List<DiagnosticReport> findDiagnosticReportsByPatient(@RequiredParam(name = DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) TokenParam thePatientId,
|
||||
@OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
|
||||
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> theIncludes) throws Exception {
|
||||
@OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
|
||||
@IncludeParam(allow = {"DiagnosticReport.result"}) Set<Include> theIncludes) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1070,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
@Search(type = Patient.class)
|
||||
public Patient findPatient2(
|
||||
@Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = { Patient.class }) ReferenceAndListParam theLink) {
|
||||
@Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = {Patient.class}) ReferenceAndListParam theLink) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1014,15 +1080,15 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
public static class SearchProviderWithWhitelist {
|
||||
|
||||
@Search(type = Patient.class)
|
||||
public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION, chainWhitelist = { "foo",
|
||||
"bar" }) ReferenceAndListParam theIdentifier) {
|
||||
public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION, chainWhitelist = {"foo",
|
||||
"bar"}) ReferenceAndListParam theIdentifier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class SearchProviderWithListNoType implements IResourceProvider {
|
||||
public static class SearchProviderWithListNoType implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
|
@ -1030,7 +1096,6 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Search()
|
||||
public List<Patient> findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION) ReferenceAndListParam theIdentifier) {
|
||||
return null;
|
||||
|
@ -1039,7 +1104,7 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class SearchProviderWithListWithType implements IResourceProvider {
|
||||
public static class SearchProviderWithListWithType implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
|
@ -1047,15 +1112,13 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Search(type=Patient.class)
|
||||
@Search(type = Patient.class)
|
||||
public List<Patient> findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION) ReferenceAndListParam theIdentifier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class SystemHistoryProvider {
|
||||
|
||||
@History
|
||||
|
@ -1140,48 +1203,52 @@ public class ServerCapabilityStatementProviderR4Test {
|
|||
|
||||
}
|
||||
|
||||
public static class ProfiledPatientProvider implements IResourceProvider {
|
||||
public static class ProfiledPatientProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return PatientSubSub2.class;
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return PatientSubSub2.class;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<PatientSubSub2> find() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Search
|
||||
public List<PatientSubSub2> find() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultipleProfilesPatientProvider implements IResourceProvider {
|
||||
public static class MultipleProfilesPatientProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return PatientSubSub.class;
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return PatientSubSub.class;
|
||||
}
|
||||
|
||||
@Read(type = PatientTripleSub.class)
|
||||
public PatientTripleSub read(@IdParam IdType theId) {
|
||||
return null;
|
||||
}
|
||||
@Read(type = PatientTripleSub.class)
|
||||
public PatientTripleSub read(@IdParam IdType theId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static final String PATIENT_SUB = "PatientSub";
|
||||
public static final String PATIENT_SUB_SUB = "PatientSubSub";
|
||||
public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2";
|
||||
public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub";
|
||||
@ResourceDef(id = PATIENT_SUB)
|
||||
public static class PatientSub extends Patient {
|
||||
}
|
||||
|
||||
@ResourceDef(id = PATIENT_SUB)
|
||||
public static class PatientSub extends Patient {}
|
||||
@ResourceDef(id = PATIENT_SUB_SUB)
|
||||
public static class PatientSubSub extends PatientSub {
|
||||
}
|
||||
|
||||
@ResourceDef(id = PATIENT_SUB_SUB)
|
||||
public static class PatientSubSub extends PatientSub {}
|
||||
@ResourceDef(id = PATIENT_SUB_SUB_2)
|
||||
public static class PatientSubSub2 extends PatientSub {
|
||||
}
|
||||
|
||||
@ResourceDef(id = PATIENT_SUB_SUB_2)
|
||||
public static class PatientSubSub2 extends PatientSub {}
|
||||
@ResourceDef(id = PATIENT_TRIPLE_SUB)
|
||||
public static class PatientTripleSub extends PatientSubSub {
|
||||
}
|
||||
|
||||
@ResourceDef(id = PATIENT_TRIPLE_SUB)
|
||||
public static class PatientTripleSub extends PatientSubSub {}
|
||||
@AfterAll
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue