From 917cf8d062588d26b8746c32f877ed384c6d0d87 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 25 Sep 2022 17:29:08 -0400 Subject: [PATCH] Remove ActionRequestDetails (#3527) * Add test * Add test * Start removing unneeded things * Fixes * Add changelog * Test fix * Address LGTM issues * Resolve fixme * Address review comment * Add changelog * Update doc * Test fixes --- .../ca/uhn/fhir/interceptor/api/Pointcut.java | 6 +- .../main/java/ca/uhn/fhir/util/UrlUtil.java | 94 ++++++---- .../6_2_0/3527-drop-actionrequestdetails.yaml | 12 ++ ...owinginterceptor-and-batch-perf-issue.yaml | 6 + .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 27 +-- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 85 +-------- .../fhir/jpa/dao/BaseHapiFhirSystemDao.java | 12 +- .../jpa/dao/FhirResourceDaoPatientDstu2.java | 14 +- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 6 - .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 5 - .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 6 - .../uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java | 6 - .../r4/ResourceProviderInterceptorR4Test.java | 7 +- .../jpa/provider/r4/SystemProviderR4Test.java | 92 +++++++++- .../interceptor/AnalyticsInterceptor.java | 84 ++++----- .../interceptor/IServerInterceptor.java | 167 +----------------- .../interceptor/InterceptorAdapter.java | 28 ++- .../auth/AuthorizationInterceptor.java | 15 +- .../interceptor/auth/RuleImplConditional.java | 5 +- .../rest/server/method/BaseMethodBinding.java | 9 +- ...turningMethodBindingWithResourceParam.java | 13 +- .../method/ConformanceMethodBinding.java | 13 +- .../server/method/OperationMethodBinding.java | 9 +- .../rest/server/method/PageMethodBinding.java | 16 +- .../method/TransactionMethodBinding.java | 17 +- .../jobs/imprt/BulkDataImportProvider.java | 3 + .../provider/BulkDataExportProvider.java | 21 +-- .../ca/uhn/fhir/jpa/dao/BaseStorageDao.java | 94 +++++----- .../jpa/dao/BaseTransactionProcessor.java | 5 - .../InterceptorUserDataMapDstu2Test.java | 5 +- .../ServerActionInterceptorTest.java | 78 ++++---- .../rest/server/InterceptorDstu3Test.java | 76 ++++---- .../auth/AuthorizationInterceptorR4Test.java | 159 ++++++++++------- 33 files changed, 494 insertions(+), 701 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-drop-actionrequestdetails.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-searchnarrowinginterceptor-and-batch-perf-issue.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index e61dc2bb83c..84dd1dee46f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -344,9 +344,6 @@ public enum Pointcut implements IPointcut { *
  • * ca.uhn.fhir.rest.api.RestOperationTypeEnum - The type of operation that the FHIR server has determined that the client is trying to invoke *
  • - *
  • - * ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails - This parameter is provided for legacy reasons only and will be removed in the future. Do not use. - *
  • * *

    *

    @@ -360,8 +357,7 @@ public enum Pointcut implements IPointcut { SERVER_INCOMING_REQUEST_PRE_HANDLED(void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "ca.uhn.fhir.rest.api.RestOperationTypeEnum", - "ca.uhn.fhir.rest.server.interceptor.IServerInterceptor$ActionRequestDetails" + "ca.uhn.fhir.rest.api.RestOperationTypeEnum" ), diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index c93dabab28f..47384a87f58 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -62,47 +62,25 @@ public class UrlUtil { private static final String URL_FORM_PARAMETER_OTHER_SAFE_CHARS = "-_.*"; private static final Escaper PARAMETER_ESCAPER = new PercentEscaper(URL_FORM_PARAMETER_OTHER_SAFE_CHARS, false); - public static String sanitizeBaseUrl(String theBaseUrl) { - return theBaseUrl.replaceAll("[^a-zA-Z0-9:/._-]", ""); + /** + * Non instantiable + */ + private UrlUtil() { } - public static class UrlParts { - private String myParams; - private String myResourceId; - private String myResourceType; - private String myVersionId; + /** + * Cleans up a value that will be serialized as an HTTP header. This method: + *

    + * - Strips any newline (\r or \n) characters + * + * @since 6.2.0 + */ + public static String sanitizeHeaderValue(String theHeader) { + return theHeader.replace("\n", "").replace("\r", ""); + } - public String getParams() { - return myParams; - } - - public void setParams(String theParams) { - myParams = theParams; - } - - public String getResourceId() { - return myResourceId; - } - - public void setResourceId(String theResourceId) { - myResourceId = theResourceId; - } - - public String getResourceType() { - return myResourceType; - } - - public void setResourceType(String theResourceType) { - myResourceType = theResourceType; - } - - public String getVersionId() { - return myVersionId; - } - - public void setVersionId(String theVersionId) { - myVersionId = theVersionId; - } + public static String sanitizeBaseUrl(String theBaseUrl) { + return theBaseUrl.replaceAll("[^a-zA-Z0-9:/._-]", ""); } /** @@ -186,7 +164,6 @@ public class UrlUtil { .collect(Collectors.toList()); } - public static boolean isAbsolute(String theValue) { String value = theValue.toLowerCase(); return value.startsWith("http://") || value.startsWith("https://"); @@ -570,4 +547,43 @@ public class UrlUtil { return parameters; } + + public static class UrlParts { + private String myParams; + private String myResourceId; + private String myResourceType; + private String myVersionId; + + public String getParams() { + return myParams; + } + + public void setParams(String theParams) { + myParams = theParams; + } + + public String getResourceId() { + return myResourceId; + } + + public void setResourceId(String theResourceId) { + myResourceId = theResourceId; + } + + public String getResourceType() { + return myResourceType; + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public String getVersionId() { + return myVersionId; + } + + public void setVersionId(String theVersionId) { + myVersionId = theVersionId; + } + } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-drop-actionrequestdetails.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-drop-actionrequestdetails.yaml new file mode 100644 index 00000000000..e78fddcea17 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-drop-actionrequestdetails.yaml @@ -0,0 +1,12 @@ +--- +type: remove +issue: 3527 +title: "The `ActionRequestDetails` class has been dropped (it has been deprecated + since HAPI FHIR 4.0.0). This class was used as a parameter to the + `SERVER_INCOMING_REQUEST_PRE_HANDLED` interceptor pointcut, but can be + replaced in any existing client code with `RequestDetails`. This change + also removes an undocumented behaviour where the JPA server internally + invoked the `SERVER_INCOMING_REQUEST_PRE_HANDLED` a second time from + within various processing methods. This behaviour caused performance + problems for some interceptors (e.g. `SearchNarrowingInterceptor`) and + no longer offers any benefit so it is being removed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-searchnarrowinginterceptor-and-batch-perf-issue.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-searchnarrowinginterceptor-and-batch-perf-issue.yaml new file mode 100644 index 00000000000..14aeeb9149d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_2_0/3527-searchnarrowinginterceptor-and-batch-perf-issue.yaml @@ -0,0 +1,6 @@ +--- +type: perf +issue: 3527 +title: "When using SearchNarrowingInterceptor, FHIR batch operations with a large number + of conditional create/update entries exhibited very slow performance due to an + unnecessary nested loop. This has been corrected." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 52565ce2cb3..ef1110fe13f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -82,7 +82,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; @@ -224,28 +223,18 @@ public abstract class BaseHapiFhirDao extends BaseStora @Autowired ExpungeService myExpungeService; @Autowired - private HistoryBuilderFactory myHistoryBuilderFactory; - @Autowired private DaoConfig myConfig; @Autowired - private PlatformTransactionManager myPlatformTransactionManager; - @Autowired - private ISearchCacheSvc mySearchCacheSvc; - @Autowired private ISearchParamPresenceSvc mySearchParamPresenceSvc; @Autowired private SearchParamWithInlineReferencesExtractor mySearchParamWithInlineReferencesExtractor; @Autowired private DaoSearchParamSynchronizer myDaoSearchParamSynchronizer; - @Autowired - private SearchBuilderFactory mySearchBuilderFactory; private FhirContext myContext; private ApplicationContext myApplicationContext; @Autowired private PartitionSettings myPartitionSettings; @Autowired - private RequestPartitionHelperSvc myRequestPartitionHelperSvc; - @Autowired private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory; @Autowired private IPartitionLookupSvc myPartitionLookupSvc; @@ -272,7 +261,7 @@ public abstract class BaseHapiFhirDao extends BaseStora } @Override - public void setApplicationContext(ApplicationContext theApplicationContext) throws BeansException { + public void setApplicationContext(@Nonnull ApplicationContext theApplicationContext) throws BeansException { /* * We do a null check here because Smile's module system tries to * initialize the application context twice if two modules depend on @@ -363,9 +352,7 @@ public abstract class BaseHapiFhirDao extends BaseStora private Set getAllTagDefinitions(ResourceTable theEntity) { HashSet retVal = Sets.newHashSet(); if (theEntity.isHasTags()) { - for (ResourceTag next : theEntity.getTags()) { - retVal.add(next); - } + retVal.addAll(theEntity.getTags()); } return retVal; } @@ -1505,9 +1492,6 @@ public abstract class BaseHapiFhirDao extends BaseStora oldResource = toResource(entity, false); } - if (theRequest.getServer() != null) { - ActionRequestDetails actionRequestDetails = new ActionRequestDetails(theRequest, theResource, theResourceId.getResourceType(), theResourceId); - } notifyInterceptors(theRequest, theResource, oldResource, theTransactionDetails, true); ResourceTable savedEntity = updateEntity(theRequest, theResource, entity, null, true, false, theTransactionDetails, false, false); @@ -1684,13 +1668,6 @@ public abstract class BaseHapiFhirDao extends BaseStora // now at least set it to something useful for the interceptors theResource.setId(entity.getIdDt()); - // Notify interceptors - ActionRequestDetails requestDetails; - if (theRequestDetails != null && theRequestDetails.getServer() != null) { - requestDetails = new ActionRequestDetails(theRequestDetails, theResource, theResourceId.getResourceType(), theResourceId); - notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails); - } - // Notify IServerOperationInterceptors about pre-action call notifyInterceptors(theRequestDetails, theResource, theOldResource, theTransactionDetails, true); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 683c18499ab..4701343427b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -99,7 +99,6 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.ObjectUtil; @@ -126,7 +125,7 @@ import org.springframework.beans.factory.annotation.Required; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; @@ -155,9 +154,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseHapiFhirResourceDao extends BaseHapiFhirDao implements IFhirResourceDao { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class); public static final String BASE_RESOURCE_NAME = "resource"; - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class); @Autowired protected PlatformTransactionManager myPlatformTransactionManager; @Autowired(required = false) @@ -309,12 +307,6 @@ public abstract class BaseHapiFhirResourceDao extends B } } - // Notify interceptors - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getContext(), theResource); - notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails); - } - String resourceIdBeforeStorage = theResource.getIdElement().getIdPart(); boolean resourceHadIdBeforeStorage = isNotBlank(resourceIdBeforeStorage); boolean resourceIdWasServerAssigned = theResource.getUserData(JpaConstants.RESOURCE_ID_SERVER_ASSIGNED) == Boolean.TRUE; @@ -584,12 +576,6 @@ public abstract class BaseHapiFhirResourceDao extends B preDelete(resourceToDelete, entity, theRequestDetails); - // Notify interceptors - if (theRequestDetails != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId); - notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); - } - ResourceTable savedEntity = updateEntityForDelete(theRequestDetails, theTransactionDetails, entity); resourceToDelete.setId(entity.getIdDt()); @@ -704,20 +690,13 @@ public abstract class BaseHapiFhirResourceDao extends B myDeleteConflictService.validateOkToDelete(theDeleteConflicts, entity, false, theRequest, transactionDetails); - // Notify interceptors - IdDt idToDelete = entity.getIdDt(); - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, idToDelete.getResourceType(), idToDelete); - notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); - } - // Perform delete updateEntityForDelete(theRequest, transactionDetails, entity); resourceToDelete.setId(entity.getIdDt()); // Notify JPA interceptors - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void beforeCommit(boolean readOnly) { HookParams hookParams = new HookParams() @@ -937,10 +916,6 @@ public abstract class BaseHapiFhirResourceDao extends B @Override @Transactional public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails); - StopWatch w = new StopWatch(); IBundleProvider retVal = super.history(theRequestDetails, myResourceName, null, theSince, theUntil, theOffset); ourLog.debug("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart()); @@ -950,12 +925,6 @@ public abstract class BaseHapiFhirResourceDao extends B @Override @Transactional public IBundleProvider history(final IIdType theId, final Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequest) { - if (theRequest != null) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theId); - notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails); - } - StopWatch w = new StopWatch(); IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless(); @@ -990,7 +959,7 @@ public abstract class BaseHapiFhirResourceDao extends B addAllResourcesTypesToReindex(theBase, theRequestDetails, params); } - ReadPartitionIdRequestDetails details= new ReadPartitionIdRequestDetails(null, RestOperationTypeEnum.EXTENDED_OPERATION_SERVER, null, null, null); + ReadPartitionIdRequestDetails details = new ReadPartitionIdRequestDetails(null, RestOperationTypeEnum.EXTENDED_OPERATION_SERVER, null, null, null); RequestPartitionId requestPartition = myRequestPartitionHelperService.determineReadPartitionForRequest(theRequestDetails, null, details); params.setRequestPartitionId(requestPartition); @@ -1034,12 +1003,6 @@ public abstract class BaseHapiFhirResourceDao extends B public MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequest) { TransactionDetails transactionDetails = new TransactionDetails(); - // Notify interceptors - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theResourceId); - notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails); - } - StopWatch w = new StopWatch(); BaseHasResource entity = readEntity(theResourceId, theRequest); if (entity == null) { @@ -1069,12 +1032,6 @@ public abstract class BaseHapiFhirResourceDao extends B public MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequest) { TransactionDetails transactionDetails = new TransactionDetails(); - // Notify interceptors - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theResourceId); - notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails); - } - StopWatch w = new StopWatch(); BaseHasResource entity = readEntity(theResourceId, theRequest); if (entity == null) { @@ -1102,12 +1059,6 @@ public abstract class BaseHapiFhirResourceDao extends B @Override @Transactional public MT metaGetOperation(Class theType, IIdType theId, RequestDetails theRequest) { - // Notify interceptors - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theId); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - } - Set tagDefs = new HashSet<>(); BaseHasResource entity = readEntity(theId, theRequest); for (BaseTag next : entity.getTags()) { @@ -1124,12 +1075,6 @@ public abstract class BaseHapiFhirResourceDao extends B @Override @Transactional public MT metaGetOperation(Class theType, RequestDetails theRequestDetails) { - // Notify interceptors - if (theRequestDetails != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - } - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)"; TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); q.setParameter("res_type", myResourceName); @@ -1273,13 +1218,6 @@ public abstract class BaseHapiFhirResourceDao extends B public T doRead(IIdType theId, RequestDetails theRequest, boolean theDeletedOk) { assert TransactionSynchronizationManager.isActualTransactionActive(); - // Notify interceptors - if (theRequest != null && theRequest.getServer() != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theId); - RestOperationTypeEnum operationType = theId.hasVersionIdPart() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ; - notifyInterceptors(operationType, requestDetails); - } - StopWatch w = new StopWatch(); BaseHasResource entity = readEntity(theId, theRequest); validateResourceType(entity); @@ -1485,12 +1423,6 @@ public abstract class BaseHapiFhirResourceDao extends B @Transactional @Override public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequest) { - // Notify interceptors - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theId); - notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails); - } - StopWatch w = new StopWatch(); BaseHasResource entity = readEntity(theId, theRequest); if (entity == null) { @@ -1537,7 +1469,7 @@ public abstract class BaseHapiFhirResourceDao extends B if (theParams.getSearchContainedMode() != SearchContainedModeEnum.FALSE && !myModelConfig.isIndexOnContainedResources()) { throw new MethodNotAllowedException(Msg.code(984) + "Searching with _contained mode enabled is not enabled on this server"); } - + translateListSearchParams(theParams); notifySearchInterceptors(theParams, theRequest); @@ -1587,8 +1519,6 @@ public abstract class BaseHapiFhirResourceDao extends B private void notifySearchInterceptors(SearchParameterMap theParams, RequestDetails theRequest) { if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getContext(), getResourceName(), null); - notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails); if (theRequest.isSubRequest()) { Integer max = getConfig().getMaximumSearchResultCountInTransaction(); @@ -1928,11 +1858,6 @@ public abstract class BaseHapiFhirResourceDao extends B public MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequest) { TransactionDetails transactionDetails = new TransactionDetails(); - if (theRequest != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, theResource, null, theId); - notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails); - } - if (theMode == ValidationModeEnum.DELETE) { if (theId == null || theId.hasIdPart() == false) { throw new InvalidRequestException(Msg.code(991) + "No ID supplied. ID is required when validating with mode=DELETE"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java index 29a22be18a9..78739652c06 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java @@ -10,12 +10,10 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.search.builder.SearchBuilder; import ca.uhn.fhir.jpa.util.QueryChunker; import ca.uhn.fhir.jpa.util.ResourceCountCache; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -62,8 +60,8 @@ import java.util.stream.Collectors; public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao implements IFhirSystemDao { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class); public static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[0]; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class); public ResourceCountCache myResourceCountsCache; @Autowired private TransactionProcessor myTransactionProcessor; @@ -125,12 +123,6 @@ public abstract class BaseHapiFhirSystemDao extends B @Override public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) { - if (theRequestDetails != null) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails); - } - StopWatch w = new StopWatch(); IBundleProvider retVal = super.history(theRequestDetails, null, null, theSince, theUntil, theOffset); ourLog.info("Processed global history in {}ms", w.getMillisAndRestart()); @@ -242,8 +234,6 @@ public abstract class BaseHapiFhirSystemDao extends B }); - - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java index 2089189c0f5..09154a3d98a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java @@ -31,12 +31,13 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; @@ -91,9 +92,6 @@ public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao { @Override public MetaDt metaGetOperation(RequestDetails theRequestDetails) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); List tagDefinitions = q.getResultList(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 5b744599551..a0c96b07e38 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Meta; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -45,10 +44,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { @Override public Meta metaGetOperation(RequestDetails theRequestDetails) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); List tagDefinitions = q.getResultList(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 5c03e4cc620..d7f9596b02f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -23,9 +23,7 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2; import ca.uhn.fhir.jpa.model.entity.TagDefinition; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Meta; @@ -38,10 +36,6 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { @Override public Meta metaGetOperation(RequestDetails theRequestDetails) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); List tagDefinitions = q.getResultList(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java index 385533a81f7..eb7ffa901fe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java @@ -23,9 +23,7 @@ package ca.uhn.fhir.jpa.dao.r5; import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.FhirResourceDaoMessageHeaderDstu2; import ca.uhn.fhir.jpa.model.entity.TagDefinition; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Meta; @@ -40,10 +38,6 @@ public class FhirSystemDaoR5 extends BaseHapiFhirSystemDao { @Override public Meta metaGetOperation(RequestDetails theRequestDetails) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); List tagDefinitions = q.getResultList(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java index 6fcc54e3735..dc951b067f7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java @@ -224,8 +224,8 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes transaction(bundle); - verify(interceptor, timeout(10 * MILLIS_PER_SECOND).times(2)).invoke(eq(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED), myParamsCaptor.capture()); - assertEquals(RestOperationTypeEnum.CREATE, myParamsCaptor.getValue().get(RestOperationTypeEnum.class)); + verify(interceptor, timeout(10 * MILLIS_PER_SECOND).times(1)).invoke(eq(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED), myParamsCaptor.capture()); + assertEquals(RestOperationTypeEnum.TRANSACTION, myParamsCaptor.getValue().get(RestOperationTypeEnum.class)); verify(interceptor, timeout(10 * MILLIS_PER_SECOND).times(1)).invoke(eq(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED), myParamsCaptor.capture()); verify(interceptor, timeout(10 * MILLIS_PER_SECOND).times(1)).invoke(eq(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED), myParamsCaptor.capture()); @@ -326,9 +326,8 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes entry.getRequest().setIfNoneExist("Patient?name=" + methodName); transaction(bundle); - verify(interceptor, timeout(10 * MILLIS_PER_SECOND).times(2)).invoke(eq(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED), myParamsCaptor.capture()); + verify(interceptor, timeout(10 * MILLIS_PER_SECOND).times(1)).invoke(eq(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED), myParamsCaptor.capture()); assertEquals(RestOperationTypeEnum.TRANSACTION, myParamsCaptor.getAllValues().get(0).get(RestOperationTypeEnum.class)); - assertEquals(RestOperationTypeEnum.UPDATE, myParamsCaptor.getAllValues().get(1).get(RestOperationTypeEnum.class)); verify(interceptor, times(0)).invoke(eq(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED), any()); verify(interceptor, times(0)).invoke(eq(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED), any()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java index 43c3706f566..45c8f85de09 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java @@ -4,6 +4,9 @@ import ca.uhn.fhir.batch2.jobs.expunge.DeleteExpungeProvider; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; +import ca.uhn.fhir.interceptor.api.IPointcut; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.rp.r4.BinaryResourceProvider; @@ -13,6 +16,7 @@ import ca.uhn.fhir.jpa.rp.r4.ObservationResourceProvider; import ca.uhn.fhir.jpa.rp.r4.OrganizationResourceProvider; import ca.uhn.fhir.jpa.rp.r4.PatientResourceProvider; import ca.uhn.fhir.jpa.rp.r4.PractitionerResourceProvider; +import ca.uhn.fhir.jpa.rp.r4.PractitionerRoleResourceProvider; import ca.uhn.fhir.jpa.rp.r4.ServiceRequestResourceProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; @@ -21,6 +25,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; @@ -31,6 +36,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizedList; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.test.utilities.BatchJobHelper; import ca.uhn.fhir.test.utilities.JettyUtil; @@ -58,6 +65,7 @@ import org.hl7.fhir.r4.hapi.rest.server.helper.BatchHelperR4; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleType; import org.hl7.fhir.r4.model.Bundle.HTTPVerb; +import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.DecimalType; import org.hl7.fhir.r4.model.DiagnosticReport; @@ -69,12 +77,15 @@ import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; @@ -83,6 +94,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -117,6 +129,8 @@ public class SystemProviderR4Test extends BaseJpaR4Test { myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled()); myDaoConfig.setDeleteExpungeEnabled(new DaoConfig().isDeleteExpungeEnabled()); + myDaoConfig.setAutoCreatePlaceholderReferenceTargets(new DaoConfig().isAutoCreatePlaceholderReferenceTargets()); + myDaoConfig.setPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets(new DaoConfig().isPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets()); } @BeforeEach @@ -144,11 +158,15 @@ public class SystemProviderR4Test extends BaseJpaR4Test { diagnosticReportRp.setDao(myDiagnosticReportDao); ServiceRequestResourceProvider diagnosticOrderRp = new ServiceRequestResourceProvider(); diagnosticOrderRp.setDao(myServiceRequestDao); + PractitionerResourceProvider practitionerRp = new PractitionerResourceProvider(); practitionerRp.setDao(myPractitionerDao); + PractitionerRoleResourceProvider practitionerRoleRp = new PractitionerRoleResourceProvider(); + practitionerRoleRp.setDao(myPractitionerRoleDao); + RestfulServer restServer = new RestfulServer(ourCtx); - restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp, locationRp, binaryRp, diagnosticReportRp, diagnosticOrderRp, practitionerRp); + restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp, locationRp, binaryRp, diagnosticReportRp, diagnosticOrderRp, practitionerRp, practitionerRoleRp); restServer.registerProviders(mySystemProvider, myDeleteExpungeProvider); @@ -490,6 +508,78 @@ public class SystemProviderR4Test extends BaseJpaR4Test { } + /** + * Ensure that the interceptor is called an appropriate number of times + */ + @Test + public void testBatchWithMultipleConditionalCreates() { + + AtomicInteger counter0 = new AtomicInteger(0); + AtomicInteger counter1 = new AtomicInteger(0); + AtomicInteger counter2 = new AtomicInteger(0); + + class MyAnonymousInterceptor0 implements IAnonymousInterceptor { + + @Override + public void invoke(IPointcut thePointcut, HookParams theArgs) { + int count = counter0.incrementAndGet(); + ourLog.info("counter0 have been called {} times", count); + } + } + + class MyAnonymousInterceptor1 implements IAnonymousInterceptor { + + @Override + public void invoke(IPointcut thePointcut, HookParams theArgs) { + int count = counter1.incrementAndGet(); + ourLog.info("counter1 have been called {} times", count); + } + } + + class MySearchNarrowingInterceptor extends SearchNarrowingInterceptor { + private static final Logger ourLog = LoggerFactory.getLogger(MySearchNarrowingInterceptor.class); + + + @Override + protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) { + int count = counter2.incrementAndGet(); + ourLog.info("Have been called {} times", count); + return super.buildAuthorizedList(theRequestDetails); + } + } + + BundleBuilder bb = new BundleBuilder(myFhirContext); + bb.setType("batch"); + + for (int i = 0; i < 5; i++) { + Practitioner p0 = new Practitioner(); + p0.addIdentifier().setSystem("sys").setValue("p" + i); + bb.addTransactionCreateEntry(p0).conditional("Practitioner?identifier=sys|p" + i); + } + + // Avoid the auto-CS fetching affecting counts + myClient.capabilities().ofType(CapabilityStatement.class).execute(); + + Bundle input = (Bundle) bb.getBundle(); + + MyAnonymousInterceptor0 interceptor0 = new MyAnonymousInterceptor0(); + ourRestServer.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, interceptor0); + MyAnonymousInterceptor1 interceptor1 = new MyAnonymousInterceptor1(); + ourRestServer.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED, interceptor1); + MySearchNarrowingInterceptor interceptor2 = new MySearchNarrowingInterceptor(); + ourRestServer.getInterceptorService().registerInterceptor(interceptor2); + try { + myClient.transaction().withBundle(input).execute(); + assertEquals(1, counter0.get()); + assertEquals(1, counter1.get()); + assertEquals(5, counter2.get()); + + } finally { + ourRestServer.getInterceptorService().unregisterInterceptor(interceptor1); + } + } + + @Test public void testTransactionFromBundle() throws Exception { InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/transaction_link_patient_eve.xml"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java index ddc5687d799..cf438c864da 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -32,10 +33,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank; public class AnalyticsInterceptor extends InterceptorAdapter { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AnalyticsInterceptor.class); - + private final LinkedList myEventBuffer = new LinkedList<>(); private String myAnalyticsTid; private int myCollectThreshold = 100000; - private final LinkedList myEventBuffer = new LinkedList<>(); private String myHostname; private HttpClient myHttpClient; private long mySubmitPeriod = 60000; @@ -63,16 +63,6 @@ public class AnalyticsInterceptor extends InterceptorAdapter { mySchedulerService.scheduleLocalJob(5000, jobDetail); } - public static class Job implements HapiJob { - @Autowired - private AnalyticsInterceptor myAnalyticsInterceptor; - - @Override - public void execute(JobExecutionContext theContext) { - myAnalyticsInterceptor.flush(); - } - } - @PreDestroy public void stop() throws IOException { if (myHttpClient instanceof CloseableHttpClient) { @@ -105,13 +95,13 @@ public class AnalyticsInterceptor extends InterceptorAdapter { b.append("&an=").append(UrlUtil.escapeUrlParam(myHostname)).append('+').append(UrlUtil.escapeUrlParam(next.getApplicationName())); b.append("&ec=").append(next.getResourceName()); b.append("&ea=").append(next.getRestOperation()); - + b.append("&cid=").append(next.getClientId()); b.append("&uip=").append(UrlUtil.escapeUrlParam(next.getSourceIp())); b.append("&ua=").append(UrlUtil.escapeUrlParam(next.getUserAgent())); b.append("\n"); } - + String contents = b.toString(); HttpPost post = new HttpPost("https://www.google-analytics.com/batch"); post.setEntity(new StringEntity(contents, ContentType.APPLICATION_FORM_URLENCODED)); @@ -121,7 +111,7 @@ public class AnalyticsInterceptor extends InterceptorAdapter { } catch (Exception e) { ourLog.error("Failed to submit analytics:", e); } - + } private synchronized void flush() { @@ -145,15 +135,15 @@ public class AnalyticsInterceptor extends InterceptorAdapter { } @Override - public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequest) { - ServletRequestDetails details = (ServletRequestDetails) theRequest.getRequestDetails(); - + public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, RequestDetails theRequest) { + ServletRequestDetails details = (ServletRequestDetails) theRequest; + // Make sure we only send one event per request if (details.getUserData().containsKey(getClass().getName())) { return; } details.getUserData().put(getClass().getName(), ""); - + String sourceIp = details.getHeader("x-forwarded-for"); if (isBlank(sourceIp)) { sourceIp = details.getServletRequest().getRemoteAddr(); @@ -178,64 +168,74 @@ public class AnalyticsInterceptor extends InterceptorAdapter { myEventBuffer.add(event); } } - + public void setAnalyticsTid(String theAnalyticsTid) { myAnalyticsTid = theAnalyticsTid; } - + + public static class Job implements HapiJob { + @Autowired + private AnalyticsInterceptor myAnalyticsInterceptor; + + @Override + public void execute(JobExecutionContext theContext) { + myAnalyticsInterceptor.flush(); + } + } + public static class AnalyticsEvent { private String myApplicationName; private String myClientId; private String myResourceName; private RestOperationTypeEnum myRestOperation; private String mySourceIp; - + private String myUserAgent; String getApplicationName() { return myApplicationName; } - String getClientId() { - return myClientId; - } - - String getResourceName() { - return myResourceName; - } - - RestOperationTypeEnum getRestOperation() { - return myRestOperation; - } - - String getSourceIp() { - return mySourceIp; - } - - String getUserAgent() { - return myUserAgent; - } - void setApplicationName(String theApplicationName) { myApplicationName = theApplicationName; } + String getClientId() { + return myClientId; + } + void setClientId(String theClientId) { myClientId = theClientId; } + String getResourceName() { + return myResourceName; + } + void setResourceName(String theResourceName) { myResourceName = theResourceName; } + RestOperationTypeEnum getRestOperation() { + return myRestOperation; + } + void setRestOperation(RestOperationTypeEnum theRestOperation) { myRestOperation = theRestOperation; } + String getSourceIp() { + return mySourceIp; + } + void setSourceIp(String theSourceIp) { mySourceIp = theSourceIp; } + String getUserAgent() { + return myUserAgent; + } + void setUserAgent(String theUserAgent) { myUserAgent = theUserAgent; } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java index 7d941c0e886..69ed1b21be5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java @@ -119,7 +119,6 @@ public interface IServerInterceptor { * Invoked before an incoming request is processed. Note that this method is called * after the server has begin preparing the response to the incoming client request. * As such, it is not able to supply a response to the incoming request in the way that - * {@link #incomingRequestPreHandled(RestOperationTypeEnum, ActionRequestDetails)} and * {@link #incomingRequestPostProcessed(RequestDetails, HttpServletRequest, HttpServletResponse)} * are. *

    @@ -132,7 +131,7 @@ public interface IServerInterceptor { * server, e.g. the FHIR operation type and the parsed resource body (if any). */ @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED) - void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest); + void incomingRequestPreHandled(RestOperationTypeEnum theOperation, RequestDetails theProcessedRequest); /** * This method is called before any other processing takes place for each incoming request. It may be used to provide @@ -291,168 +290,4 @@ public interface IServerInterceptor { @Hook(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY) void processingCompletedNormally(ServletRequestDetails theRequestDetails); - /** - * @deprecated This class doesn't bring anything that can't be done with {@link RequestDetails}. That - * class should be used instead. Deprecated in 4.0.0 - */ - @Deprecated - class ActionRequestDetails { - private final FhirContext myContext; - private final IIdType myId; - private final String myResourceType; - private RequestDetails myRequestDetails; - private IBaseResource myResource; - - public ActionRequestDetails(RequestDetails theRequestDetails) { - myId = theRequestDetails.getId(); - myResourceType = theRequestDetails.getResourceName(); - myContext = theRequestDetails.getServer().getFhirContext(); - myRequestDetails = theRequestDetails; - } - - public ActionRequestDetails(RequestDetails theRequestDetails, FhirContext theContext, IBaseResource theResource) { - this(theRequestDetails, theContext, theContext.getResourceType(theResource), theResource.getIdElement()); - myResource = theResource; - } - - public ActionRequestDetails(RequestDetails theRequestDetails, FhirContext theContext, String theResourceType, IIdType theId) { - if (theId != null && isBlank(theId.getValue())) { - myId = null; - } else { - myId = theId; - } - myResourceType = theResourceType; - myContext = theContext; - myRequestDetails = theRequestDetails; - } - - public ActionRequestDetails(RequestDetails theRequestDetails, IBaseResource theResource) { - this(theRequestDetails, theRequestDetails.getServer().getFhirContext().getResourceType(theResource), theResource.getIdElement()); - myResource = theResource; - } - - public ActionRequestDetails(RequestDetails theRequestDetails, IBaseResource theResource, String theResourceType, IIdType theId) { - this(theRequestDetails, theResourceType, theId); - myResource = theResource; - } - - /** - * Constructor - * - * @param theRequestDetails The request details to wrap - * @param theId The ID of the resource being created (note that the ID should have the resource type populated) - */ - public ActionRequestDetails(RequestDetails theRequestDetails, IIdType theId) { - this(theRequestDetails, theId.getResourceType(), theId); - } - - public ActionRequestDetails(RequestDetails theRequestDetails, String theResourceType, IIdType theId) { - this(theRequestDetails, theRequestDetails.getServer().getFhirContext(), theResourceType, theId); - } - - public FhirContext getContext() { - return myContext; - } - - /** - * Returns the ID of the incoming request (typically this is from the request URL) - */ - public IIdType getId() { - return myId; - } - - /** - * Returns the request details associated with this request - */ - public RequestDetails getRequestDetails() { - return myRequestDetails; - } - - /** - * For requests where a resource is passed from the client to the server (e.g. create, update, etc.) this method - * will return the resource which was provided by the client. Otherwise, this method will return null - * . - *

    - * Note that this method is currently only populated if the handling method has a parameter annotated with the - * {@link ResourceParam} annotation. - *

    - */ - public IBaseResource getResource() { - return myResource; - } - - /** - * This method should not be called by client code - */ - public void setResource(IBaseResource theObject) { - myResource = theObject; - } - - /** - * Returns the resource type this request pertains to, or null if this request is not type specific - * (e.g. server-history) - */ - public String getResourceType() { - return myResourceType; - } - - @Override - public String toString() { - return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) - .append("id", myId) - .append("resourceType", myResourceType) - .append("resource", myResource) - .toString(); - } - - /** - * Returns the same map which was - */ - public Map getUserData() { - if (myRequestDetails == null) { - /* - * Technically this shouldn't happen.. But some of the unit tests use old IXXXDao methods that don't - * take in a RequestDetails object. Eventually I guess we should clean that up. - */ - return Collections.emptyMap(); - } - return myRequestDetails.getUserData(); - } - - /** - * This method may be invoked by user code to notify interceptors that a nested - * operation is being invoked which is denoted by this request details. - */ - public void notifyIncomingRequestPreHandled(RestOperationTypeEnum theOperationType) { - RequestDetails requestDetails = getRequestDetails(); - if (requestDetails == null) { - return; - } - IRestfulServerDefaults server = requestDetails.getServer(); - if (server == null) { - return; - } - - IIdType previousRequestId = requestDetails.getId(); - requestDetails.setId(getId()); - - IInterceptorService interceptorService = server.getInterceptorService(); - if (interceptorService == null) { - return; - } - - HookParams params = new HookParams(); - params.add(RestOperationTypeEnum.class, theOperationType); - params.add(this); - params.add(RequestDetails.class, this.getRequestDetails()); - params.addIfMatchesType(ServletRequestDetails.class, this.getRequestDetails()); - interceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, params); - - // Reset the request ID - requestDetails.setId(previousRequestId); - - } - - } - } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java index 8ab74d6d2ec..b884f888269 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java @@ -20,21 +20,19 @@ package ca.uhn.fhir.rest.server.interceptor; * #L% */ -import java.io.IOException; +import ca.uhn.fhir.model.api.TagList; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.ResponseDetails; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.hl7.fhir.instance.model.api.IBaseResource; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import ca.uhn.fhir.rest.api.server.ResponseDetails; -import org.hl7.fhir.instance.model.api.IBaseResource; - -import ca.uhn.fhir.model.api.TagList; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import java.io.IOException; /** * Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation @@ -44,7 +42,7 @@ public class InterceptorAdapter implements IServerInterceptor { @Override public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws ServletException, IOException { + throws ServletException, IOException { return true; } @@ -54,7 +52,7 @@ public class InterceptorAdapter implements IServerInterceptor { } @Override - public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) { + public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, RequestDetails theProcessedRequest) { // nothing } @@ -82,7 +80,7 @@ public class InterceptorAdapter implements IServerInterceptor { @Override public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws AuthenticationException { + throws AuthenticationException { return true; } @@ -99,7 +97,7 @@ public class InterceptorAdapter implements IServerInterceptor { @Override public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) - throws AuthenticationException { + throws AuthenticationException { return true; } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java index 217702e8501..0572877a1f7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java @@ -208,7 +208,8 @@ public class AuthorizationInterceptor implements IRuleApplier { return new ArrayList<>(); } - private OperationExamineDirection determineOperationDirection(RestOperationTypeEnum theOperation, IBaseResource theRequestResource) { + private OperationExamineDirection determineOperationDirection(RestOperationTypeEnum theOperation) { + switch (theOperation) { case ADD_TAGS: case DELETE_TAGS: @@ -232,13 +233,6 @@ public class AuthorizationInterceptor implements IRuleApplier { case CREATE: case UPDATE: case PATCH: - // if (theRequestResource != null) { - // if (theRequestResource.getIdElement() != null) { - // if (theRequestResource.getIdElement().hasIdPart() == false) { - // return OperationExamineDirection.IN_UNCATEGORIZED; - // } - // } - // } return OperationExamineDirection.IN; case META: @@ -363,7 +357,7 @@ public class AuthorizationInterceptor implements IRuleApplier { IBaseResource inputResource = null; IIdType inputResourceId = null; - switch (determineOperationDirection(theRequest.getRestOperationType(), theRequest.getResource())) { + switch (determineOperationDirection(theRequest.getRestOperationType())) { case IN: case BOTH: inputResource = theRequest.getResource(); @@ -422,7 +416,8 @@ public class AuthorizationInterceptor implements IRuleApplier { } private void checkOutgoingResourceAndFailIfDeny(RequestDetails theRequestDetails, IBaseResource theResponseObject, Pointcut thePointcut) { - switch (determineOperationDirection(theRequestDetails.getRestOperationType(), null)) { + + switch (determineOperationDirection(theRequestDetails.getRestOperationType())) { case IN: case NONE: return; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java index 86cb3f0974c..3f8293de6a5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java @@ -52,6 +52,9 @@ public class RuleImplConditional extends BaseRule implements IAuthRule { if (theRequestDetails.getConditionalUrl(myOperationType) == null) { return null; } + if (theInputResource == null) { + return null; + } switch (myAppliesTo) { case ALL_RESOURCES: @@ -65,7 +68,7 @@ public class RuleImplConditional extends BaseRule implements IAuthRule { } } else { String inputResourceName = theRequestDetails.getFhirContext().getResourceType(theInputResource); - if (theInputResource == null || !myAppliesToTypes.contains(inputResourceName)) { + if (!myAppliesToTypes.contains(inputResourceName)) { return null; } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java index 6a65485b23c..2fd871d0252 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java @@ -38,7 +38,6 @@ import ca.uhn.fhir.rest.server.BundleProviders; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ReflectionUtil; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -242,15 +241,13 @@ public abstract class BaseMethodBinding { RestOperationTypeEnum operationType = getRestOperationType(theRequest); if (operationType != null) { - ActionRequestDetails details = new ActionRequestDetails(theRequest); - populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams); + populateRequestDetailsForInterceptor(theRequest, theMethodParams); // Interceptor invoke: SERVER_INCOMING_REQUEST_PRE_HANDLED HookParams preHandledParams = new HookParams(); preHandledParams.add(RestOperationTypeEnum.class, theRequest.getRestOperationType()); preHandledParams.add(RequestDetails.class, theRequest); preHandledParams.addIfMatchesType(ServletRequestDetails.class, theRequest); - preHandledParams.add(ActionRequestDetails.class, details); if (theRequest.getInterceptorBroadcaster() != null) { theRequest .getInterceptorBroadcaster() @@ -293,12 +290,10 @@ public abstract class BaseMethodBinding { /** * Subclasses may override this method (but should also call super) to provide method specifics to the * interceptors. - * * @param theRequestDetails The server request details - * @param theDetails The details object to populate * @param theMethodParams The method params as generated by the specific method binding */ - protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) { + protected void populateRequestDetailsForInterceptor(RequestDetails theRequestDetails, Object[] theMethodParams) { // nothing by default } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceParam.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceParam.java index d78212e895d..f14a2dc5e7f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceParam.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceParam.java @@ -36,11 +36,10 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOutcomeReturningMethodBinding { - private Integer myIdParamIndex; - private String myResourceName; + private final Integer myIdParamIndex; + private final String myResourceName; private int myResourceParameterIndex = -1; private Class myResourceType; private Class myIdParamType; @@ -122,8 +121,8 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu } @Override - protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) { - super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams); + protected void populateRequestDetailsForInterceptor(RequestDetails theRequestDetails, Object[] theMethodParams) { + super.populateRequestDetailsForInterceptor(theRequestDetails, theMethodParams); /* * If the method has no parsed resource parameter, we parse here in order to have something for the interceptor. @@ -136,10 +135,6 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu } theRequestDetails.setResource(resource); - if (theDetails != null) { - theDetails.setResource(resource); - } - } /** diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java index 654780cee0b..58e9d670876 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java @@ -20,9 +20,9 @@ package ca.uhn.fhir.rest.server.method; * #L% */ -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.valueset.BundleTypeEnum; @@ -38,7 +38,6 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import org.hl7.fhir.instance.model.api.IBaseConformance; @@ -152,15 +151,15 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding // Handle server action interceptors RestOperationTypeEnum operationType = getRestOperationType(theRequest); if (operationType != null) { - IServerInterceptor.ActionRequestDetails details = new IServerInterceptor.ActionRequestDetails(theRequest); - populateActionRequestDetailsForInterceptor(theRequest, details, theMethodParams); + + populateRequestDetailsForInterceptor(theRequest, theMethodParams); + // Interceptor hook: SERVER_INCOMING_REQUEST_PRE_HANDLED if (theRequest.getInterceptorBroadcaster() != null) { HookParams preHandledParams = new HookParams(); preHandledParams.add(RestOperationTypeEnum.class, theRequest.getRestOperationType()); preHandledParams.add(RequestDetails.class, theRequest); preHandledParams.addIfMatchesType(ServletRequestDetails.class, theRequest); - preHandledParams.add(IServerInterceptor.ActionRequestDetails.class, details); theRequest .getInterceptorBroadcaster() .callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, preHandledParams); @@ -196,7 +195,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding myCachedResponse.set(conf); myCachedResponseExpires.set(System.currentTimeMillis() + getCacheMillis()); } - + return conf; } @@ -239,7 +238,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding public IBaseConformance provideCapabilityStatement(RestfulServer theServer, RequestDetails theRequest) { Object[] params = createMethodParams(theRequest); IBundleProvider resultObj = invokeServer(theServer, theRequest, params); - return (IBaseConformance) resultObj.getResources(0,1).get(0); + return (IBaseConformance) resultObj.getResources(0, 1).get(0); } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java index 1f01aa60890..185a3ba0e19 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java @@ -38,7 +38,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.ParametersUtil; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -373,14 +372,10 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { } @Override - protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails - theDetails, Object[] theMethodParams) { - super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams); + protected void populateRequestDetailsForInterceptor(RequestDetails theRequestDetails, Object[] theMethodParams) { + super.populateRequestDetailsForInterceptor(theRequestDetails, theMethodParams); IBaseResource resource = (IBaseResource) theRequestDetails.getUserData().get(OperationParameter.REQUEST_CONTENTS_USERDATA_KEY); theRequestDetails.setResource(resource); - if (theDetails != null) { - theDetails.setResource(resource); - } } public boolean isManualRequestMode() { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java index f009e993417..8c3e7fee09c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java @@ -20,8 +20,8 @@ package ca.uhn.fhir.rest.server.method; * #L% */ -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.api.Include; @@ -39,7 +39,6 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ReflectionUtil; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -54,12 +53,12 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class PageMethodBinding extends BaseResourceReturningMethodBinding { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PageMethodBinding.class); + public PageMethodBinding(FhirContext theContext, Method theMethod) { super(null, theMethod, theContext, null); } - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PageMethodBinding.class); - public IBaseResource provider() { return null; } @@ -83,7 +82,7 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding { public IBaseResource doInvokeServer(IRestfulServer theServer, RequestDetails theRequest) { return handlePagingRequest(theServer, theRequest, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]); } - + private IBaseResource handlePagingRequest(IRestfulServer theServer, RequestDetails theRequest, String thePagingAction) { IPagingProvider pagingProvider = theServer.getPagingProvider(); if (pagingProvider == null) { @@ -91,13 +90,11 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding { } // Interceptor invoke: SERVER_INCOMING_REQUEST_PRE_HANDLED - IServerInterceptor.ActionRequestDetails details = new IServerInterceptor.ActionRequestDetails(theRequest); - populateActionRequestDetailsForInterceptor(theRequest, details, ReflectionUtil.EMPTY_OBJECT_ARRAY); + populateRequestDetailsForInterceptor(theRequest, ReflectionUtil.EMPTY_OBJECT_ARRAY); HookParams preHandledParams = new HookParams(); preHandledParams.add(RestOperationTypeEnum.class, theRequest.getRestOperationType()); preHandledParams.add(RequestDetails.class, theRequest); preHandledParams.addIfMatchesType(ServletRequestDetails.class, theRequest); - preHandledParams.add(IServerInterceptor.ActionRequestDetails.class, details); if (theRequest.getInterceptorBroadcaster() != null) { theRequest .getInterceptorBroadcaster() @@ -152,8 +149,7 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding { } } - String linkSelfBase = theRequest.getFhirServerBase(); // myServerAddressStrategy.determineServerBase(getServletContext(), - // theRequest.getServletRequest()); + String linkSelfBase = theRequest.getFhirServerBase(); String completeUrl = theRequest.getCompleteUrl(); String linkSelf = linkSelfBase + completeUrl.substring(theRequest.getCompleteUrl().indexOf('?')); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java index 7374af3fe25..3d26b76f6b7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java @@ -20,9 +20,9 @@ package ca.uhn.fhir.rest.server.method; * #L% */ -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.rest.annotation.Transaction; @@ -34,7 +34,6 @@ import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.method.TransactionParameter.ParamStyle; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -58,8 +57,8 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding if (next instanceof TransactionParameter) { if (myTransactionParamIndex != -1) { throw new ConfigurationException(Msg.code(372) + "Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " has multiple parameters annotated with the @" - + TransactionParam.class + " annotation, exactly one is required for @" + Transaction.class - + " methods"); + + TransactionParam.class + " annotation, exactly one is required for @" + Transaction.class + + " methods"); } myTransactionParamIndex = index; myTransactionParamStyle = ((TransactionParameter) next).getParamStyle(); @@ -69,7 +68,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding if (myTransactionParamIndex == -1) { throw new ConfigurationException(Msg.code(373) + "Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with the @" - + TransactionParam.class + " annotation"); + + TransactionParam.class + " annotation"); } } @@ -143,8 +142,8 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding } @Override - protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) { - super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams); + protected void populateRequestDetailsForInterceptor(RequestDetails theRequestDetails, Object[] theMethodParams) { + super.populateRequestDetailsForInterceptor(theRequestDetails, theMethodParams); /* * If the method has no parsed resource parameter, we parse here in order to have something for the interceptor. @@ -158,10 +157,6 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding } theRequestDetails.setResource(resource); - if (theDetails != null) { - theDetails.setResource(resource); - } - } } diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java index bc035c28824..cce3aa4a4d5 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.ParametersUtil; +import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -262,6 +263,8 @@ public class BulkDataImportProvider { public void writePollingLocationToResponseHeaders(ServletRequestDetails theRequestDetails, String theJobId) { String pollLocation = createPollLocationLink(theRequestDetails, theJobId); + pollLocation = UrlUtil.sanitizeHeaderValue(pollLocation); + HttpServletResponse response = theRequestDetails.getServletResponse(); // Add standard headers theRequestDetails.getServer().addHeadersToResponse(response); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java index af9d2e95278..3f0a8e9e746 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java @@ -53,6 +53,7 @@ import ca.uhn.fhir.util.ArrayUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.SearchParameterUtil; +import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IIdType; @@ -113,7 +114,7 @@ public class BulkDataExportProvider { } private void startJob(ServletRequestDetails theRequestDetails, - BulkDataExportOptions theOptions){ + BulkDataExportOptions theOptions) { // permission check HookParams params = (new HookParams()).add(BulkDataExportOptions.class, theOptions) .add(RequestDetails.class, theRequestDetails) @@ -124,7 +125,6 @@ public class BulkDataExportProvider { boolean useCache = shouldUseCache(theRequestDetails); - BulkExportParameters parameters = BulkExportUtils.createBulkExportJobParametersFromExportOptions(theOptions); parameters.setUseExistingJobsFirst(useCache); @@ -434,6 +434,7 @@ public class BulkDataExportProvider { throw new InternalErrorException(Msg.code(2136) + "Unable to get the server base."); } String pollLocation = serverBase + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + theOutcome.getJobMetadataId(); + pollLocation = UrlUtil.sanitizeHeaderValue(pollLocation); HttpServletResponse response = theRequestDetails.getServletResponse(); @@ -445,14 +446,6 @@ public class BulkDataExportProvider { response.setStatus(Constants.STATUS_HTTP_202_ACCEPTED); } - public static void validatePreferAsyncHeader(ServletRequestDetails theRequestDetails, String theOperationName) { - String preferHeader = theRequestDetails.getHeader(Constants.HEADER_PREFER); - PreferHeader prefer = RestfulServerUtils.parsePreferHeader(null, preferHeader); - if (prefer.getRespondAsync() == false) { - throw new InvalidRequestException(Msg.code(513) + "Must request async processing for " + theOperationName); - } - } - private Set splitTypeFilters(List> theTypeFilter) { if (theTypeFilter == null) { return null; @@ -470,4 +463,12 @@ public class BulkDataExportProvider { return retVal; } + + public static void validatePreferAsyncHeader(ServletRequestDetails theRequestDetails, String theOperationName) { + String preferHeader = theRequestDetails.getHeader(Constants.HEADER_PREFER); + PreferHeader prefer = RestfulServerUtils.parsePreferHeader(null, preferHeader); + if (prefer.getRespondAsync() == false) { + throw new InvalidRequestException(Msg.code(513) + "Must request async processing for " + theOperationName); + } + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java index 6d3b7c58010..a5cfbeeeffa 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java @@ -40,7 +40,6 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil; import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.rest.api.QualifiedParamList; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails; import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -49,12 +48,10 @@ import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.param.QualifierDetails; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; @@ -106,42 +103,6 @@ public abstract class BaseStorageDao { @Autowired protected DaoConfig myDaoConfig; - /** - * @see ModelConfig#getAutoVersionReferenceAtPaths() - */ - @Nonnull - public static Set extractReferencesToAutoVersion(FhirContext theFhirContext, ModelConfig theModelConfig, IBaseResource theResource) { - Map references = Collections.emptyMap(); - if (!theModelConfig.getAutoVersionReferenceAtPaths().isEmpty()) { - String resourceName = theFhirContext.getResourceType(theResource); - for (String nextPath : theModelConfig.getAutoVersionReferenceAtPathsByResourceType(resourceName)) { - List nextReferences = theFhirContext.newTerser().getValues(theResource, nextPath, IBaseReference.class); - for (IBaseReference next : nextReferences) { - if (next.getReferenceElement().hasVersionIdPart()) { - continue; - } - if (references.isEmpty()) { - references = new IdentityHashMap<>(); - } - references.put(next, null); - } - } - } - return references.keySet(); - } - - public static void clearRequestAsProcessingSubRequest(RequestDetails theRequestDetails) { - if (theRequestDetails != null) { - theRequestDetails.getUserData().remove(PROCESSING_SUB_REQUEST); - } - } - - public static void markRequestAsProcessingSubRequest(RequestDetails theRequestDetails) { - if (theRequestDetails != null) { - theRequestDetails.getUserData().put(PROCESSING_SUB_REQUEST, Boolean.TRUE); - } - } - @VisibleForTesting public void setSearchParamRegistry(ISearchParamRegistry theSearchParamRegistry) { mySearchParamRegistry = theSearchParamRegistry; @@ -171,7 +132,7 @@ public abstract class BaseStorageDao { verifyBundleTypeIsAppropriateForStorage(theResource); - if(!getConfig().getTreatBaseUrlsAsLocal().isEmpty()) { + if (!getConfig().getTreatBaseUrlsAsLocal().isEmpty()) { replaceAbsoluteReferencesWithRelative(theResource, myFhirContext.newTerser()); } @@ -219,16 +180,16 @@ public abstract class BaseStorageDao { * Replace absolute references with relative ones if configured to do so */ private void replaceAbsoluteReferencesWithRelative(IBaseResource theResource, FhirTerser theTerser) { - List refs = theTerser.getAllResourceReferences(theResource); - for (ResourceReferenceInfo nextRef : refs) { - IIdType refId = nextRef.getResourceReference().getReferenceElement(); - if (refId != null && refId.hasBaseUrl()) { - if (getConfig().getTreatBaseUrlsAsLocal().contains(refId.getBaseUrl())) { - IIdType newRefId = refId.toUnqualified(); - nextRef.getResourceReference().setReference(newRefId.getValue()); - } + List refs = theTerser.getAllResourceReferences(theResource); + for (ResourceReferenceInfo nextRef : refs) { + IIdType refId = nextRef.getResourceReference().getReferenceElement(); + if (refId != null && refId.hasBaseUrl()) { + if (getConfig().getTreatBaseUrlsAsLocal().contains(refId.getBaseUrl())) { + IIdType newRefId = refId.toUnqualified(); + nextRef.getResourceReference().setReference(newRefId.getValue()); } } + } } /** @@ -463,15 +424,40 @@ public abstract class BaseStorageDao { } } - public void notifyInterceptors(RestOperationTypeEnum theOperationType, IServerInterceptor.ActionRequestDetails theRequestDetails) { - if (theRequestDetails.getId() != null && theRequestDetails.getId().hasResourceType() && isNotBlank(theRequestDetails.getResourceType())) { - if (theRequestDetails.getId().getResourceType().equals(theRequestDetails.getResourceType()) == false) { - throw new InternalErrorException(Msg.code(525) + "Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType()); + /** + * @see ModelConfig#getAutoVersionReferenceAtPaths() + */ + @Nonnull + public static Set extractReferencesToAutoVersion(FhirContext theFhirContext, ModelConfig theModelConfig, IBaseResource theResource) { + Map references = Collections.emptyMap(); + if (!theModelConfig.getAutoVersionReferenceAtPaths().isEmpty()) { + String resourceName = theFhirContext.getResourceType(theResource); + for (String nextPath : theModelConfig.getAutoVersionReferenceAtPathsByResourceType(resourceName)) { + List nextReferences = theFhirContext.newTerser().getValues(theResource, nextPath, IBaseReference.class); + for (IBaseReference next : nextReferences) { + if (next.getReferenceElement().hasVersionIdPart()) { + continue; + } + if (references.isEmpty()) { + references = new IdentityHashMap<>(); + } + references.put(next, null); + } } } + return references.keySet(); + } - if (theRequestDetails.getUserData().get(PROCESSING_SUB_REQUEST) == Boolean.TRUE) { - theRequestDetails.notifyIncomingRequestPreHandled(theOperationType); + public static void clearRequestAsProcessingSubRequest(RequestDetails theRequestDetails) { + if (theRequestDetails != null) { + theRequestDetails.getUserData().remove(PROCESSING_SUB_REQUEST); } } + + public static void markRequestAsProcessingSubRequest(RequestDetails theRequestDetails) { + if (theRequestDetails != null) { + theRequestDetails.getUserData().put(PROCESSING_SUB_REQUEST, Boolean.TRUE); + } + } + } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index fae565a0415..6dd06649a92 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -203,11 +203,6 @@ public abstract class BaseTransactionProcessor { } public BUNDLE transaction(RequestDetails theRequestDetails, BUNDLE theRequest, boolean theNestedMode) { - if (theRequestDetails != null && theRequestDetails.getServer() != null && myDao != null) { - IServerInterceptor.ActionRequestDetails requestDetails = new IServerInterceptor.ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null); - myDao.notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails); - } - String actionName = "Transaction"; IBaseBundle response = processTransactionAsSubRequest(theRequestDetails, theRequest, actionName, theNestedMode); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java index 5d2b71725db..8209c5c1ee5 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java @@ -23,7 +23,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; @@ -132,7 +131,7 @@ public class InterceptorUserDataMapDstu2Test { } @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED) - public void incomingRequestPreHandled(ActionRequestDetails theRequestDetails) { + public void incomingRequestPreHandled(RequestDetails theRequestDetails) { updateMapUsing(theRequestDetails.getUserData(), "incomingRequestPreHandled"); } @@ -185,7 +184,7 @@ public class InterceptorUserDataMapDstu2Test { } public Map getIdToPatient() { - Map idToPatient = new HashMap(); + Map idToPatient = new HashMap<>(); { Patient patient = createPatient1(); idToPatient.put("1", patient); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java index 1ee99c467b2..48b2924cf7f 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java @@ -20,7 +20,6 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; @@ -62,13 +61,6 @@ public class ServerActionInterceptorTest { private static IServerInterceptor ourInterceptor; private static IGenericClient ourFhirClient; - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @Test public void testRead() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); @@ -77,10 +69,10 @@ public class ServerActionInterceptorTest { assertEquals(200, status.getStatusLine().getStatusCode()); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); + RequestDetails details = detailsCapt.getValue(); assertEquals("Patient/123", details.getId().getValue()); } @@ -92,10 +84,10 @@ public class ServerActionInterceptorTest { assertEquals(200, status.getStatusLine().getStatusCode()); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); + RequestDetails details = detailsCapt.getValue(); assertEquals("Patient/123/_history/456", details.getId().getValue()); } @@ -105,11 +97,11 @@ public class ServerActionInterceptorTest { patient.addName().addFamily("FAMILY"); ourFhirClient.create().resource(patient).execute(); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Patient", details.getResourceType()); + RequestDetails details = detailsCapt.getValue(); + assertEquals("Patient", details.getResourceName()); assertEquals(Patient.class, details.getResource().getClass()); assertEquals("FAMILY", ((Patient) details.getResource()).getName().get(0).getFamily().get(0).getValue()); } @@ -120,11 +112,11 @@ public class ServerActionInterceptorTest { observation.getCode().setText("OBSCODE"); ourFhirClient.create().resource(observation).execute(); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Observation", details.getResourceType()); + RequestDetails details = detailsCapt.getValue(); + assertEquals("Observation", details.getResourceName()); assertEquals(Observation.class, details.getResource().getClass()); assertEquals("OBSCODE", ((Observation) details.getResource()).getCode().getText()); } @@ -136,11 +128,11 @@ public class ServerActionInterceptorTest { patient.setId("Patient/123"); ourFhirClient.update().resource(patient).execute(); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); - ActionRequestDetails details = detailsCapt.getValue(); - assertEquals("Patient", details.getResourceType()); + RequestDetails details = detailsCapt.getValue(); + assertEquals("Patient", details.getResourceName()); assertEquals("Patient/123", details.getId().getValue()); assertEquals(Patient.class, details.getResource().getClass()); assertEquals("FAMILY", ((Patient) details.getResource()).getName().get(0).getFamily().get(0).getValue()); @@ -155,7 +147,7 @@ public class ServerActionInterceptorTest { assertEquals(200, status.getStatusLine().getStatusCode()); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture()); } @@ -167,9 +159,9 @@ public class ServerActionInterceptorTest { assertEquals(200, status.getStatusLine().getStatusCode()); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture()); - assertEquals("Patient", detailsCapt.getValue().getResourceType()); + assertEquals("Patient", detailsCapt.getValue().getResourceName()); } @Test @@ -180,12 +172,29 @@ public class ServerActionInterceptorTest { assertEquals(200, status.getStatusLine().getStatusCode()); - ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture()); - assertEquals("Patient", detailsCapt.getValue().getResourceType()); + assertEquals("Patient", detailsCapt.getValue().getResourceName()); assertEquals("Patient/123", detailsCapt.getValue().getId().getValue()); } + @BeforeEach + public void before() { + reset(ourInterceptor); + + when(ourInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(ourInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + } + + @AfterAll + public static void afterClassClearContext() throws Exception { + JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); + } + @BeforeAll public static void beforeClass() throws Exception { ourServer = new Server(0); @@ -202,7 +211,7 @@ public class ServerActionInterceptorTest { proxyHandler.addServletWithMapping(servletHolder, "/*"); ourServer.setHandler(proxyHandler); JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); + ourPort = JettyUtil.getPortForStartedServer(ourServer); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); HttpClientBuilder builder = HttpClientBuilder.create(); @@ -212,23 +221,12 @@ public class ServerActionInterceptorTest { ourInterceptor = mock(InterceptorAdapter.class); servlet.registerInterceptor(ourInterceptor); - ourCtx.getRestfulClientFactory().setSocketTimeout(240*1000); + ourCtx.getRestfulClientFactory().setSocketTimeout(240 * 1000); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); } - @BeforeEach - public void before() { - reset(ourInterceptor); - - when(ourInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - } - public static class PlainProvider { @History() @@ -300,6 +298,6 @@ public class ServerActionInterceptorTest { return new MethodOutcome(retVal.getId()); } } - + } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java index a69235ea2e3..1f636645ada 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java @@ -18,14 +18,12 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; @@ -186,9 +184,9 @@ public class InterceptorDstu3Test { when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class), nullable(IBaseResource.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor2.outgoingResponse(nullable(RequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); - doAnswer(t->{ + doAnswer(t -> { RestOperationTypeEnum type = (RestOperationTypeEnum) t.getArguments()[0]; - ActionRequestDetails det = (ActionRequestDetails) t.getArguments()[1]; + RequestDetails det = (RequestDetails) t.getArguments()[1]; return null; }).when(myInterceptor1).incomingRequestPreHandled(any(), any()); @@ -206,9 +204,9 @@ public class InterceptorDstu3Test { order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); order.verify(myInterceptor2, times(1)).incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); ArgumentCaptor opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(RequestDetails.class); order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture()); - order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(nullable(RestOperationTypeEnum.class), nullable(ActionRequestDetails.class)); + order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(nullable(RestOperationTypeEnum.class), nullable(RequestDetails.class)); assertEquals(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, opTypeCapt.getValue()); assertNotNull(arTypeCapt.getValue().getResource()); @@ -261,7 +259,7 @@ public class InterceptorDstu3Test { verify(myInterceptor1, times(1)).incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); verify(myInterceptor1, times(1)).incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); ArgumentCaptor opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(RequestDetails.class); ArgumentCaptor rdCapt = ArgumentCaptor.forClass(ServletRequestDetails.class); ArgumentCaptor resourceCapt = ArgumentCaptor.forClass(OperationOutcome.class); verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture()); @@ -296,7 +294,7 @@ public class InterceptorDstu3Test { order.verify(myInterceptor1, times(1)).incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class)); ArgumentCaptor opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class); - ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class); + ArgumentCaptor arTypeCapt = ArgumentCaptor.forClass(RequestDetails.class); ArgumentCaptor resourceCapt = ArgumentCaptor.forClass(OperationOutcome.class); order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture()); order.verify(myInterceptor1, times(1)).outgoingResponse(nullable(ServletRequestDetails.class), resourceCapt.capture()); @@ -315,6 +313,35 @@ public class InterceptorDstu3Test { i.resourceUpdated(null, null, null); } + @AfterAll + public static void afterClassClearContext() throws Exception { + JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); + } + + @BeforeAll + public static void beforeClass() throws Exception { + ourServer = new Server(0); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + ourServlet = new RestfulServer(ourCtx); + ourServlet.setResourceProviders(patientProvider); + ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ServletHolder servletHolder = new ServletHolder(ourServlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + JettyUtil.startServer(ourServer); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + public static class DummyPatientResourceProvider implements IResourceProvider { @Create() @@ -323,10 +350,10 @@ public class InterceptorDstu3Test { return new MethodOutcome().setCreated(true); } - @Operation(name="$postOperation") + @Operation(name = "$postOperation") public Parameters postOperation( @OperationParam(name = "limit") IntegerType theLimit - ) { + ) { return new Parameters(); } @@ -350,33 +377,4 @@ public class InterceptorDstu3Test { } - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorR4Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorR4Test.java index a056e3ea2cf..7151de55c37 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorR4Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorR4Test.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.i18n.Msg; 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.interceptor.model.RequestPartitionId; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Create; @@ -31,7 +32,9 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PatchTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenAndListParam; @@ -39,7 +42,6 @@ import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.test.utilities.JettyUtil; @@ -112,15 +114,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; public class AuthorizationInterceptorR4Test { - private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\""+ Msg.code(334) + "Access denied by default policy (no applicable rules)\"}]}"; + private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\"" + Msg.code(334) + "Access denied by default policy (no applicable rules)\"}]}"; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AuthorizationInterceptorR4Test.class); private static CloseableHttpClient ourClient; private static String ourConditionalCreateId; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4(); private static boolean ourHitMethod; private static int ourPort; private static List ourReturn; @@ -1173,7 +1174,7 @@ public class AuthorizationInterceptorR4Test { } - @Test + @Test public void testCodeIn_TransactionCreate() throws IOException { ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @Override @@ -2540,7 +2541,7 @@ public class AuthorizationInterceptorR4Test { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().withTester(null /* null should be ignored */ ).withTester(new IAuthRuleTester() { + .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().withTester(null /* null should be ignored */).withTester(new IAuthRuleTester() { @Override public boolean matches(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IIdType theInputResourceId, IBaseResource theInputResource) { return theInputResourceId.getIdPart().equals("1"); @@ -4202,6 +4203,47 @@ public class AuthorizationInterceptorR4Test { assertTrue(ourHitMethod); } + @AfterAll + public static void afterClassClearContext() throws Exception { + JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); + } + + @BeforeAll + public static void beforeClass() throws Exception { + ourServer = new Server(0); + + DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider(); + DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider(); + DummyOrganizationResourceProvider orgProv = new DummyOrganizationResourceProvider(); + DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider(); + DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider(); + DummyDiagnosticReportResourceProvider drProv = new DummyDiagnosticReportResourceProvider(); + DummyDeviceResourceProvider devProv = new DummyDeviceResourceProvider(); + PlainProvider plainProvider = new PlainProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + ourServlet = new RestfulServer(ourCtx); + ourServlet.setFhirContext(ourCtx); + ourServlet.registerProviders(patProvider, obsProv, encProv, cpProv, orgProv, drProv, devProv); + ourServlet.registerProvider(new DummyServiceRequestResourceProvider()); + ourServlet.registerProvider(new DummyConsentResourceProvider()); + ourServlet.setPlainProviders(plainProvider); + ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); + ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ServletHolder servletHolder = new ServletHolder(ourServlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + JettyUtil.startServer(ourServer); + ourPort = JettyUtil.getPortForStartedServer(ourServer); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + public static class DummyCarePlanResourceProvider implements IResourceProvider { @Override @@ -4263,7 +4305,6 @@ public class AuthorizationInterceptorR4Test { } - public static class DummyDiagnosticReportResourceProvider implements IResourceProvider { @@ -4297,6 +4338,7 @@ public class AuthorizationInterceptorR4Test { } return (Device) ourReturn.get(0); } + @Search() public List search( @OptionalParam(name = "patient") ReferenceParam thePatient @@ -4377,15 +4419,29 @@ public class AuthorizationInterceptorR4Test { if (isNotBlank(theConditionalUrl)) { IdType actual = new IdType("Observation", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); theResource.setId(actual); } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); theResource.setId(theId.withVersion("2")); } + { + HookParams params = new HookParams(); + params.add(IBaseResource.class, theResource); + params.add(RequestDetails.class, theRequestDetails); + params.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + params.add(TransactionDetails.class, new TransactionDetails()); + params.add(RequestPartitionId.class, RequestPartitionId.defaultPartition()); + ourServlet.getInterceptorService().callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, params); + } + + { + HookParams params = new HookParams(); + params.add(RequestDetails.class, theRequestDetails); + params.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + params.add(IPreResourceShowDetails.class, new SimplePreResourceShowDetails(theResource)); + ourServlet.getInterceptorService().callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params); + } + MethodOutcome retVal = new MethodOutcome(); retVal.setResource(theResource); return retVal; @@ -4431,20 +4487,18 @@ public class AuthorizationInterceptorR4Test { @Create() public MethodOutcome create(@ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - if (isNotBlank(theConditionalUrl)) { - IdType actual = new IdType("Patient", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); - } - ourHitMethod = true; theResource.setId("Patient/1/_history/1"); MethodOutcome retVal = new MethodOutcome(); retVal.setCreated(true); retVal.setResource(theResource); + + HookParams params = new HookParams(); + params.add(RequestDetails.class, theRequestDetails); + params.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + params.add(IPreResourceShowDetails.class, new SimplePreResourceShowDetails(theResource)); + ourServlet.getInterceptorService().callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params); + return retVal; } @@ -4521,8 +4575,7 @@ public class AuthorizationInterceptorR4Test { public MethodOutcome patch(@IdParam IdType theId, @ResourceParam String theResource, PatchTypeEnum thePatchType) { ourHitMethod = true; - MethodOutcome retVal = new MethodOutcome(); - return retVal; + return new MethodOutcome(); } @Read(version = true) @@ -4546,15 +4599,28 @@ public class AuthorizationInterceptorR4Test { if (isNotBlank(theConditionalUrl)) { IdType actual = new IdType("Patient", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); theResource.setId(actual); } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); theResource.setId(theId.withVersion("2")); } + { + HookParams params = new HookParams(); + params.add(IBaseResource.class, theResource); + params.add(RequestDetails.class, theRequestDetails); + params.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + params.add(TransactionDetails.class, new TransactionDetails()); + params.add(RequestPartitionId.class, RequestPartitionId.defaultPartition()); + ourServlet.getInterceptorService().callHooks(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED, params); + } + { + HookParams params = new HookParams(); + params.add(RequestDetails.class, theRequestDetails); + params.addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + params.add(IPreResourceShowDetails.class, new SimplePreResourceShowDetails(theResource)); + ourServlet.getInterceptorService().callHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, params); + } + MethodOutcome retVal = new MethodOutcome(); retVal.setResource(theResource); return retVal; @@ -4624,46 +4690,5 @@ public class AuthorizationInterceptorR4Test { } - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider(); - DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider(); - DummyOrganizationResourceProvider orgProv = new DummyOrganizationResourceProvider(); - DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider(); - DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider(); - DummyDiagnosticReportResourceProvider drProv = new DummyDiagnosticReportResourceProvider(); - DummyDeviceResourceProvider devProv = new DummyDeviceResourceProvider(); - PlainProvider plainProvider = new PlainProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.registerProviders(patProvider, obsProv, encProv, cpProv, orgProv, drProv, devProv); - ourServlet.registerProvider(new DummyServiceRequestResourceProvider()); - ourServlet.registerProvider(new DummyConsentResourceProvider()); - ourServlet.setPlainProviders(plainProvider); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - }