Add repo validation outcome to response (#2293)
* Add repository validating interceptor outcome to response * Add a doc * Add a test and some docs
This commit is contained in:
parent
9d8bbaf868
commit
29cf20aac3
|
@ -64,6 +64,8 @@ Note that this rule alone does not actually enforce validation against the speci
|
|||
}
|
||||
```
|
||||
|
||||
<a name="require-validation"/>
|
||||
|
||||
# Rules: Require Validation to Declared Profiles
|
||||
|
||||
Use the following rule to require that resources of the given type be validated successfully before allowing them to be persisted. For every resource of the given type that is submitted for storage, the `Resource.meta.profile` field will be examined and the resource will be validated against any declarations found there.
|
||||
|
@ -101,3 +103,7 @@ Rules can declare that a specific profile is not allowed.
|
|||
```java
|
||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RepositoryValidatingInterceptorExamples.java|disallowProfiles}}
|
||||
```
|
||||
|
||||
# Adding Validation Outcome to HTTP Response
|
||||
|
||||
If you have included a [Require Validation](#require-validation) rule to your chain, you can add the `ValidationResultEnrichingInterceptor` to your server if you wish to have validation results added to and OperationOutcome objects that are returned by the server.
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.interceptor.validation;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -35,7 +36,7 @@ public interface IRepositoryValidatingRule {
|
|||
String getResourceType();
|
||||
|
||||
@Nonnull
|
||||
RuleEvaluation evaluate(@Nonnull IBaseResource theResource);
|
||||
RuleEvaluation evaluate(RequestDetails theRequestDetails, @Nonnull IBaseResource theResource);
|
||||
|
||||
class RuleEvaluation {
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
|
@ -47,7 +48,7 @@ import java.util.stream.Collectors;
|
|||
public class RepositoryValidatingInterceptor {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RepositoryValidatingInterceptor.class);
|
||||
private Multimap<String, IRepositoryValidatingRule> myRules = ArrayListMultimap.create();
|
||||
private final Multimap<String, IRepositoryValidatingRule> myRules = ArrayListMultimap.create();
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
|
@ -113,25 +114,25 @@ public class RepositoryValidatingInterceptor {
|
|||
* Interceptor hook method. This method should not be called directly.
|
||||
*/
|
||||
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED)
|
||||
void create(IBaseResource theResource) {
|
||||
handle(theResource);
|
||||
void create(RequestDetails theRequestDetails, IBaseResource theResource) {
|
||||
handle(theRequestDetails, theResource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interceptor hook method. This method should not be called directly.
|
||||
*/
|
||||
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED)
|
||||
void update(IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
handle(theNewResource);
|
||||
void update(RequestDetails theRequestDetails, IBaseResource theOldResource, IBaseResource theNewResource) {
|
||||
handle(theRequestDetails, theNewResource);
|
||||
}
|
||||
|
||||
private void handle(IBaseResource theNewResource) {
|
||||
private void handle(RequestDetails theRequestDetails, IBaseResource theNewResource) {
|
||||
Validate.notNull(myFhirContext, "No FhirContext has been set for this interceptor of type: %s", getClass());
|
||||
|
||||
String resourceType = myFhirContext.getResourceType(theNewResource);
|
||||
Collection<IRepositoryValidatingRule> rules = myRules.get(resourceType);
|
||||
for (IRepositoryValidatingRule nextRule : rules) {
|
||||
IRepositoryValidatingRule.RuleEvaluation outcome = nextRule.evaluate(theNewResource);
|
||||
IRepositoryValidatingRule.RuleEvaluation outcome = nextRule.evaluate(theRequestDetails, theNewResource);
|
||||
if (!outcome.isPasses()) {
|
||||
handleFailure(outcome);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.interceptor.validation;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
|
@ -148,13 +149,27 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param theProfileUrl
|
||||
* @return
|
||||
* If set, any resources that contain a profile declaration in <code>Resource.meta.profile</code>
|
||||
* matching {@literal theProfileUrl} will be rejected.
|
||||
*
|
||||
* @param theProfileUrl The profile canonical URL
|
||||
*/
|
||||
public FinalizedTypedRule disallowProfile(String theProfileUrl) {
|
||||
return disallowProfiles(theProfileUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a resource validation step using the FHIR Instance Validator and reject the
|
||||
* storage if the validation fails.
|
||||
*
|
||||
* <p>
|
||||
* If the {@link ValidationResultEnrichingInterceptor} is registered against the
|
||||
* {@link ca.uhn.fhir.rest.server.RestfulServer} interceptor registry, the validation results
|
||||
* will be appended to any <code>OperationOutcome</code> resource returned by the server.
|
||||
* </p>
|
||||
*
|
||||
* @see ValidationResultEnrichingInterceptor
|
||||
*/
|
||||
public FinalizedRequireValidationRule requireValidationToDeclaredProfiles() {
|
||||
RequireValidationRule rule = new RequireValidationRule(myFhirContext, myType, myValidationSupport, myValidatorResourceFetcher);
|
||||
myRules.add(rule);
|
||||
|
@ -251,13 +266,13 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
|
|||
* Specifies that if the validation results in any results with a severity of <code>theSeverity</code> or
|
||||
* greater, the resource will be tagged with the given tag when it is saved.
|
||||
*
|
||||
* @param theSeverity The minimum severity. Must be drawn from values in {@link ResultSeverityEnum} and must not be <code>null</code>
|
||||
* @param theSeverity The minimum severity. Must be drawn from values in {@link ResultSeverityEnum} and must not be <code>null</code>
|
||||
* @param theTagSystem The system for the tag to add. Must not be <code>null</code>
|
||||
* @param theTagCode The code for the tag to add. Must not be <code>null</code>
|
||||
* @param theTagCode The code for the tag to add. Must not be <code>null</code>
|
||||
* @return
|
||||
*/
|
||||
@Nonnull
|
||||
public FinalizedRequireValidationRule tagOnSeverity(@Nonnull String theSeverity,@Nonnull String theTagSystem,@Nonnull String theTagCode) {
|
||||
public FinalizedRequireValidationRule tagOnSeverity(@Nonnull String theSeverity, @Nonnull String theTagSystem, @Nonnull String theTagCode) {
|
||||
ResultSeverityEnum severity = ResultSeverityEnum.fromCode(toLowerCase(theSeverity));
|
||||
return tagOnSeverity(severity, theTagSystem, theTagCode);
|
||||
}
|
||||
|
@ -266,13 +281,13 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
|
|||
* Specifies that if the validation results in any results with a severity of <code>theSeverity</code> or
|
||||
* greater, the resource will be tagged with the given tag when it is saved.
|
||||
*
|
||||
* @param theSeverity The minimum severity. Must be drawn from values in {@link ResultSeverityEnum} and must not be <code>null</code>
|
||||
* @param theSeverity The minimum severity. Must be drawn from values in {@link ResultSeverityEnum} and must not be <code>null</code>
|
||||
* @param theTagSystem The system for the tag to add. Must not be <code>null</code>
|
||||
* @param theTagCode The code for the tag to add. Must not be <code>null</code>
|
||||
* @param theTagCode The code for the tag to add. Must not be <code>null</code>
|
||||
* @return
|
||||
*/
|
||||
@Nonnull
|
||||
public FinalizedRequireValidationRule tagOnSeverity(@Nonnull ResultSeverityEnum theSeverity,@Nonnull String theTagSystem,@Nonnull String theTagCode) {
|
||||
public FinalizedRequireValidationRule tagOnSeverity(@Nonnull ResultSeverityEnum theSeverity, @Nonnull String theTagSystem, @Nonnull String theTagCode) {
|
||||
myRule.tagOnSeverity(theSeverity, theTagSystem, theTagCode);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.interceptor.validation;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
|
@ -58,7 +60,7 @@ class RequireValidationRule extends BaseTypedRule {
|
|||
|
||||
@Nonnull
|
||||
@Override
|
||||
public RuleEvaluation evaluate(@Nonnull IBaseResource theResource) {
|
||||
public RuleEvaluation evaluate(RequestDetails theRequestDetails, @Nonnull IBaseResource theResource) {
|
||||
|
||||
FhirValidator validator = getFhirContext().newValidator();
|
||||
validator.registerValidatorModule(myValidator);
|
||||
|
@ -83,6 +85,8 @@ class RequireValidationRule extends BaseTypedRule {
|
|||
|
||||
}
|
||||
|
||||
ValidationResultEnrichingInterceptor.addValidationResultToRequestDetails(theRequestDetails, outcome);
|
||||
|
||||
return RuleEvaluation.forSuccess(this);
|
||||
}
|
||||
|
||||
|
@ -104,6 +108,14 @@ class RequireValidationRule extends BaseTypedRule {
|
|||
myRejectOnSeverity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("resourceType", getResourceType())
|
||||
.append("rejectOnSeverity", myRejectOnSeverity)
|
||||
.append("tagOnSeverity", myTagOnSeverity)
|
||||
.toString();
|
||||
}
|
||||
|
||||
private static class TagOnSeverity {
|
||||
private final int mySeverity;
|
||||
|
@ -133,13 +145,4 @@ class RequireValidationRule extends BaseTypedRule {
|
|||
return ResultSeverityEnum.values()[mySeverity].name() + "/" + myTagSystem + "/" + myTagCode;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("resourceType", getResourceType())
|
||||
.append("rejectOnSeverity", myRejectOnSeverity)
|
||||
.append("tagOnSeverity", myTagOnSeverity)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.interceptor.validation;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
@ -47,7 +48,7 @@ class RuleDisallowProfile extends BaseTypedRule {
|
|||
|
||||
@Nonnull
|
||||
@Override
|
||||
public RuleEvaluation evaluate(@Nonnull IBaseResource theResource) {
|
||||
public RuleEvaluation evaluate(RequestDetails theRequestDetails, @Nonnull IBaseResource theResource) {
|
||||
for (IPrimitiveType<String> next : theResource.getMeta().getProfile()) {
|
||||
String nextUrl = next.getValueAsString();
|
||||
String nextUrlNormalized = UrlUtil.normalizeCanonicalUrlForComparison(nextUrl);
|
||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.interceptor.validation;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -39,7 +40,7 @@ class RuleRequireProfileDeclaration extends BaseTypedRule {
|
|||
|
||||
@Nonnull
|
||||
@Override
|
||||
public RuleEvaluation evaluate(@Nonnull IBaseResource theResource) {
|
||||
public RuleEvaluation evaluate(RequestDetails theRequestDetails, @Nonnull IBaseResource theResource) {
|
||||
Optional<String> matchingProfile = theResource
|
||||
.getMeta()
|
||||
.getProfile()
|
||||
|
|
|
@ -64,7 +64,9 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||
import ca.uhn.fhir.jpa.provider.r4.BaseJpaResourceProviderObservationR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.rp.r4.ObservationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package ca.uhn.fhir.jpa.interceptor.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.jpa.rp.r4.ObservationResourceProvider;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
public class RepositoryValidatingInterceptorHttpR4Test extends BaseJpaR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RepositoryValidatingInterceptorHttpR4Test.class);
|
||||
@Autowired
|
||||
protected ObservationResourceProvider myObservationResourceProvider;
|
||||
private RepositoryValidatingInterceptor myValInterceptor;
|
||||
@RegisterExtension
|
||||
protected RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(FhirVersionEnum.R4);
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myValInterceptor = new RepositoryValidatingInterceptor();
|
||||
myValInterceptor.setFhirContext(myFhirCtx);
|
||||
myInterceptorRegistry.registerInterceptor(myValInterceptor);
|
||||
|
||||
myRestfulServerExtension.getRestfulServer().registerProvider(myObservationResourceProvider);
|
||||
myRestfulServerExtension.getRestfulServer().getInterceptorService().registerInterceptor(new ValidationResultEnrichingInterceptor());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof RepositoryValidatingInterceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationOutcomeAddedToRequestResponse() {
|
||||
List<IRepositoryValidatingRule> rules = newRuleBuilder()
|
||||
.forResourcesOfType("Observation")
|
||||
.requireValidationToDeclaredProfiles()
|
||||
.withBestPracticeWarningLevel("WARNING")
|
||||
.build();
|
||||
myValInterceptor.setRules(rules);
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getCode().addCoding().setSystem("http://foo").setCode("123").setDisplay("help im a bug");
|
||||
obs.setStatus(Observation.ObservationStatus.AMENDED);
|
||||
|
||||
MethodOutcome outcome = myRestfulServerExtension
|
||||
.getFhirClient()
|
||||
.create()
|
||||
.resource(obs)
|
||||
.prefer(PreferReturnEnum.OPERATION_OUTCOME)
|
||||
.execute();
|
||||
|
||||
String operationOutcomeEncoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info("Outcome: {}", operationOutcomeEncoded);
|
||||
assertThat(operationOutcomeEncoded, containsString("All observations should have a subject"));
|
||||
|
||||
}
|
||||
|
||||
private RepositoryValidatingRuleBuilder newRuleBuilder() {
|
||||
return myApplicationContext.getBean(BaseConfig.REPOSITORY_VALIDATING_RULE_BUILDER, RepositoryValidatingRuleBuilder.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@ import org.hl7.fhir.r4.model.OperationOutcome;
|
|||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UrlType;
|
||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -44,6 +43,7 @@ public class RepositoryValidatingInterceptorR4Test extends BaseJpaR4Test {
|
|||
myValInterceptor = new RepositoryValidatingInterceptor();
|
||||
myValInterceptor.setFhirContext(myFhirCtx);
|
||||
myInterceptorRegistry.registerInterceptor(myValInterceptor);
|
||||
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
|
@ -45,7 +45,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* request with an {@link UnprocessableEntityException HTTP 422 Unprocessable Entity}.
|
||||
*/
|
||||
@Interceptor
|
||||
public abstract class BaseValidatingInterceptor<T> {
|
||||
public abstract class BaseValidatingInterceptor<T> extends ValidationResultEnrichingInterceptor {
|
||||
|
||||
/**
|
||||
* Default value:<br/>
|
||||
|
|
|
@ -31,8 +31,6 @@ import ca.uhn.fhir.rest.server.method.ResourceParameter;
|
|||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -51,11 +49,6 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
|
|||
* X-HAPI-Request-Validation
|
||||
*/
|
||||
public static final String DEFAULT_RESPONSE_HEADER_NAME = "X-FHIR-Request-Validation";
|
||||
/**
|
||||
* A {@link RequestDetails#getUserData() user data} entry will be created with this
|
||||
* key which contains the {@link ValidationResult} from validating the request.
|
||||
*/
|
||||
public static final String REQUEST_VALIDATION_RESULT = RequestValidatingInterceptor.class.getName() + "_REQUEST_VALIDATION_RESULT";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RequestValidatingInterceptor.class);
|
||||
private boolean myAddValidationResultsToResponseOperationOutcome = true;
|
||||
|
||||
|
@ -82,8 +75,9 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
|
|||
|
||||
ValidationResult validationResult = validate(requestText, theRequestDetails);
|
||||
|
||||
// The JPA server will use this
|
||||
theRequestDetails.getUserData().put(REQUEST_VALIDATION_RESULT, validationResult);
|
||||
if (myAddValidationResultsToResponseOperationOutcome) {
|
||||
addValidationResultToRequestDetails(theRequestDetails, validationResult);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -110,25 +104,6 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
|
|||
myAddValidationResultsToResponseOperationOutcome = theAddValidationResultsToResponseOperationOutcome;
|
||||
}
|
||||
|
||||
@Hook(Pointcut.SERVER_OUTGOING_RESPONSE)
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||
if (myAddValidationResultsToResponseOperationOutcome) {
|
||||
if (theResponseObject instanceof IBaseOperationOutcome) {
|
||||
IBaseOperationOutcome oo = (IBaseOperationOutcome) theResponseObject;
|
||||
|
||||
if (theRequestDetails != null) {
|
||||
ValidationResult validationResult = (ValidationResult) theRequestDetails.getUserData().get(RequestValidatingInterceptor.REQUEST_VALIDATION_RESULT);
|
||||
if (validationResult != null) {
|
||||
validationResult.populateOperationOutcome(oo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String provideDefaultResponseHeaderName() {
|
||||
return DEFAULT_RESPONSE_HEADER_NAME;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Interceptor
|
||||
public class ValidationResultEnrichingInterceptor {
|
||||
|
||||
/**
|
||||
* A {@link RequestDetails#getUserData() user data} entry will be created with this
|
||||
* key which contains the {@link ValidationResult} from validating the request.
|
||||
*/
|
||||
public static final String REQUEST_VALIDATION_RESULT = ValidationResultEnrichingInterceptor.class.getName() + "_REQUEST_VALIDATION_RESULT";
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Hook(Pointcut.SERVER_OUTGOING_RESPONSE)
|
||||
public boolean addValidationResultsToOperationOutcome(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||
if (theResponseObject instanceof IBaseOperationOutcome) {
|
||||
IBaseOperationOutcome oo = (IBaseOperationOutcome) theResponseObject;
|
||||
|
||||
if (theRequestDetails != null) {
|
||||
List<ValidationResult> validationResult = (List<ValidationResult>) theRequestDetails.getUserData().remove(REQUEST_VALIDATION_RESULT);
|
||||
if (validationResult != null) {
|
||||
for (ValidationResult next : validationResult) {
|
||||
next.populateOperationOutcome(oo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void addValidationResultToRequestDetails(@Nullable RequestDetails theRequestDetails, @Nonnull ValidationResult theValidationResult) {
|
||||
if (theRequestDetails != null) {
|
||||
List<ValidationResult> results = (List<ValidationResult>) theRequestDetails.getUserData().computeIfAbsent(REQUEST_VALIDATION_RESULT, t -> new ArrayList<>(2));
|
||||
results.add(theValidationResult);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue