Update endpoint validation interceptors (#2698)
This commit is contained in:
parent
fe763dd2d6
commit
1d2b47c9fb
|
@ -271,7 +271,7 @@ public class FhirValidator {
|
|||
*
|
||||
* @since 5.5.0
|
||||
*/
|
||||
public void setInterceptorBraodcaster(IInterceptorBroadcaster theInterceptorBraodcaster) {
|
||||
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBraodcaster) {
|
||||
myInterceptorBraodcaster = theInterceptorBraodcaster;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1717,7 +1717,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
FhirValidator validator = getContext().newValidator();
|
||||
|
||||
validator.setInterceptorBraodcaster(CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest));
|
||||
validator.setInterceptorBroadcaster(CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequest));
|
||||
validator.registerValidatorModule(getInstanceValidator());
|
||||
validator.registerValidatorModule(new IdChecker(theMode));
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class RequireValidationRule extends BaseTypedRule {
|
|||
public RuleEvaluation evaluate(RequestDetails theRequestDetails, @Nonnull IBaseResource theResource) {
|
||||
|
||||
FhirValidator validator = getFhirContext().newValidator();
|
||||
validator.setInterceptorBraodcaster(CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails));
|
||||
validator.setInterceptorBroadcaster(CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails));
|
||||
validator.registerValidatorModule(myValidator);
|
||||
ValidationResult outcome = validator.validateWithResult(theResource);
|
||||
|
||||
|
|
|
@ -8,15 +8,13 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.validation.ValidationMessageSuppressingInterceptor;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -82,11 +80,13 @@ public class ValidationMessageSuppressingInterceptorTest extends BaseResourcePro
|
|||
upload("/r4/uscore/CodeSystem-dummy-loinc.json");
|
||||
upload("/r4/uscore/StructureDefinition-us-core-pulse-oximetry.json");
|
||||
|
||||
FhirValidator validator = myFhirCtx.newValidator();
|
||||
validator.setInterceptorBroadcaster(myInterceptorRegistry);
|
||||
validator.registerValidatorModule(new FhirInstanceValidator(myValidationSupport));
|
||||
|
||||
RequestValidatingInterceptor requestInterceptor = new RequestValidatingInterceptor();
|
||||
requestInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
requestInterceptor.setValidatorModules(Collections.singletonList(new FhirInstanceValidator(myValidationSupport)));
|
||||
requestInterceptor.setInterceptorBroadcaster(myInterceptorRegistry);
|
||||
requestInterceptor.setValidator(validator);
|
||||
ourRestServer.registerInterceptor(requestInterceptor);
|
||||
|
||||
|
||||
|
|
|
@ -21,17 +21,18 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
import ca.uhn.fhir.validation.*;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.text.StrLookup;
|
||||
import org.apache.commons.lang3.text.StrSubstitutor;
|
||||
|
@ -73,7 +74,7 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
private String myResponseOutcomeHeaderName = provideDefaultResponseHeaderName();
|
||||
|
||||
private List<IValidatorModule> myValidatorModules;
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
private FhirValidator myValidator;
|
||||
|
||||
private void addResponseIssueHeader(RequestDetails theRequestDetails, SingleValidationMessage theNext) {
|
||||
// Perform any string substitutions from the message format
|
||||
|
@ -87,15 +88,33 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
theRequestDetails.getResponse().addHeader(myResponseIssueHeaderName, headerValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a validator module to use.
|
||||
*
|
||||
* @see #setValidator(FhirValidator)
|
||||
*/
|
||||
public BaseValidatingInterceptor<T> addValidatorModule(IValidatorModule theModule) {
|
||||
Validate.notNull(theModule, "theModule must not be null");
|
||||
Validate.isTrue(myValidator == null, "Can not specify both a validator and validator modules. Only one needs to be supplied.");
|
||||
if (getValidatorModules() == null) {
|
||||
setValidatorModules(new ArrayList<IValidatorModule>());
|
||||
setValidatorModules(new ArrayList<>());
|
||||
}
|
||||
getValidatorModules().add(theModule);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the validator to use. This can be used as an alternative to {@link #addValidatorModule(IValidatorModule)}
|
||||
*
|
||||
* @see #addValidatorModule(IValidatorModule)
|
||||
* @see #setValidatorModules(List)
|
||||
*/
|
||||
public void setValidator(FhirValidator theValidator) {
|
||||
Validate.isTrue(theValidator == null || getValidatorModules() == null || getValidatorModules().isEmpty(), "Can not specify both a validator and validator modules. Only one needs to be supplied.");
|
||||
myValidator = theValidator;
|
||||
}
|
||||
|
||||
|
||||
abstract ValidationResult doValidate(FhirValidator theValidator, T theRequest);
|
||||
|
||||
/**
|
||||
|
@ -115,6 +134,15 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
return myAddResponseOutcomeHeaderOnSeverity != null ? ResultSeverityEnum.values()[myAddResponseOutcomeHeaderOnSeverity] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the validation produces a result with at least the given severity, a header with the name
|
||||
* specified by {@link #setResponseOutcomeHeaderName(String)} will be added containing a JSON encoded
|
||||
* OperationOutcome resource containing the validation results.
|
||||
*/
|
||||
public void setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum theAddResponseOutcomeHeaderOnSeverity) {
|
||||
myAddResponseOutcomeHeaderOnSeverity = theAddResponseOutcomeHeaderOnSeverity != null ? theAddResponseOutcomeHeaderOnSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum length for an individual header. If an individual header would be written exceeding this length,
|
||||
* the header value will be truncated.
|
||||
|
@ -123,6 +151,15 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
return myMaximumHeaderLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum length for an individual header. If an individual header would be written exceeding this length,
|
||||
* the header value will be truncated. Value must be greater than 100.
|
||||
*/
|
||||
public void setMaximumHeaderLength(int theMaximumHeaderLength) {
|
||||
Validate.isTrue(theMaximumHeaderLength >= 100, "theMaximumHeadeerLength must be >= 100");
|
||||
myMaximumHeaderLength = theMaximumHeaderLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the header specified by {@link #setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum)}
|
||||
*/
|
||||
|
@ -130,10 +167,23 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
return myResponseOutcomeHeaderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the header specified by {@link #setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum)}
|
||||
*/
|
||||
public void setResponseOutcomeHeaderName(String theResponseOutcomeHeaderName) {
|
||||
Validate.notEmpty(theResponseOutcomeHeaderName, "theResponseOutcomeHeaderName can not be empty or null");
|
||||
myResponseOutcomeHeaderName = theResponseOutcomeHeaderName;
|
||||
}
|
||||
|
||||
public List<IValidatorModule> getValidatorModules() {
|
||||
return myValidatorModules;
|
||||
}
|
||||
|
||||
public void setValidatorModules(List<IValidatorModule> theValidatorModules) {
|
||||
Validate.isTrue(myValidator == null || theValidatorModules == null || theValidatorModules.isEmpty(), "Can not specify both a validator and validator modules. Only one needs to be supplied.");
|
||||
myValidatorModules = theValidatorModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) this interceptor
|
||||
* will exit immediately and allow processing to continue if the validator throws
|
||||
|
@ -146,36 +196,6 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
return myIgnoreValidatorExceptions;
|
||||
}
|
||||
|
||||
abstract String provideDefaultResponseHeaderName();
|
||||
|
||||
/**
|
||||
* Sets the minimum severity at which an issue detected by the validator will result in a header being added to the
|
||||
* response. Default is {@link ResultSeverityEnum#INFORMATION}. Set to <code>null</code> to disable this behaviour.
|
||||
*
|
||||
* @see #setResponseHeaderName(String)
|
||||
* @see #setResponseHeaderValue(String)
|
||||
*/
|
||||
public void setAddResponseHeaderOnSeverity(ResultSeverityEnum theSeverity) {
|
||||
myAddResponseIssueHeaderOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the validation produces a result with at least the given severity, a header with the name
|
||||
* specified by {@link #setResponseOutcomeHeaderName(String)} will be added containing a JSON encoded
|
||||
* OperationOutcome resource containing the validation results.
|
||||
*/
|
||||
public void setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum theAddResponseOutcomeHeaderOnSeverity) {
|
||||
myAddResponseOutcomeHeaderOnSeverity = theAddResponseOutcomeHeaderOnSeverity != null ? theAddResponseOutcomeHeaderOnSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum severity at which an issue detected by the validator will fail/reject the request. Default is
|
||||
* {@link ResultSeverityEnum#ERROR}. Set to <code>null</code> to disable this behaviour.
|
||||
*/
|
||||
public void setFailOnSeverity(ResultSeverityEnum theSeverity) {
|
||||
myFailOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) this interceptor
|
||||
* will exit immediately and allow processing to continue if the validator throws
|
||||
|
@ -188,18 +208,30 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
myIgnoreValidatorExceptions = theIgnoreValidatorExceptions;
|
||||
}
|
||||
|
||||
abstract String provideDefaultResponseHeaderName();
|
||||
|
||||
/**
|
||||
* The maximum length for an individual header. If an individual header would be written exceeding this length,
|
||||
* the header value will be truncated. Value must be greater than 100.
|
||||
* Sets the minimum severity at which an issue detected by the validator will result in a header being added to the
|
||||
* response. Default is {@link ResultSeverityEnum#INFORMATION}. Set to <code>null</code> to disable this behaviour.
|
||||
*
|
||||
* @see #setResponseHeaderName(String)
|
||||
* @see #setResponseHeaderValue(String)
|
||||
*/
|
||||
public void setMaximumHeaderLength(int theMaximumHeaderLength) {
|
||||
Validate.isTrue(theMaximumHeaderLength >= 100, "theMaximumHeadeerLength must be >= 100");
|
||||
myMaximumHeaderLength = theMaximumHeaderLength;
|
||||
public void setAddResponseHeaderOnSeverity(ResultSeverityEnum theSeverity) {
|
||||
myAddResponseIssueHeaderOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum severity at which an issue detected by the validator will fail/reject the request. Default is
|
||||
* {@link ResultSeverityEnum#ERROR}. Set to <code>null</code> to disable this behaviour.
|
||||
*/
|
||||
public void setFailOnSeverity(ResultSeverityEnum theSeverity) {
|
||||
myFailOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the response header to add validation failures to
|
||||
*
|
||||
*
|
||||
* @see #setAddResponseHeaderOnSeverity(ResultSeverityEnum)
|
||||
*/
|
||||
protected void setResponseHeaderName(String theResponseHeaderName) {
|
||||
|
@ -240,7 +272,7 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
* <td>The validation message</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
*
|
||||
* @see #DEFAULT_RESPONSE_HEADER_VALUE
|
||||
* @see #setAddResponseHeaderOnSeverity(ResultSeverityEnum)
|
||||
*/
|
||||
|
@ -257,48 +289,39 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
myResponseIssueHeaderValueNoIssues = theResponseHeaderValueNoIssues;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the header specified by {@link #setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum)}
|
||||
*/
|
||||
public void setResponseOutcomeHeaderName(String theResponseOutcomeHeaderName) {
|
||||
Validate.notEmpty(theResponseOutcomeHeaderName, "theResponseOutcomeHeaderName can not be empty or null");
|
||||
myResponseOutcomeHeaderName = theResponseOutcomeHeaderName;
|
||||
}
|
||||
|
||||
public void setValidatorModules(List<IValidatorModule> theValidatorModules) {
|
||||
myValidatorModules = theValidatorModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for subclasses (e.g. add a tag (coding) to an incoming resource when a given severity appears in the
|
||||
* ValidationResult).
|
||||
*/
|
||||
protected void postProcessResult(RequestDetails theRequestDetails, ValidationResult theValidationResult) { }
|
||||
protected void postProcessResult(RequestDetails theRequestDetails, ValidationResult theValidationResult) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for subclasses on failure (e.g. add a response header to an incoming resource upon rejection).
|
||||
*/
|
||||
protected void postProcessResultOnFailure(RequestDetails theRequestDetails, ValidationResult theValidationResult) { }
|
||||
protected void postProcessResultOnFailure(RequestDetails theRequestDetails, ValidationResult theValidationResult) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: May return null
|
||||
*/
|
||||
protected ValidationResult validate(T theRequest, RequestDetails theRequestDetails) {
|
||||
FhirValidator validator = theRequestDetails.getServer().getFhirContext().newValidator();
|
||||
|
||||
IInterceptorBroadcaster interceptorBroadcaster = CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
|
||||
validator.setInterceptorBraodcaster(interceptorBroadcaster);
|
||||
|
||||
if (myValidatorModules != null) {
|
||||
for (IValidatorModule next : myValidatorModules) {
|
||||
validator.registerValidatorModule(next);
|
||||
}
|
||||
}
|
||||
|
||||
if (theRequest == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FhirValidator validator;
|
||||
if (myValidator != null) {
|
||||
validator = myValidator;
|
||||
} else {
|
||||
validator = theRequestDetails.getServer().getFhirContext().newValidator();
|
||||
if (myValidatorModules != null) {
|
||||
for (IValidatorModule next : myValidatorModules) {
|
||||
validator.registerValidatorModule(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValidationResult validationResult;
|
||||
try {
|
||||
validationResult = doValidate(validator, theRequest);
|
||||
|
@ -308,7 +331,7 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
return null;
|
||||
}
|
||||
if (e instanceof BaseServerResponseException) {
|
||||
throw (BaseServerResponseException)e;
|
||||
throw (BaseServerResponseException) e;
|
||||
}
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
@ -363,16 +386,8 @@ public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnric
|
|||
}
|
||||
|
||||
postProcessResult(theRequestDetails, validationResult);
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be used to specify an interceptor to broadcast validation events through. This
|
||||
* is mostly intended for the
|
||||
*/
|
||||
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) {
|
||||
myInterceptorBroadcaster = theInterceptorBroadcaster;
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
private static class MyLookup extends StrLookup<String> {
|
||||
|
|
Loading…
Reference in New Issue