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
This commit is contained in:
James Agnew 2022-09-25 17:29:08 -04:00 committed by GitHub
parent 3bab3544ec
commit 917cf8d062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 494 additions and 701 deletions

View File

@ -344,9 +344,6 @@ public enum Pointcut implements IPointcut {
* <li>
* ca.uhn.fhir.rest.api.RestOperationTypeEnum - The type of operation that the FHIR server has determined that the client is trying to invoke
* </li>
* <li>
* 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.
* </li>
* </ul>
* </p>
* <p>
@ -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"
),

View File

@ -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:
* <p>
* - 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;
}
}
}

View File

@ -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."

View File

@ -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."

View File

@ -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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> extends BaseStora
private Set<ResourceTag> getAllTagDefinitions(ResourceTable theEntity) {
HashSet<ResourceTag> 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<T extends IBaseResource> 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<T extends IBaseResource> 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);

View File

@ -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<T extends IBaseResource> extends BaseHapiFhirDao<T> implements IFhirResourceDao<T> {
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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> extends B
public <MT extends IBaseMetaType> 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<T extends IBaseResource> extends B
public <MT extends IBaseMetaType> 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<T extends IBaseResource> extends B
@Override
@Transactional
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequest) {
// Notify interceptors
if (theRequest != null) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequest, getResourceName(), theId);
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
}
Set<TagDefinition> tagDefs = new HashSet<>();
BaseHasResource entity = readEntity(theId, theRequest);
for (BaseTag next : entity.getTags()) {
@ -1124,12 +1075,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
@Transactional
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> 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<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
q.setParameter("res_type", myResourceName);
@ -1273,13 +1218,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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<T extends IBaseResource> 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");

View File

@ -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<T extends IBaseBundle, MT> extends BaseHapiFhirDao<IBaseResource> implements IFhirSystemDao<T, MT> {
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<T extends IBaseBundle, MT> 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<T extends IBaseBundle, MT> extends B
});
}

View File

@ -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<Patient
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, IIdType theId) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails);
TokenOrListParam id = new TokenOrListParam().add(new TokenParam(theId.getIdPart()));
return doEverythingOperation(id, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getFilter(), theQueryParams.getTypes(), theRequestDetails);
}
@ -101,10 +99,6 @@ public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao<Patient
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, RequestDetails theRequestDetails, PatientEverythingParameters theQueryParams, TokenOrListParam theIds) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails);
return doEverythingOperation(theIds, theQueryParams.getCount(), theQueryParams.getOffset(), theQueryParams.getLastUpdated(), theQueryParams.getSort(), theQueryParams.getContent(), theQueryParams.getNarrative(), theQueryParams.getFilter(), theQueryParams.getTypes(), theRequestDetails);
}

View File

@ -23,9 +23,7 @@ package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
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 javax.persistence.TypedQuery;
@ -36,10 +34,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
@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<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList();

View File

@ -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<Bundle, Meta> {
@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<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList();

View File

@ -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<Bundle, Meta> {
@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<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList();

View File

@ -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<Bundle, Meta> {
@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<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList();

View File

@ -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());

View File

@ -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");

View File

@ -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<AnalyticsEvent> myEventBuffer = new LinkedList<>();
private String myAnalyticsTid;
private int myCollectThreshold = 100000;
private final LinkedList<AnalyticsEvent> 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;
}

View File

@ -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.
* <p>
@ -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 <code>null</code>
* .
* <p>
* Note that this method is currently only populated if the handling method has a parameter annotated with the
* {@link ResourceParam} annotation.
* </p>
*/
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 <code>null</code> 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<Object, Object> 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);
}
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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
}

View File

@ -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<? extends IBaseResource> myResourceType;
private Class<? extends IIdType> 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);
}
}
/**

View File

@ -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);
}
}

View File

@ -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() {

View File

@ -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('?'));

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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<String> splitTypeFilters(List<IPrimitiveType<String>> 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);
}
}
}

View File

@ -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<IBaseReference> extractReferencesToAutoVersion(FhirContext theFhirContext, ModelConfig theModelConfig, IBaseResource theResource) {
Map<IBaseReference, Object> references = Collections.emptyMap();
if (!theModelConfig.getAutoVersionReferenceAtPaths().isEmpty()) {
String resourceName = theFhirContext.getResourceType(theResource);
for (String nextPath : theModelConfig.getAutoVersionReferenceAtPathsByResourceType(resourceName)) {
List<IBaseReference> 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<ResourceReferenceInfo> 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<ResourceReferenceInfo> 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<IBaseReference> extractReferencesToAutoVersion(FhirContext theFhirContext, ModelConfig theModelConfig, IBaseResource theResource) {
Map<IBaseReference, Object> references = Collections.emptyMap();
if (!theModelConfig.getAutoVersionReferenceAtPaths().isEmpty()) {
String resourceName = theFhirContext.getResourceType(theResource);
for (String nextPath : theModelConfig.getAutoVersionReferenceAtPathsByResourceType(resourceName)) {
List<IBaseReference> 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);
}
}
}

View File

@ -203,11 +203,6 @@ public abstract class BaseTransactionProcessor {
}
public <BUNDLE extends IBaseBundle> 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);

View File

@ -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<String, Patient> getIdToPatient() {
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
Map<String, Patient> idToPatient = new HashMap<>();
{
Patient patient = createPatient1();
idToPatient.put("1", patient);

View File

@ -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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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());
}
}
}

View File

@ -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<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> 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<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> arTypeCapt = ArgumentCaptor.forClass(RequestDetails.class);
ArgumentCaptor<ServletRequestDetails> rdCapt = ArgumentCaptor.forClass(ServletRequestDetails.class);
ArgumentCaptor<OperationOutcome> 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<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
ArgumentCaptor<RequestDetails> arTypeCapt = ArgumentCaptor.forClass(RequestDetails.class);
ArgumentCaptor<OperationOutcome> 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();
}
}

View File

@ -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<Resource> 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<IAuthRule> 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<Resource> 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();
}
}