Add new interceptor hook for auditing
This commit is contained in:
parent
89a7750bf4
commit
330dbde983
|
@ -93,7 +93,6 @@
|
|||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet_api_version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -149,7 +149,6 @@
|
|||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet_api_version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -117,6 +117,21 @@ public enum RestOperationTypeEnum {
|
|||
*/
|
||||
METADATA("metadata"),
|
||||
|
||||
/**
|
||||
* $meta-add extended operation
|
||||
*/
|
||||
META_ADD("$meta-add"),
|
||||
|
||||
/**
|
||||
* $meta-add extended operation
|
||||
*/
|
||||
META("$meta"),
|
||||
|
||||
/**
|
||||
* $meta-delete extended operation
|
||||
*/
|
||||
META_DELETE("$meta-delete"),
|
||||
|
||||
;
|
||||
|
||||
private static Map<String, RestOperationTypeEnum> CODE_TO_ENUM = new HashMap<String, RestOperationTypeEnum>();
|
||||
|
|
|
@ -38,7 +38,7 @@ class AddTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.ADD_TAGS;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
invokeServerMethod(params);
|
||||
invokeServerMethod(theServer, theRequest, params);
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
|
|
|
@ -19,7 +19,7 @@ package ca.uhn.fhir.rest.method;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -79,6 +79,8 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
|
||||
|
@ -102,7 +104,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
myMethod = theMethod;
|
||||
myContext = theContext;
|
||||
myProvider = theProvider;
|
||||
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getResourceOperationType());
|
||||
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getRestOperationType());
|
||||
|
||||
for (IParameter next : myParameters) {
|
||||
if (next instanceof ConditionalParamBinder) {
|
||||
|
@ -232,7 +234,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
*/
|
||||
public abstract String getResourceName();
|
||||
|
||||
public abstract RestOperationTypeEnum getResourceOperationType();
|
||||
public abstract RestOperationTypeEnum getRestOperationType();
|
||||
|
||||
/**
|
||||
* Determine which operation is being fired for a specific request
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* The request
|
||||
*/
|
||||
public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
|
||||
return getRestOperationType();
|
||||
}
|
||||
|
||||
public abstract boolean incomingServerRequestMatchesMethod(RequestDetails theRequest);
|
||||
|
||||
|
@ -240,7 +252,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
|
||||
public abstract void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
|
||||
|
||||
protected Object invokeServerMethod(Object[] theMethodParams) {
|
||||
protected final Object invokeServerMethod(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) {
|
||||
// Handle server action interceptors
|
||||
RestOperationTypeEnum operationType = getRestOperationType(theRequest);
|
||||
if (operationType != null) {
|
||||
for (IServerInterceptor next : theServer.getInterceptors()) {
|
||||
ActionRequestDetails details = new ActionRequestDetails(theRequest);
|
||||
next.incomingRequestPreHandled(operationType, details);
|
||||
}
|
||||
}
|
||||
|
||||
// Actuall invoke the method
|
||||
try {
|
||||
Method method = getMethod();
|
||||
return method.invoke(getProvider(), theMethodParams);
|
||||
|
@ -448,12 +470,12 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
if (returnTypeFromRp != null) {
|
||||
if (returnTypeFromAnnotation != null && !isResourceInterface(returnTypeFromAnnotation)) {
|
||||
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName()
|
||||
+ " (or a subclass of it) per IResourceProvider contract");
|
||||
throw new ConfigurationException(
|
||||
"Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return " + returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
|
||||
}
|
||||
if (!returnTypeFromRp.isAssignableFrom(returnTypeFromAnnotation)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName() + " per method annotation - Must return "
|
||||
+ returnTypeFromRp.getCanonicalName() + " (or a subclass of it) per IResourceProvider contract");
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " claims to return type " + returnTypeFromAnnotation.getCanonicalName() + " per method annotation - Must return " + returnTypeFromRp.getCanonicalName()
|
||||
+ " (or a subclass of it) per IResourceProvider contract");
|
||||
}
|
||||
returnType = returnTypeFromAnnotation;
|
||||
} else {
|
||||
|
@ -462,8 +484,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
} else {
|
||||
if (!isResourceInterface(returnTypeFromAnnotation)) {
|
||||
if (!verifyIsValidResourceReturnType(returnTypeFromAnnotation)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns " + toLogString(returnTypeFromAnnotation)
|
||||
+ " according to annotation - Must return a resource type");
|
||||
throw new ConfigurationException(
|
||||
"Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns " + toLogString(returnTypeFromAnnotation) + " according to annotation - Must return a resource type");
|
||||
}
|
||||
returnType = returnTypeFromAnnotation;
|
||||
} else {
|
||||
|
|
|
@ -152,7 +152,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
HttpServletResponse servletResponse = theRequest.getServletResponse();
|
||||
MethodOutcome response;
|
||||
try {
|
||||
response = (MethodOutcome) invokeServerMethod(params);
|
||||
response = (MethodOutcome) invokeServerMethod(theServer, theRequest, params);
|
||||
} catch (InternalErrorException e) {
|
||||
ourLog.error("Internal error during method invocation", e);
|
||||
EncodingEnum encodingNotNull = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
|
@ -182,7 +182,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
}
|
||||
|
||||
boolean allowPrefer = false;
|
||||
switch (getResourceOperationType()) {
|
||||
switch (getRestOperationType()) {
|
||||
case CREATE:
|
||||
if (response == null) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName()
|
||||
|
|
|
@ -233,7 +233,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
throw new IllegalStateException("Should not get here!");
|
||||
}
|
||||
|
||||
public abstract Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||
public abstract Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
|
@ -265,7 +265,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
}
|
||||
|
||||
Object resultObj = invokeServer(theRequest, params);
|
||||
Object resultObj = invokeServer(theServer, theRequest, params);
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
|
|
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
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.InternalErrorException;
|
||||
|
@ -71,8 +72,8 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
IBaseResource conf = (IBaseResource) invokeServerMethod(theMethodParams);
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
IBaseResource conf = (IBaseResource) invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
return new SimpleBundleProvider(conf);
|
||||
}
|
||||
|
||||
|
@ -90,7 +91,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.METADATA;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.CREATE;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.DELETE;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public class DeleteTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.DELETE_TAGS;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -85,17 +86,17 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
return toResourceList(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.SEARCH_TYPE;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.GET_TAGS;
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
params[myVersionIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
TagList resp = (TagList) invokeServerMethod(params);
|
||||
TagList resp = (TagList) invokeServerMethod(theServer, theRequest, params);
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
|
|
|
@ -43,6 +43,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -87,7 +88,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return myResourceOperationType;
|
||||
}
|
||||
|
||||
|
@ -154,12 +155,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
|
||||
final IBundleProvider resources = toResourceList(response);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
|
@ -166,7 +167,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return myOtherOperatiopnType;
|
||||
}
|
||||
|
||||
|
@ -222,7 +223,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.POST) {
|
||||
// always ok
|
||||
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
|
@ -244,7 +245,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
IBundleProvider retVal = toResourceList(response);
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
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.InternalErrorException;
|
||||
|
@ -91,6 +92,15 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
|
||||
if (mySupportsVersion && theRequestDetails.getId().hasVersionIdPart()) {
|
||||
return RestOperationTypeEnum.VREAD;
|
||||
} else {
|
||||
return RestOperationTypeEnum.READ;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getAllowableParamAnnotations() {
|
||||
ArrayList<Class<?>> retVal = new ArrayList<Class<?>>();
|
||||
|
@ -99,7 +109,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return isVread() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
|
||||
}
|
||||
|
||||
|
@ -192,13 +202,13 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
theMethodParams[myIdIndex] = MethodUtil.convertIdToType(theRequest.getId(), myIdParameterType);
|
||||
if (myVersionIdIndex != null) {
|
||||
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getId().getVersionIdPart());
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
IBundleProvider retVal = toResourceList(response);
|
||||
|
||||
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
|
|
|
@ -46,8 +46,8 @@ public class RequestDetails {
|
|||
private String myRequestPath;
|
||||
private RequestTypeEnum myRequestType;
|
||||
private String myResourceName;
|
||||
private RestOperationTypeEnum myResourceOperationType;
|
||||
private boolean myRespondGzip;
|
||||
private RestOperationTypeEnum myRestOperationType;
|
||||
private String mySecondaryOperation;
|
||||
private RestfulServer myServer;
|
||||
private HttpServletRequest myServletRequest;
|
||||
|
@ -96,8 +96,8 @@ public class RequestDetails {
|
|||
return myResourceName;
|
||||
}
|
||||
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
return myResourceOperationType;
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return myRestOperationType;
|
||||
}
|
||||
|
||||
public String getSecondaryOperation() {
|
||||
|
@ -185,14 +185,14 @@ public class RequestDetails {
|
|||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
public void setResourceOperationType(RestOperationTypeEnum theResourceOperationType) {
|
||||
myResourceOperationType = theResourceOperationType;
|
||||
}
|
||||
|
||||
public void setRespondGzip(boolean theRespondGzip) {
|
||||
myRespondGzip = theRespondGzip;
|
||||
}
|
||||
|
||||
public void setRestOperationType(RestOperationTypeEnum theRestOperationType) {
|
||||
myRestOperationType = theRestOperationType;
|
||||
}
|
||||
|
||||
public void setSecondaryOperation(String theSecondaryOperation) {
|
||||
mySecondaryOperation = theSecondaryOperation;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.param.BaseQueryParameter;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -127,7 +128,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.SEARCH_TYPE;
|
||||
}
|
||||
|
||||
|
@ -277,12 +278,12 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
|
||||
return toResourceList(response);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.param.TransactionParameter;
|
||||
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -75,7 +76,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.TRANSACTION;
|
||||
}
|
||||
|
||||
|
@ -118,7 +119,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object invokeServer(RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
|
||||
/*
|
||||
* The design of HAPI's transaction method for DSTU1 support assumed that a transaction was just an update on a
|
||||
|
@ -127,7 +128,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||
*/
|
||||
if (myTransactionParamStyle == ParamStyle.RESOURCE_BUNDLE) {
|
||||
// This is the DSTU2 style
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -145,7 +146,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
// Call the server implementation method
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
Object response = invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
IBundleProvider retVal = toResourceList(response);
|
||||
|
||||
/*
|
||||
|
|
|
@ -47,7 +47,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.UPDATE;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public class ValidateMethodBindingDstu1 extends BaseOutcomeReturningMethodBindin
|
|||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getResourceOperationType() {
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.VALIDATE;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ public class ResourceParameter implements IParameter {
|
|||
String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
if (ctValue != null) {
|
||||
if (ctValue.startsWith("application/x-www-form-urlencoded")) {
|
||||
String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getResourceOperationType());
|
||||
String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
@ -183,13 +183,13 @@ public class ResourceParameter implements IParameter {
|
|||
}
|
||||
encoding = MethodUtil.detectEncodingNoDefault(body);
|
||||
if (encoding == null) {
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", theMethodBinding.getResourceOperationType());
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", theMethodBinding.getRestOperationType());
|
||||
throw new InvalidRequestException(msg);
|
||||
} else {
|
||||
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset);
|
||||
}
|
||||
} else {
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getResourceOperationType());
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.server;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
|
@ -640,7 +639,7 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
||||
if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
||||
requestDetails.setResourceOperationType(RestOperationTypeEnum.GET_PAGE);
|
||||
requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
|
||||
if (theRequestType != RequestTypeEnum.GET) {
|
||||
/*
|
||||
* We reconstruct the link-self URL using the request parameters, and this would break if the parameters came in using a POST. We could probably work around that but why bother unless
|
||||
|
@ -665,8 +664,9 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
requestDetails.setResourceOperationType(resourceMethod.getResourceOperationType());
|
||||
requestDetails.setRestOperationType(resourceMethod.getRestOperationType());
|
||||
|
||||
// Handle server interceptors
|
||||
for (IServerInterceptor next : myInterceptors) {
|
||||
boolean continueProcessing = next.incomingRequestPostProcessed(requestDetails, theRequest, theResponse);
|
||||
if (!continueProcessing) {
|
||||
|
@ -675,6 +675,14 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Actualy invoke the server method. This call is to a HAPI method
|
||||
* binding, which is an object that wraps a specific implementing (user-supplied)
|
||||
* method, but handles its input and provides its output back to the client.
|
||||
*
|
||||
* This is basically the end of processing for a successful request,
|
||||
* since the method binding replies to the client and closes the response.
|
||||
*/
|
||||
resourceMethod.invokeServer(this, requestDetails);
|
||||
|
||||
} catch (NotModifiedException e) {
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
/**
|
||||
* Action interceptors are invoked by the server upon specific fhir operations, such as "read" (HTTP GET) or "create" (HTTP POST). They can be thought of as being a layer "above"
|
||||
* {@link IServerInterceptor} interceptors.
|
||||
* <p>
|
||||
* These interceptors are useful as a means of adding authentication checks or audit operations on top of a server, since the HAPI RestfulServer translates the incoming requests into higher level
|
||||
* operations.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that unlike {@link IServerInterceptor}s, {@link IServerActionInterceptor}s do not have the ability to handle a request themselves and stop processing.
|
||||
* </p>
|
||||
*/
|
||||
public interface IServerActionInterceptor extends IServerInterceptor {
|
||||
|
||||
/**
|
||||
* Invoked before an incoming request is processed
|
||||
*
|
||||
* @param theServletRequest
|
||||
* The incoming servlet request as provided by the servlet container
|
||||
* @param theOperation
|
||||
* The type of operation that the FHIR server has determined that the client is trying to invoke
|
||||
* @param theRequestDetails
|
||||
* An object which will be populated with any relevant details about the incoming request
|
||||
*/
|
||||
void preAction(HttpServletRequest theServletRequest, ActionOperationEnum theOperation, ActionRequestDetails theRequestDetails);
|
||||
|
||||
/**
|
||||
* Represents the type of operation being invoked for a {@link IServerActionInterceptor#preAction(HttpServletRequest, ActionOperationEnum, ActionRequestDetails) preAction} call
|
||||
*/
|
||||
public static enum ActionOperationEnum {
|
||||
READ, VREAD
|
||||
}
|
||||
|
||||
public static class ActionRequestDetails {
|
||||
private final IIdType myId;
|
||||
private final IBaseResource myRequestResource;
|
||||
|
||||
public ActionRequestDetails(IIdType theId, IBaseResource theRequestResource) {
|
||||
super();
|
||||
myId = theId;
|
||||
myRequestResource = theRequestResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the incoming request (typically this is from the request URL)
|
||||
*/
|
||||
public IIdType getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the incoming resource from the request (this will be populated only for operations which receive a resource, such as "create" and "update")
|
||||
*/
|
||||
public IBaseResource getRequestResource() {
|
||||
return myRequestResource;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,12 +27,14 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -44,8 +46,6 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
|||
* <b>See:</b> See the <a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_server_interceptor.html">server
|
||||
* interceptor documentation</a> for more information on how to use this class.
|
||||
* </p>
|
||||
*
|
||||
* @see
|
||||
*/
|
||||
public interface IServerInterceptor {
|
||||
|
||||
|
@ -83,7 +83,7 @@ public interface IServerInterceptor {
|
|||
* @throws IOException
|
||||
* If this exception is thrown, it will be re-thrown up to the container for handling.
|
||||
*/
|
||||
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException;
|
||||
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException;
|
||||
|
||||
/**
|
||||
* This method is called just before the actual implementing server method is invoked.
|
||||
|
@ -109,7 +109,7 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
|
||||
boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called before any other processing takes place for each incoming request. It may be used to provide
|
||||
|
@ -129,7 +129,7 @@ public interface IServerInterceptor {
|
|||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
*/
|
||||
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
|
||||
boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
|
@ -153,7 +153,7 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
|
@ -177,7 +177,7 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
|
@ -203,7 +203,7 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
|
@ -229,20 +229,33 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* Invoked before an incoming request is processed
|
||||
*
|
||||
* @param theServletRequest
|
||||
* The incoming servlet request as provided by the servlet container
|
||||
* @param theOperation
|
||||
* The type of operation that the FHIR server has determined that the client is trying to invoke
|
||||
* @param theRequestDetails
|
||||
* An object which will be populated with any relevant details about the incoming request (this includes
|
||||
* the HttpServletRequest)
|
||||
*/
|
||||
void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequestDetails);
|
||||
|
||||
/**
|
||||
* This method is called upon any exception being thrown within the server's request processing code. This includes
|
||||
* any exceptions thrown within resource provider methods (e.g. {@link Search} and {@link Read} methods) as well as
|
||||
* any runtime exceptions thrown by the server itself. This method is invoked for each interceptor (until one of them
|
||||
* returns a non-<code>null</code> response or the end of the list is reached), after which
|
||||
* {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is called for each
|
||||
* interceptor.
|
||||
* {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is
|
||||
* called for each interceptor.
|
||||
* <p>
|
||||
* This may be used to add an OperationOutcome to a response, or to convert between exception types for any reason.
|
||||
* </p>
|
||||
* <p>
|
||||
* Implementations of this method may choose to ignore/log/count/etc exceptions, and return <code>true</code>. In
|
||||
* Implementations of this method may choose to ignore/log/count/etc exceptions, and return <code>null</code>. In
|
||||
* this case, processing will continue, and the server will automatically generate an {@link BaseOperationOutcome
|
||||
* OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they
|
||||
* should return a non-<code>null</code>, to indicate that they have handled the request and processing should stop.
|
||||
|
@ -255,4 +268,34 @@ public interface IServerInterceptor {
|
|||
*/
|
||||
BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException;
|
||||
|
||||
public static class ActionRequestDetails {
|
||||
private final IIdType myId;
|
||||
private final String myResourceType;
|
||||
|
||||
public ActionRequestDetails(IIdType theId, String theResourceType) {
|
||||
myId = theId;
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
public ActionRequestDetails(RequestDetails theRequestDetails) {
|
||||
myId = theRequestDetails.getId();
|
||||
myResourceType = theRequestDetails.getResourceName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the incoming request (typically this is from the request URL)
|
||||
*/
|
||||
public IIdType getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -38,8 +39,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
|||
* Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation
|
||||
* of all methods, always returning <code>true</code>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class InterceptorAdapter implements IServerInterceptor, IServerActionInterceptor {
|
||||
public class InterceptorAdapter implements IServerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
|
||||
|
@ -83,7 +83,7 @@ public class InterceptorAdapter implements IServerInterceptor, IServerActionInte
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preAction(HttpServletRequest theServletRequest, ActionOperationEnum theOperation, ActionRequestDetails theRequestDetails) {
|
||||
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequestDetails) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
|
|
|
@ -141,13 +141,13 @@ public class LoggingInterceptor extends InterceptorAdapter {
|
|||
*/
|
||||
|
||||
if ("operationType".equals(theKey)) {
|
||||
if (myRequestDetails.getResourceOperationType() != null) {
|
||||
return myRequestDetails.getResourceOperationType().getCode();
|
||||
if (myRequestDetails.getRestOperationType() != null) {
|
||||
return myRequestDetails.getRestOperationType().getCode();
|
||||
}
|
||||
return "";
|
||||
} else if ("operationName".equals(theKey)) {
|
||||
if (myRequestDetails.getResourceOperationType() != null) {
|
||||
switch (myRequestDetails.getResourceOperationType()) {
|
||||
if (myRequestDetails.getRestOperationType() != null) {
|
||||
switch (myRequestDetails.getRestOperationType()) {
|
||||
case EXTENDED_OPERATION_INSTANCE:
|
||||
case EXTENDED_OPERATION_SERVER:
|
||||
case EXTENDED_OPERATION_TYPE:
|
||||
|
|
|
@ -127,20 +127,23 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-dbcp</groupId>
|
||||
<artifactId>commons-dbcp</artifactId>
|
||||
<version>1.4</version>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -60,6 +60,11 @@ import org.springframework.transaction.TransactionStatus;
|
|||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
|
@ -95,6 +100,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
|
@ -105,13 +111,10 @@ 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;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public abstract class BaseHapiFhirDao implements IDao {
|
||||
|
||||
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
|
||||
|
@ -305,6 +308,21 @@ public abstract class BaseHapiFhirDao implements IDao {
|
|||
}
|
||||
}
|
||||
|
||||
protected void notifyInterceptors(RestOperationTypeEnum operationType, ActionRequestDetails requestDetails) {
|
||||
if (requestDetails.getId() != null && requestDetails.getId().hasResourceType() && isNotBlank(requestDetails.getResourceType())) {
|
||||
if (requestDetails.getId().getResourceType().equals(requestDetails.getResourceType()) == false) {
|
||||
throw new InternalErrorException("Inconsistent server state - Resource types don't match: " + requestDetails.getId().getResourceType() + " / " + requestDetails.getResourceType());
|
||||
}
|
||||
}
|
||||
List<IServerInterceptor> interceptors = getConfig().getInterceptors();
|
||||
if (interceptors == null) {
|
||||
return;
|
||||
}
|
||||
for (IServerInterceptor next : interceptors) {
|
||||
next.incomingRequestPreHandled(operationType, requestDetails);
|
||||
}
|
||||
}
|
||||
|
||||
protected DaoConfig getConfig() {
|
||||
return myConfig;
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
|
@ -125,6 +126,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
|||
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.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
|
||||
|
@ -1192,6 +1194,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
throw new InvalidRequestException("Trying to update " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
|
||||
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
||||
|
||||
ResourceTable savedEntity = updateEntity(null, entity, true, new Date());
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
@ -1212,9 +1218,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
}
|
||||
|
||||
Long pid = resource.iterator().next();
|
||||
|
||||
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
|
||||
|
||||
// Notify interceptors
|
||||
IdDt idToDelete = entity.getIdDt();
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType());
|
||||
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
|
||||
|
||||
// Perform delete
|
||||
ResourceTable savedEntity = updateEntity(null, entity, true, new Date());
|
||||
notifyWriteCompleted();
|
||||
|
||||
|
@ -1259,6 +1270,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
}
|
||||
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource));
|
||||
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
|
||||
|
||||
updateEntity(theResource, entity, false, null, thePerformIndexing, true);
|
||||
|
||||
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
|
||||
|
@ -1274,6 +1289,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public TagList getAllResourceTags() {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null,null);
|
||||
notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
TagList tags = super.getTags(myResourceType, null);
|
||||
ourLog.info("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||
|
@ -1286,8 +1305,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
return myResourceType;
|
||||
}
|
||||
|
||||
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagList getTags(IIdType theResourceId) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, null);
|
||||
notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
TagList retVal = super.getTags(myResourceType, theResourceId);
|
||||
ourLog.info("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart());
|
||||
|
@ -1296,6 +1324,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Date theSince) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
|
||||
notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
IBundleProvider retVal = super.history(myResourceName, null, theSince);
|
||||
ourLog.info("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||
|
@ -1304,6 +1336,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(final IIdType theId, final Date theSince) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails);
|
||||
|
||||
final InstantDt end = createHistoryToTimestamp();
|
||||
final String resourceType = getContext().getResourceDefinition(myResourceType).getName();
|
||||
|
||||
|
@ -1400,6 +1436,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Long theId, Date theSince) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.HISTORY_TYPE, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
IBundleProvider retVal = super.history(myResourceName, theId, theSince);
|
||||
ourLog.info("Processed history on {} in {}ms", theId, w.getMillisAndRestart());
|
||||
|
@ -1492,6 +1532,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public MetaDt metaAddOperation(IIdType theResourceId, MetaDt theMetaAdd) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.META_ADD, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theResourceId);
|
||||
if (entity == null) {
|
||||
|
@ -1532,6 +1576,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public MetaDt metaDeleteOperation(IIdType theResourceId, MetaDt theMetaDel) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theResourceId, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theResourceId);
|
||||
if (entity == null) {
|
||||
|
@ -1566,6 +1614,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public MetaDt metaGetOperation() {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
|
||||
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);
|
||||
|
@ -1578,6 +1630,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public MetaDt metaGetOperation(IIdType theId) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
|
||||
|
||||
Long pid = super.translateForcedIdToPid(theId);
|
||||
|
||||
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type AND t.myResourceId = :res_id)";
|
||||
|
@ -1622,6 +1678,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
public T read(IIdType theId) {
|
||||
validateResourceTypeAndThrowIllegalArgumentException(theId);
|
||||
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
|
||||
RestOperationTypeEnum operationType = theId.hasVersionIdPart() ? RestOperationTypeEnum.VREAD : RestOperationTypeEnum.READ;
|
||||
notifyInterceptors(operationType, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theId);
|
||||
validateResourceType(entity);
|
||||
|
@ -1691,6 +1752,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
BaseHasResource entity = readEntity(theId);
|
||||
if (entity == null) {
|
||||
|
@ -1728,6 +1793,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
@Override
|
||||
public IBundleProvider search(final SearchParameterMap theParams) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
final InstantDt now = InstantDt.withCurrentTime();
|
||||
|
||||
|
@ -2205,6 +2274,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
throw new InvalidRequestException("Trying to update " + resourceId + " but this is not the current version");
|
||||
}
|
||||
|
||||
if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
|
||||
throw new UnprocessableEntityException("Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]");
|
||||
}
|
||||
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(resourceId, getResourceName());
|
||||
notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails);
|
||||
|
||||
// Perform update
|
||||
ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true);
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
|
|
@ -41,8 +41,10 @@ import ca.uhn.fhir.jpa.util.StopWatch;
|
|||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
||||
public abstract class BaseHapiFhirSystemDao<T> extends BaseHapiFhirDao implements IFhirSystemDao<T> {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
|
||||
|
@ -50,6 +52,10 @@ public abstract class BaseHapiFhirSystemDao<T> extends BaseHapiFhirDao implement
|
|||
@Transactional(propagation=Propagation.REQUIRED)
|
||||
@Override
|
||||
public void deleteAllTagsOnServer() {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
|
||||
notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
|
||||
|
||||
myEntityManager.createQuery("DELETE from ResourceTag t").executeUpdate();
|
||||
}
|
||||
|
||||
|
@ -77,6 +83,10 @@ public abstract class BaseHapiFhirSystemDao<T> extends BaseHapiFhirDao implement
|
|||
|
||||
@Override
|
||||
public IBundleProvider history(Date theSince) {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
|
||||
notifyInterceptors(RestOperationTypeEnum.HISTORY_SYSTEM, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
IBundleProvider retVal = super.history(null, null, theSince);
|
||||
ourLog.info("Processed global history in {}ms", w.getMillisAndRestart());
|
||||
|
@ -85,6 +95,10 @@ public abstract class BaseHapiFhirSystemDao<T> extends BaseHapiFhirDao implement
|
|||
|
||||
@Override
|
||||
public TagList getAllTags() {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
|
||||
notifyInterceptors(RestOperationTypeEnum.GET_TAGS, requestDetails);
|
||||
|
||||
StopWatch w = new StopWatch();
|
||||
TagList retVal = super.getTags(null, null);
|
||||
ourLog.info("Processed getAllTags in {}ms", w.getMillisAndRestart());
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -21,23 +25,16 @@ package ca.uhn.fhir.jpa.dao;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
||||
public class DaoConfig {
|
||||
|
||||
private int myHardSearchLimit = 1000;
|
||||
private int myHardTagListLimit = 1000;
|
||||
private ResourceEncodingEnum myResourceEncoding=ResourceEncodingEnum.JSONC;
|
||||
private int myIncludeLimit = 2000;
|
||||
|
||||
/**
|
||||
* This is the maximum number of resources that will be added to a single page of
|
||||
* returned resources. Because of includes with wildcards and other possibilities it is possible for a client to make
|
||||
* requests that include very large amounts of data, so this hard limit can be imposed to prevent runaway
|
||||
* requests.
|
||||
*/
|
||||
public void setIncludeLimit(int theIncludeLimit) {
|
||||
myIncludeLimit = theIncludeLimit;
|
||||
}
|
||||
private List<IServerInterceptor> myInterceptors;
|
||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||
|
||||
/**
|
||||
* See {@link #setIncludeLimit(int)}
|
||||
|
@ -50,6 +47,23 @@ public class DaoConfig {
|
|||
return myHardTagListLimit;
|
||||
}
|
||||
|
||||
public int getIncludeLimit() {
|
||||
return myIncludeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the interceptors which will be notified of operations.
|
||||
*
|
||||
* @see #setInterceptors(List)
|
||||
*/
|
||||
public List<IServerInterceptor> getInterceptors() {
|
||||
return myInterceptors;
|
||||
}
|
||||
|
||||
public ResourceEncodingEnum getResourceEncoding() {
|
||||
return myResourceEncoding;
|
||||
}
|
||||
|
||||
public void setHardSearchLimit(int theHardSearchLimit) {
|
||||
myHardSearchLimit = theHardSearchLimit;
|
||||
}
|
||||
|
@ -58,16 +72,47 @@ public class DaoConfig {
|
|||
myHardTagListLimit = theHardTagListLimit;
|
||||
}
|
||||
|
||||
public ResourceEncodingEnum getResourceEncoding() {
|
||||
return myResourceEncoding;
|
||||
/**
|
||||
* This is the maximum number of resources that will be added to a single page of returned resources. Because of
|
||||
* includes with wildcards and other possibilities it is possible for a client to make requests that include very
|
||||
* large amounts of data, so this hard limit can be imposed to prevent runaway requests.
|
||||
*/
|
||||
public void setIncludeLimit(int theIncludeLimit) {
|
||||
myIncludeLimit = theIncludeLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* This may be used to optionally register server interceptors directly against the DAOs.
|
||||
* <p>
|
||||
* Registering server action interceptors against the JPA DAOs can be more powerful than registering them against the
|
||||
* {@link RestfulServer}, since the DAOs are able to break transactions into individual actions, and will account for
|
||||
* match URLs (e.g. if a request contains an If-None-Match URL, the ID will be adjusted to account for the matching
|
||||
* ID).
|
||||
* </p>
|
||||
*/
|
||||
public void setInterceptors(List<IServerInterceptor> theInterceptors) {
|
||||
myInterceptors = theInterceptors;
|
||||
}
|
||||
|
||||
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
|
||||
myResourceEncoding = theResourceEncoding;
|
||||
}
|
||||
|
||||
public int getIncludeLimit() {
|
||||
return myIncludeLimit;
|
||||
/**
|
||||
* This may be used to optionally register server interceptors directly against the DAOs.
|
||||
* <p>
|
||||
* Registering server action interceptors against the JPA DAOs can be more powerful than registering them against the
|
||||
* {@link RestfulServer}, since the DAOs are able to break transactions into individual actions, and will account for
|
||||
* match URLs (e.g. if a request contains an If-None-Match URL, the ID will be adjusted to account for the matching
|
||||
* ID).
|
||||
* </p>
|
||||
*/
|
||||
public void setInterceptors(IServerInterceptor... theInterceptor) {
|
||||
if (theInterceptor == null || theInterceptor.length==0){
|
||||
setInterceptors(new ArrayList<IServerInterceptor>());
|
||||
} else {
|
||||
setInterceptors(Arrays.asList(theInterceptor));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -22,7 +24,6 @@ package ca.uhn.fhir.jpa.dao;
|
|||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -41,8 +42,10 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.IParserErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
@ -70,12 +73,15 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
protected IBaseOperationOutcome createOperationOutcome(String theSeverity, String theMessage) {
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.getIssueFirstRep().getSeverityElement().setValue(theSeverity);
|
||||
oo.getIssueFirstRep().getDetailsElement().setValue(theMessage);
|
||||
oo.getIssueFirstRep().getDiagnosticsElement().setValue(theMessage);
|
||||
return oo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome validate(T theResource, IdDt theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, null);
|
||||
notifyInterceptors(RestOperationTypeEnum.VALIDATE, requestDetails);
|
||||
|
||||
final OperationOutcome oo = new OperationOutcome();
|
||||
|
||||
IParser parser = theEncoding.newParser(getContext());
|
||||
|
@ -83,17 +89,17 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
|
||||
@Override
|
||||
public void unknownAttribute(IParseLocation theLocation, String theAttributeName) {
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown attribute found: " + theAttributeName);
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDiagnostics("Unknown attribute found: " + theAttributeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unknownElement(IParseLocation theLocation, String theElementName) {
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Unknown element found: " + theElementName);
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDiagnostics("Unknown element found: " + theElementName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDetails("Multiple repetitions of non-repeatable element found: " + theElementName);
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setCode(IssueTypeEnum.INVALID_CONTENT).setDiagnostics("Multiple repetitions of non-repeatable element found: " + theElementName);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -108,11 +114,12 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
|
|||
|
||||
// This method returns a MethodOutcome object
|
||||
MethodOutcome retVal = new MethodOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Validation succeeded");
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Validation succeeded");
|
||||
retVal.setOperationOutcome(oo);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -19,9 +19,7 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
@ -58,12 +56,14 @@ import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||
|
@ -137,7 +137,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
Entry nextEntry = resp.addEntry();
|
||||
|
||||
OperationOutcome oo = new OperationOutcome();
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDetails(caughtEx.getMessage());
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.ERROR).setDiagnostics(caughtEx.getMessage());
|
||||
nextEntry.setResource(oo);
|
||||
|
||||
EntryResponse nextEntryResp = nextEntry.getResponse();
|
||||
|
@ -148,13 +148,16 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info("Batch completed in {}ms", new Object[] { delay });
|
||||
ooResp.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Batch completed in " + delay + "ms");
|
||||
ooResp.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Batch completed in " + delay + "ms");
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaDt metaGetOperation() {
|
||||
// Notify interceptors
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(null, null);
|
||||
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);
|
||||
|
@ -251,7 +254,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
OperationOutcome statusOperationOutcome = new OperationOutcome();
|
||||
if (transactionType == null) {
|
||||
String message = "Transactiion Bundle did not specify valid Bundle.type, assuming " + BundleTypeEnum.TRANSACTION.getCode();
|
||||
statusOperationOutcome.addIssue().setCode(IssueTypeEnum.INVALID_CONTENT).setSeverity(IssueSeverityEnum.WARNING).setDetails(message);
|
||||
statusOperationOutcome.addIssue().setCode(IssueTypeEnum.INVALID_CONTENT).setSeverity(IssueSeverityEnum.WARNING).setDiagnostics(message);
|
||||
ourLog.warn(message);
|
||||
transactionType = BundleTypeEnum.TRANSACTION;
|
||||
}
|
||||
|
@ -405,7 +408,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
int configuredMax = 100; // this should probably be configurable or something
|
||||
if (bundle.size() > configuredMax) {
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.WARNING)
|
||||
.setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
|
||||
.setDiagnostics("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions");
|
||||
}
|
||||
List<IBaseResource> resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax));
|
||||
for (IBaseResource next : resourcesToAdd) {
|
||||
|
@ -472,7 +475,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
|
||||
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails(theActionName + " completed in " + delay + "ms");
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics(theActionName + " completed in " + delay + "ms");
|
||||
|
||||
for (IdDt next : allIds) {
|
||||
IdDt replacement = idSubstitutions.get(next);
|
||||
|
@ -482,7 +485,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
if (replacement.equals(next)) {
|
||||
continue;
|
||||
}
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
|
||||
statusOperationOutcome.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDiagnostics("Placeholder resource ID \"" + next + "\" was replaced with permanent ID \"" + replacement + "\"");
|
||||
}
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
|
|
@ -65,7 +65,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
|||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
|
||||
class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
public SearchParamExtractorDstu1(FhirContext theContext) {
|
||||
super(theContext);
|
||||
|
|
|
@ -70,7 +70,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
|||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
|
||||
class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||
|
||||
public SearchParamExtractorDstu2(FhirContext theContext) {
|
||||
super(theContext);
|
||||
|
|
|
@ -52,6 +52,9 @@ import ca.uhn.fhir.validation.ValidationResult;
|
|||
|
||||
public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResourceProvider<T> {
|
||||
|
||||
public static final String OPERATION_NAME_META = "$meta";
|
||||
public static final String OPERATION_NAME_META_DELETE = "$meta-delete";
|
||||
public static final String OPERATION_NAME_META_ADD = "$meta-add";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceProviderDstu2.class);
|
||||
|
||||
public JpaResourceProviderDstu2() {
|
||||
|
@ -91,7 +94,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$meta", idempotent=true, returnParameters= {
|
||||
@Operation(name=OPERATION_NAME_META, idempotent=true, returnParameters= {
|
||||
@OperationParam(name="return", type=MetaDt.class)
|
||||
})
|
||||
//@formatter:on
|
||||
|
@ -103,7 +106,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$meta", idempotent=true, returnParameters= {
|
||||
@Operation(name=OPERATION_NAME_META, idempotent=true, returnParameters= {
|
||||
@OperationParam(name="return", type=MetaDt.class)
|
||||
})
|
||||
//@formatter:on
|
||||
|
@ -115,7 +118,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$meta-add", idempotent=true, returnParameters= {
|
||||
@Operation(name=OPERATION_NAME_META_ADD, idempotent=true, returnParameters= {
|
||||
@OperationParam(name="return", type=MetaDt.class)
|
||||
})
|
||||
//@formatter:on
|
||||
|
@ -127,7 +130,7 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
|
|||
}
|
||||
|
||||
//@formatter:off
|
||||
@Operation(name="$meta-delete", idempotent=true, returnParameters= {
|
||||
@Operation(name=OPERATION_NAME_META_DELETE, idempotent=true, returnParameters= {
|
||||
@OperationParam(name="return", type=MetaDt.class)
|
||||
})
|
||||
//@formatter:on
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
|
@ -14,12 +17,15 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -64,6 +70,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.param.CompositeParam;
|
||||
|
@ -82,6 +89,8 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
|||
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.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
||||
|
@ -101,11 +110,12 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
private static IFhirResourceDao<Questionnaire> ourQuestionnaireDao;
|
||||
private static IFhirSystemDao<Bundle> ourSystemDao;
|
||||
private static IFhirResourceDao<Practitioner> ourPractitionerDao;
|
||||
private static IServerInterceptor ourInterceptor;
|
||||
|
||||
private List<String> extractNames(IBundleProvider theSearch) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (IBaseResource next : theSearch.getResources(0, theSearch.size())) {
|
||||
Patient nextPt = (Patient)next;
|
||||
Patient nextPt = (Patient) next;
|
||||
retVal.add(nextPt.getNameFirstRep().getNameAsSingleString());
|
||||
}
|
||||
return retVal;
|
||||
|
@ -114,9 +124,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
@Test
|
||||
public void testCreateOperationOutcome() {
|
||||
/*
|
||||
* If any of this ever fails, it means that one of the OperationOutcome
|
||||
* issue severity codes has changed code value across versions. We store
|
||||
* the string as a constant, so something will need to be fixed.
|
||||
* If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code
|
||||
* value across versions. We store the string as a constant, so something will need to be fixed.
|
||||
*/
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
|
@ -127,7 +136,46 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() {
|
||||
Observation o1 = new Observation();
|
||||
o1.getCode().addCoding().setSystem("foo").setCode("testRead");
|
||||
IIdType id1 = ourObservationDao.create(o1).getId();
|
||||
|
||||
/*
|
||||
* READ
|
||||
*/
|
||||
|
||||
reset(ourInterceptor);
|
||||
Observation obs = ourObservationDao.read(id1.toUnqualifiedVersionless());
|
||||
assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
|
||||
|
||||
// Verify interceptor
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture());
|
||||
ActionRequestDetails details = detailsCapt.getValue();
|
||||
assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue());
|
||||
assertEquals("Observation", details.getResourceType());
|
||||
|
||||
/*
|
||||
* VREAD
|
||||
*/
|
||||
assertTrue(id1.hasVersionIdPart()); // just to make sure..
|
||||
reset(ourInterceptor);
|
||||
obs = ourObservationDao.read(id1);
|
||||
assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
|
||||
|
||||
// Verify interceptor
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture());
|
||||
details = detailsCapt.getValue();
|
||||
assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue());
|
||||
assertEquals("Observation", details.getResourceType());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testChoiceParamConcept() {
|
||||
|
@ -271,6 +319,15 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
IIdType id = ourPatientDao.create(p).getId();
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
// Verify interceptor
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
|
||||
ActionRequestDetails details = detailsCapt.getValue();
|
||||
assertNotNull(details.getId());
|
||||
assertEquals("Patient", details.getResourceType());
|
||||
|
||||
reset(ourInterceptor);
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.addName().addFamily("Hello");
|
||||
|
@ -278,6 +335,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
assertEquals(id.getIdPart(), results.getId().getIdPart());
|
||||
assertFalse(results.getCreated().booleanValue());
|
||||
|
||||
verifyNoMoreInteractions(ourInterceptor);
|
||||
|
||||
// Now create a second one
|
||||
|
||||
p = new Patient();
|
||||
|
@ -522,7 +581,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
patient.addName().addFamily("Tester_testDeleteThenUndelete").addGiven("Joe");
|
||||
IIdType id = ourPatientDao.create(patient).getId();
|
||||
assertThat(id.getValue(), endsWith("/_history/1"));
|
||||
assertThat(id.getValue(), Matchers.endsWith("/_history/1"));
|
||||
|
||||
// should be ok
|
||||
ourPatientDao.read(id.toUnqualifiedVersionless());
|
||||
|
@ -1508,7 +1567,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchNameParam() {
|
||||
IIdType id1;
|
||||
|
@ -1765,7 +1823,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
}
|
||||
|
||||
/*
|
||||
* TODO: it's kind of weird that we throw a 404 for textual IDs that don't exist, but just return an empty list for numeric IDs that don't exist
|
||||
* TODO: it's kind of weird that we throw a 404 for textual IDs that don't exist, but just return an empty list
|
||||
* for numeric IDs that don't exist
|
||||
*/
|
||||
|
||||
result = toList(ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("999999999999999")));
|
||||
|
@ -1773,7 +1832,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchStringParam() {
|
||||
{
|
||||
|
@ -1800,7 +1858,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchStringParamReallyLong() {
|
||||
String methodName = "testSearchStringParamReallyLong";
|
||||
|
@ -1863,8 +1920,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam001");
|
||||
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam1");
|
||||
patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem")
|
||||
.setDisplay("testSearchTokenParamDisplay");
|
||||
patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem").setDisplay("testSearchTokenParamDisplay");
|
||||
ourPatientDao.create(patient);
|
||||
|
||||
patient = new Patient();
|
||||
|
@ -2639,7 +2695,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
ourLog.info("Names: {}", names);
|
||||
assertThat(names.subList(0, 2), contains("Giv2 Fam2", "Giv1 Fam2"));
|
||||
assertThat(names.subList(2, 4), contains("Giv2 Fam1", "Giv1 Fam1"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreUnversionedResources() {
|
||||
|
@ -3011,7 +3067,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
ourOrganizationDao.update(p2);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
// good
|
||||
ourLog.error("Good", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3063,6 +3119,17 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
|
|||
ourQuestionnaireDao = ourCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class);
|
||||
ourQuestionnaireResponseDao = ourCtx.getBean("myQuestionnaireResponseDaoDstu2", IFhirResourceDao.class);
|
||||
ourFhirCtx = ourCtx.getBean(FhirContext.class);
|
||||
|
||||
ourInterceptor = mock(IServerInterceptor.class);
|
||||
|
||||
DaoConfig daoConfig = ourCtx.getBean(DaoConfig.class);
|
||||
daoConfig.setInterceptors(ourInterceptor);
|
||||
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
reset(ourInterceptor);
|
||||
}
|
||||
|
||||
private static void deleteEverything() {
|
||||
|
|
|
@ -16,19 +16,14 @@
|
|||
<bean id="myDaoConfig" class="ca.uhn.fhir.jpa.dao.DaoConfig">
|
||||
</bean>
|
||||
|
||||
<bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
|
||||
|
||||
<bean id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
||||
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"></property>
|
||||
<property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" />
|
||||
<property name="username" value=""/>
|
||||
<property name="password" value=""/>
|
||||
</bean>
|
||||
|
||||
<bean id="myPersistenceDataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" lazy-init="true">
|
||||
<!-- <property name="url" value="jdbc:hsqldb:mem:unit-testing-jpa"/> -->
|
||||
<!-- <property name="url" value="jdbc:hsqldb:file:svcret.hsqldb" /> -->
|
||||
<property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" />
|
||||
<!--
|
||||
<property name="username" value="sa" />
|
||||
<property name="password" value="" />
|
||||
-->
|
||||
</bean>
|
||||
|
||||
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||
<property name="dataSource" ref="myPersistenceDataSource" />
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
<groupId>ca.uhn.hapi.example</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-example</artifactId>
|
||||
<version>1.2-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>HAPI FHIR JPA Server - Example</name>
|
||||
|
@ -82,14 +81,13 @@
|
|||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.2</version>
|
||||
<version>${logback_version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Needed for JEE/Servlet support -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -100,7 +98,7 @@
|
|||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>${thymeleaf-version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Used for CORS support -->
|
||||
|
@ -134,7 +132,6 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
|
@ -146,19 +143,25 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<version>10.11.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbynet</artifactId>
|
||||
<version>10.11.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbyclient</artifactId>
|
||||
<version>10.11.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Arquillian is just used for automated tests, you don't neccesarily need it
|
||||
to use this example.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.jboss.arquillian.junit</groupId>
|
||||
<artifactId>arquillian-junit-container</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet_api_version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|||
participants.add(participant);
|
||||
auditEvent.setParticipant(participants);
|
||||
|
||||
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
|
||||
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getRestOperationType());
|
||||
byte[] query = getQueryFromRequestDetails(theRequestDetails);
|
||||
List<ObjectElement> auditableObjects = new ArrayList<SecurityEvent.ObjectElement>();
|
||||
for (BundleEntry entry : theResponseObject.getEntries()) {
|
||||
|
@ -193,7 +193,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|||
auditEvent.setParticipant(participants);
|
||||
|
||||
byte[] query = getQueryFromRequestDetails(theRequestDetails);
|
||||
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
|
||||
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getRestOperationType());
|
||||
ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle, query);
|
||||
if (auditableObject == null) {
|
||||
log.debug("No auditable resources to audit");
|
||||
|
@ -235,7 +235,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
|
|||
*/
|
||||
protected Event getEventInfo(RequestDetails theRequestDetails) {
|
||||
Event event = new Event();
|
||||
event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getResourceOperationType()));
|
||||
event.setAction(mapResourceTypeToSecurityEventAction(theRequestDetails.getRestOperationType()));
|
||||
event.setDateTimeWithMillisPrecision(new Date());
|
||||
event.setOutcome(SecurityEventOutcomeEnum.SUCCESS); // we audit successful return of PHI only, otherwise an
|
||||
// exception is thrown and no resources are returned to be
|
||||
|
|
|
@ -145,8 +145,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
|
||||
// Conformance.RestResourceSearchParam>();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
|
||||
if (nextMethodBinding.getResourceOperationType() != null) {
|
||||
RestfulOperationTypeEnum resOp = RestfulOperationTypeEnum.VALUESET_BINDER.fromCodeString(nextMethodBinding.getResourceOperationType().getCode());
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
RestfulOperationTypeEnum resOp = RestfulOperationTypeEnum.VALUESET_BINDER.fromCodeString(nextMethodBinding.getRestOperationType().getCode());
|
||||
if (resOp != null) {
|
||||
if (resourceOps.contains(resOp) == false) {
|
||||
resourceOps.add(resOp);
|
||||
|
@ -154,7 +154,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
}
|
||||
|
||||
RestfulOperationSystemEnum sysOp = RestfulOperationSystemEnum.VALUESET_BINDER.fromCodeString(nextMethodBinding.getResourceOperationType().getCode());
|
||||
RestfulOperationSystemEnum sysOp = RestfulOperationSystemEnum.VALUESET_BINDER.fromCodeString(nextMethodBinding.getRestOperationType().getCode());
|
||||
if (sysOp != null) {
|
||||
if (systemOps.contains(sysOp) == false) {
|
||||
systemOps.add(sysOp);
|
||||
|
|
|
@ -39,8 +39,10 @@ import ca.uhn.fhir.rest.annotation.IdParam;
|
|||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
/**
|
||||
|
@ -74,6 +76,8 @@ public class InterceptorTest {
|
|||
order.verify(myInterceptor2, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet_api_version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -116,7 +115,6 @@
|
|||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -101,8 +101,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
|
||||
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getResourceOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getResourceOperationType().getCode();
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteractionEnum sysOp = SystemRestfulInteractionEnum.VALUESET_BINDER.fromCodeString(sysOpCode);
|
||||
if (sysOp == null) {
|
||||
|
@ -193,8 +193,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
|
||||
// Conformance.RestResourceSearchParam>();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
if (nextMethodBinding.getResourceOperationType() != null) {
|
||||
String resOpCode = nextMethodBinding.getResourceOperationType().getCode();
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (resOpCode != null) {
|
||||
TypeRestfulInteractionEnum resOp = TypeRestfulInteractionEnum.VALUESET_BINDER.fromCodeString(resOpCode);
|
||||
if (resOp != null) {
|
||||
|
|
|
@ -11,7 +11,6 @@ public class BundleTest {
|
|||
|
||||
@Test
|
||||
public void testGetLink() {
|
||||
|
||||
Bundle b = new Bundle();
|
||||
Link link = b.getLink(Bundle.LINK_NEXT);
|
||||
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.BundleInclusionRule;
|
||||
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.util.PortUtil;
|
||||
|
||||
public class ServerActionInterceptorTest {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static final FhirContext ourCtx = FhirContext.forDstu2();
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static IServerInterceptor ourInterceptor;
|
||||
private static IGenericClient ourFhirClient;
|
||||
|
||||
@Test
|
||||
public void testRead() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture());
|
||||
|
||||
ActionRequestDetails details = detailsCapt.getValue();
|
||||
assertEquals("Patient/123", details.getId().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVRead() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture());
|
||||
|
||||
ActionRequestDetails details = detailsCapt.getValue();
|
||||
assertEquals("Patient/123/_history/456", details.getId().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAMILY");
|
||||
ourFhirClient.create().resource(patient).execute();
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
|
||||
|
||||
ActionRequestDetails details = detailsCapt.getValue();
|
||||
assertEquals("Patient", details.getResourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAMILY");
|
||||
patient.setId("Patient/123");
|
||||
ourFhirClient.update().resource(patient).execute();
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture());
|
||||
|
||||
ActionRequestDetails details = detailsCapt.getValue();
|
||||
assertEquals("Patient", details.getResourceType());
|
||||
assertEquals("Patient/123", details.getId().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistorySystem() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryType() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture());
|
||||
assertEquals("Patient", detailsCapt.getValue().getResourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryInstance() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture());
|
||||
assertEquals("Patient", detailsCapt.getValue().getResourceType());
|
||||
assertEquals("Patient/123", detailsCapt.getValue().getId().getValue());
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.registerInterceptor(new ResponseHighlighterInterceptor());
|
||||
servlet.setResourceProviders(new DummyPatientResourceProvider());
|
||||
servlet.setPlainProviders(new PlainProvider());
|
||||
servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
ourInterceptor = mock(InterceptorAdapter.class);
|
||||
servlet.registerInterceptor(ourInterceptor);
|
||||
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
ourFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Before
|
||||
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);
|
||||
}
|
||||
|
||||
public static class PlainProvider {
|
||||
|
||||
@History()
|
||||
public List<IBaseResource> history() {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId("Patient/123/_history/2");
|
||||
return Collections.singletonList((IBaseResource) retVal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Read(version = true)
|
||||
public Patient read(@IdParam IdDt theId) {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId(theId);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<Patient> history() {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId("Patient/123/_history/2");
|
||||
return Collections.singletonList(retVal);
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<Patient> history(@IdParam IdDt theId) {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId("Patient/123/_history/2");
|
||||
return Collections.singletonList(retVal);
|
||||
}
|
||||
|
||||
@Create()
|
||||
public MethodOutcome create(@ResourceParam Patient thePatient) {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId("Patient/123/_history/2");
|
||||
return new MethodOutcome(retVal.getId());
|
||||
}
|
||||
|
||||
@Update()
|
||||
public MethodOutcome update(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId("Patient/123/_history/2");
|
||||
return new MethodOutcome(retVal.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@
|
|||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet_api_version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
|
||||
private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps,
|
||||
BaseMethodBinding<?> nextMethodBinding) {
|
||||
if (nextMethodBinding.getResourceOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getResourceOperationType().getCode();
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (sysOpCode != null) {
|
||||
SystemRestfulInteraction sysOp;
|
||||
try {
|
||||
|
@ -200,8 +200,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
|
||||
// Conformance.RestResourceSearchParam>();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
|
||||
if (nextMethodBinding.getResourceOperationType() != null) {
|
||||
String resOpCode = nextMethodBinding.getResourceOperationType().getCode();
|
||||
if (nextMethodBinding.getRestOperationType() != null) {
|
||||
String resOpCode = nextMethodBinding.getRestOperationType().getCode();
|
||||
if (resOpCode != null) {
|
||||
TypeRestfulInteraction resOp;
|
||||
try {
|
||||
|
|
44
pom.xml
44
pom.xml
|
@ -171,7 +171,7 @@
|
|||
<siteMainDirectory>${user.home}/sites/hapi-fhir</siteMainDirectory>
|
||||
<scmPubCheckoutDirectory>${user.home}/sites/scm/hapi-fhir</scmPubCheckoutDirectory>
|
||||
|
||||
<!-- Plugin Versions -->
|
||||
<!-- Dependency Versions -->
|
||||
<apache_httpclient_version>4.4</apache_httpclient_version>
|
||||
<apache_httpcore_version>4.4</apache_httpcore_version>
|
||||
<commons_io_version>2.4</commons_io_version>
|
||||
|
@ -209,7 +209,6 @@
|
|||
<phloc_schematron_version>2.7.1</phloc_schematron_version>
|
||||
<phloc_commons_version>4.3.6</phloc_commons_version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<servlet_api_version>3.1.0</servlet_api_version>
|
||||
<slf4j_version>1.7.10</slf4j_version>
|
||||
<spring_version>4.1.5.RELEASE</spring_version>
|
||||
<spring_security_version>3.2.4.RELEASE</spring_security_version>
|
||||
|
@ -220,6 +219,47 @@
|
|||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Set dependency versions -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbynet</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbyclient</artifactId>
|
||||
<version>${derby_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.arquillian.junit</groupId>
|
||||
<artifactId>arquillian-junit-container</artifactId>
|
||||
<version>1.1.8.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.10.19</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<mxGraphModel dx="940" dy="431" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" fold="1" page="1" pageScale="1" pageWidth="826" pageHeight="1169" style="default-style2" math="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="RESTful
Server" style="shape=umlLifeline;perimeter=lifelinePerimeter;size=46;strokeColor=#0000FF;strokeWidth=2;fillColor=#99FFFF" parent="1" vertex="1"><mxGeometry x="150" y="20" width="100" height="280" as="geometry"/></mxCell><mxCell id="3" value="Interceptor" style="shape=umlLifeline;perimeter=lifelinePerimeter;size=46;strokeColor=#0000FF;strokeWidth=2;fillColor=#99FFFF" parent="1" vertex="1"><mxGeometry x="280" y="20" width="100" height="280" as="geometry"/></mxCell><mxCell id="4" value="Resource/Plain
Provider
Method" style="shape=umlLifeline;perimeter=lifelinePerimeter;size=46;strokeColor=#0000FF;strokeWidth=2;fillColor=#99FFFF" parent="1" vertex="1"><mxGeometry x="400" y="20" width="100" height="280" as="geometry"/></mxCell><mxCell id="7" value="" style="ellipse;shape=startState;fillColor=#000000;strokeColor=#ff0000;" parent="1" vertex="1"><mxGeometry x="70" y="80" width="30" height="30" as="geometry"/></mxCell><mxCell id="8" value="Incoming Request" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0.5;entryY=0.3;entryPerimeter=0" parent="1" source="7" edge="1"><mxGeometry x="70" y="80" as="geometry"><mxPoint x="200" y="95" as="targetPoint"/></mxGeometry></mxCell><mxCell id="21" value="Request is handled" style="rounded=1;whiteSpace=wrap" parent="1" vertex="1"><mxGeometry x="390" y="150" width="120" height="30" as="geometry"/></mxCell><mxCell id="22" value="" style="edgeStyle=none;align=left;entryX=0.5;entryY=0.632;entryPerimeter=0;exitX=0.5;exitY=0.632;exitPerimeter=0" parent="1" edge="1"><mxGeometry width="100" height="100" relative="1" as="geometry"><mxPoint x="200" y="140.15999999999997" as="sourcePoint"/><mxPoint x="450" y="140.15999999999997" as="targetPoint"/></mxGeometry></mxCell><mxCell id="24" value="" parent="1" vertex="1"><mxGeometry x="320" y="233" width="20" height="27" as="geometry"/></mxCell><mxCell id="25" value="handleException" style="edgeStyle=elbowEdgeStyle;elbow=vertical;verticalAlign=bottom;endArrow=block;exitX=0.5;exitY=0.478;exitPerimeter=0;align=left" parent="1" target="24" edge="1"><mxGeometry x="221.75" y="233.25" as="geometry"><mxPoint x="200" y="233.39999999999998" as="sourcePoint"/></mxGeometry></mxCell><mxCell id="26" value="return true;" style="edgeStyle=elbowEdgeStyle;elbow=vertical;verticalAlign=bottom;dashed=1;endArrow=open;endSize=8;" parent="1" source="24" edge="1"><mxGeometry x="221.75" y="233.25" as="geometry"><mxPoint x="200" y="260" as="targetPoint"/></mxGeometry></mxCell><mxCell id="27" value="" style="ellipse;shape=endState;fillColor=#000000;strokeColor=#ff0000;align=left" parent="1" vertex="1"><mxGeometry x="70" y="270" width="30" height="30" as="geometry"/></mxCell><mxCell id="28" value="Response" style="edgeStyle=orthogonalEdgeStyle;endArrow=block;dashed=0;dashPattern=1 4;align=left;entryX=1;entryY=0.5;exitX=0.5;exitY=0.954;exitPerimeter=0;endFill=1;strokeWidth=1;strokeColor=#FF0000" parent="1" target="27" edge="1"><mxGeometry x="0.500299820107935" y="-13" width="100" height="100" relative="1" as="geometry"><mxPoint x="200" y="285.05999999999995" as="sourcePoint"/><mxPoint x="650" y="213" as="targetPoint"/><mxPoint x="-8" y="-12" as="offset"/></mxGeometry></mxCell><mxCell id="29" value="" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;" vertex="1" parent="1"><mxGeometry x="175" y="100" width="50" height="30" as="geometry"/></mxCell><mxCell id="30" value="throw exception" style="edgeStyle=elbowEdgeStyle;elbow=vertical;verticalAlign=bottom;dashed=1;endArrow=open;endSize=8;" edge="1" parent="1"><mxGeometry x="351.75" y="173.25" as="geometry"><mxPoint x="200" y="200" as="targetPoint"/><mxPoint x="450" y="200" as="sourcePoint"/></mxGeometry></mxCell></root></mxGraphModel>
|
||||
<mxfile userAgent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:40.0) Gecko/20100101 Firefox/40.0" type="device"><diagram>3VlNb9s4EP01PsaQTH/pWCd2d4EWCOoFdvfIWJRFlBa1FJU4/fUdWkN9UbEVR2m79cEwh+SIfPPmcUSPyO3h+FHRNP4sQyZGEy88jsjdaDKZz2fwbQzPhWE69QvDXvGwMNUMW/6NodFDa85DljUGaimF5mnTuJNJwna6YYukaD4ipXvrvjJsd1S41r95qOPCupzMK/sfjO9j+xh/HhQ9mX62PkIW0Vzom5MJ+kz3gVpfp12RNQCmpAQ35tfheMuEAc0CUmx980JvuUjFElzI+Qm4iEcqclzjl/X2ryiHMbAxcqQjstoy9ciUs5kspqn5mR/EJx4xwRNorVKm+IFpGE/uBJrvK9sqK6I4nZvfWsmv7FYKaUbD4zz4bGCp2GNxBlRWEReiNjIINvBxd4sAwHo1Q5qdTLj7j0zCQtQzDLG9MyQTchDx8p6qGPse2uJafMkCjRS5ti9dV3DDD0S8G33ioP9nAjDtWKpho78n4JPllYDPBwB86tKdZTIHxGHmvaA8qfH+XslHEBgIRGn6zHQsw980MlML+8+IzMKJjIMyTABlN8havDNNld5qqo2xBYsBEFbbAW0UYc8AmFkVQMgst2uQkS7EBgBs2aEdO3ngyR6sX9h/Ocu0C2EIZxo2mXiQT+vKsDoZoCOWin+Tiabw6JWBgsMp+EHwfQKdD1JreTCjk/CDUqcJMmXQZSx4Ri/Pwg5oq+d/wOiNZ7b576lJbLOWJneIVUeYMHPr/DEbfEPcugKCPu4lh8dXMtZKlgBrGesBmLlnGie1oloup1egS1LWRQvD6/HMUIsmoWCuLCmZJyHYyZ0PwD7FXLNtSk+IPUFBNgD9SdBEoTxN65phdWToDOgoXs4SPpEnWabIZMEiWMwZNs6JUdsOPoLxyHVtDrQaU6Ddj8EX6XpJe0ujYoJq/tgsVq+nsz/1xv4sqD6YX9ZjkXoOvR2/01Z5dcnvgGnjnvVDEN6S2Z6RAXqtBaqL71af3sR3lJjajorMXx9NxcglFC/X6L2V+J5q/yDk7uuZHJgujPy7OdDKvJdiUTCgGcO+qg794wWiVAVobIG7Ph2MF1zLhQy4hqn4ClmLq2I6VxBOTytjwkrlPQIb0iwuj4hLZ3qvk/iXCRrx8Q38HcTlqnLVgHl1sdorea4uXIndwA+oXDtKV3gLS2UCYJ3juVTw8rWXCRUNsrvKVHLa4GYa91SDChn0ABRveqYIKLKgLAGssnXpXDAzjrp0Dpa0gfiiu+YLXGWpBXmzOQW5lyS+stCFVXveJAiWE8/3FgFpJtqNj1cQP6vOIGQx9ur1QCvr+9YZ8/Y1jr1bvJD7jqMbJGcJEOaF9SOjKINYvFE+UJheJx87IXNw0FHIk1WsD5ZvQ1yKtfS4jH6NJhbwodXB+qhho2OT38D5969zhjkO+yYnmTlHn78gQ9Qrtv1a+rfrdcfRcGUPcd9rX+BpB8b9C3TLpx9QoMPLX3tHqWJwjbhjWfb/LdJbbG6eRzaK/SPUVe8RbwjSgxe4Da0+KOXvQF334r6s2JPcTLuyYu939fYqkaoYAAhXFKhOCie89WL+VwrvbKi7AmhW/7AVw6v/J8n6Ow==</diagram></mxfile>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 6.7 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 10 KiB |
|
@ -11,21 +11,27 @@
|
|||
<!-- The body of the document contains a number of sections -->
|
||||
<section name="Server Interceptors">
|
||||
|
||||
<img src="svg/restful-server-interceptors.svg" alt="Interceptors" align="right"/>
|
||||
|
||||
<p>
|
||||
The RESTful server provides a powerful mechanism for adding cross-cutting behaviour
|
||||
(e.g. requests, such as authnorization, auditing, fancy output, logging, etc.)
|
||||
to each incoming request that it processes. This mechanism consists of defining one or
|
||||
more <b>interceptors</b> that will be invoked at defined points in the processing of
|
||||
each incoming request.
|
||||
</p>
|
||||
|
||||
<img src="svg/restful-server-interceptors.svg" alt="Interceptors"/>
|
||||
|
||||
<p>
|
||||
Interceptors will intercept the incoming request, and can take action such as
|
||||
logging or auditing it, or examining/injecting headers. They can optionally choose
|
||||
to handle the request themself and the cancel any subsequent processing. Interceptors
|
||||
to handle the request directly and the cancel any subsequent processing (in other words,
|
||||
the interceptor can choose to supply a response to the client, and can then signal
|
||||
to the server that it does not need to do so).
|
||||
</p>
|
||||
<p>
|
||||
Interceptors
|
||||
may also be notified of responses prior to those responses being served to a client,
|
||||
and may audit or even cancel response. The diagram on the right shows the
|
||||
and may audit or even cancel the response. The diagram on the right shows the
|
||||
lifecycle of a normal (non failing) request which is subject to an interceptor.
|
||||
</p>
|
||||
|
||||
|
@ -34,7 +40,7 @@
|
|||
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.html">IServerInterceptor</a>
|
||||
interface (or extend the convenience
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.html">InterceptorAdapter</a>
|
||||
class provided). The RESTful server will normally invoke the interceptor at three
|
||||
class provided). The RESTful server will normally invoke the interceptor at several
|
||||
points in the execution of the client request.
|
||||
</p>
|
||||
|
||||
|
@ -43,17 +49,62 @@
|
|||
Before any processing at all is performed on the request,
|
||||
<b>incomingRequestPreProcessed</b> will be invoked. This can be useful
|
||||
if you wish to handle some requests completely outside of HAPI's processing
|
||||
mechanism. If you are handling a request in your interceptor, you may
|
||||
return <code>false</code> from your implementation method to signal to
|
||||
HAPI that processing of the request should stop immediately.
|
||||
mechanism.
|
||||
<ul>
|
||||
<li>
|
||||
If this method returns <code>true</code>, processing continues to the
|
||||
next interceptor, and ultimately to the next phase of processing.
|
||||
</li>
|
||||
<li>
|
||||
Once the request is parsed (but before it is handled),
|
||||
If this method returns <code>false</code>, processing stops immediately.
|
||||
This is useful if the interceptor wishes to supply its own response
|
||||
by directly calling methods on the <code>HttpServletResponse</code>
|
||||
</li>
|
||||
<li>
|
||||
If this method throws any subclass of
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.html">BaseServerResponseException</a>,
|
||||
processing is stopped immedicately and the corresponding status is returned to the client.
|
||||
This is useful if an interceptor wishes to abort the request (e.g. because
|
||||
it did not detect valid credentials)
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Once the request is classified (meaning that the URL and request headers are
|
||||
examined to determine exactly what kind of request is being made),
|
||||
<b>incomingRequestPostProcessed</b> will be invoked. This method has
|
||||
an additional parameter, the
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/method/RequestDetails.html">RequestDetails</a>
|
||||
object which contains details about what operation is about to be
|
||||
called, and what request parameters were receievd with that request.
|
||||
<ul>
|
||||
<li>
|
||||
If this method returns <code>true</code>, processing continues to the
|
||||
next interceptor, and ultimately to the next phase of processing.
|
||||
</li>
|
||||
<li>
|
||||
If this method returns <code>false</code>, processing stops immediately.
|
||||
This is useful if the interceptor wishes to supply its own response
|
||||
by directly calling methods on the <code>HttpServletResponse</code>
|
||||
</li>
|
||||
<li>
|
||||
If this method throws any subclass of
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.html">BaseServerResponseException</a>,
|
||||
processing is stopped immedicately and the corresponding status is returned to the client.
|
||||
This is useful if an interceptor wishes to abort the request (e.g. because
|
||||
it did not detect valid credentials)
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
Once the request is being handled,
|
||||
<b>incomingRequestPreHandled</b> will be invoked. This method is useful in that
|
||||
it provides details about the FHIR operation being invoked (e.g. is this a "read" or a "create"? what
|
||||
is the resource type and ID of the resource being accessed, etc.). This method can be
|
||||
useful for adding finer grained access controls. Note that <code>incomingRequestPreHandled</code>
|
||||
is not able to directly supply a response, but it may throw a
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.html">BaseServerResponseException</a>
|
||||
to abort processing.
|
||||
</li>
|
||||
<li>
|
||||
After the operation is handled (by invoking the corresponding ResourceProvider or PlainProvider method),
|
||||
|
|
Loading…
Reference in New Issue