Add validation options to RepositoryValidingInterceptor (#2294)

* Add repository validating interceptor outcome to response

* Add a doc

* Add a test and some docs

* Rework validation
This commit is contained in:
James Agnew 2021-01-17 16:40:32 -05:00 committed by GitHub
parent 391a6d1a3f
commit 66e1a51e01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 3 deletions

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import javax.annotation.Nonnull;
import java.util.List; import java.util.List;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -107,11 +108,44 @@ public class RepositoryValidatingInterceptorExamples {
ruleBuilder ruleBuilder
.forResourcesOfType("Patient") .forResourcesOfType("Patient")
.requireValidationToDeclaredProfiles() .requireValidationToDeclaredProfiles()
.dontReject() .neverReject()
.tagOnSeverity(ResultSeverityEnum.ERROR, "http://example.com", "validation-failure"); .tagOnSeverity(ResultSeverityEnum.ERROR, "http://example.com", "validation-failure");
//END SNIPPET: requireValidationToDeclaredProfilesTagOnFailure //END SNIPPET: requireValidationToDeclaredProfilesTagOnFailure
} }
public void requireValidationToDeclaredProfilesAdditionalOptions() {
RepositoryValidatingRuleBuilder ruleBuilder = myAppCtx.getBean(RepositoryValidatingRuleBuilder.class);
//START SNIPPET: requireValidationToDeclaredProfilesAdditionalOptions
ruleBuilder
.forResourcesOfType("Patient")
.requireValidationToDeclaredProfiles()
// Configure the validator to never reject extensions
.allowAnyExtensions()
// Configure the validator to not perform terminology validation
.disableTerminologyChecks()
// Configure the validator to raise an error if a resource being
// validated declares a profile, and the StructureDefinition for
// this profile can not be found.
.errorOnUnknownProfiles()
// Configure the validator to suppress the information-level
// message that is added to the validation result if a profile
// StructureDefinition does not declare a binding for a coded
// field.
.suppressNoBindingMessage()
// Configure the validator to suppress the warning-level message
// that is added when validating a code that can't be found in a
// ValueSet that has an extensible binding.
.suppressWarningForExtensibleValueSetValidation();
//END SNIPPET: requireValidationToDeclaredProfilesAdditionalOptions
}
public void disallowProfiles() { public void disallowProfiles() {
RepositoryValidatingRuleBuilder ruleBuilder = myAppCtx.getBean(RepositoryValidatingRuleBuilder.class); RepositoryValidatingRuleBuilder ruleBuilder = myAppCtx.getBean(RepositoryValidatingRuleBuilder.class);

View File

@ -95,6 +95,13 @@ By default, resource updates/changes resulting in failing validation will cause
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RepositoryValidatingInterceptorExamples.java|requireValidationToDeclaredProfilesTagOnFailure}} {{snippet:classpath:/ca/uhn/hapi/fhir/docs/RepositoryValidatingInterceptorExamples.java|requireValidationToDeclaredProfilesTagOnFailure}}
``` ```
## Configuring the Validator
The following snippet shows a number of additional optional settings that can be chained onto the validation rule.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RepositoryValidatingInterceptorExamples.java|requireValidationToDeclaredProfilesAdditionalOptions}}
```
# Rules: Disallow Specific Profiles # Rules: Disallow Specific Profiles

View File

@ -226,7 +226,7 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
* Specifies that the resource should not be rejected from storage even if it does not pass validation. * Specifies that the resource should not be rejected from storage even if it does not pass validation.
*/ */
@Nonnull @Nonnull
public FinalizedRequireValidationRule dontReject() { public FinalizedRequireValidationRule neverReject() {
myRule.dontReject(); myRule.dontReject();
return this; return this;
} }
@ -292,6 +292,57 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
return this; return this;
} }
/**
* Configure the validator to never reject extensions
*/
@Nonnull
public FinalizedRequireValidationRule allowAnyExtensions() {
myRule.getValidator().setAnyExtensionsAllowed(true);
return this;
}
/**
* Configure the validator to not perform terminology validation
*/
@Nonnull
public FinalizedRequireValidationRule disableTerminologyChecks() {
myRule.getValidator().setNoTerminologyChecks(true);
return this;
}
/**
* Configure the validator to raise an error if a resource being validated
* declares a profile, and the StructureDefinition for this profile
* can not be found.
*/
@Nonnull
public FinalizedRequireValidationRule errorOnUnknownProfiles() {
myRule.getValidator().setErrorForUnknownProfiles(true);
return this;
}
/**
* Configure the validator to suppress the information-level message that
* is added to the validation result if a profile StructureDefinition does
* not declare a binding for a coded field.
*/
@Nonnull
public FinalizedRequireValidationRule suppressNoBindingMessage() {
myRule.getValidator().setNoBindingMsgSuppressed(true);
return this;
}
/**
* Configure the validator to suppress the warning-level message that
* is added when validating a code that can't be found in an ValueSet that
* has an extensible binding.
*/
@Nonnull
public FinalizedRequireValidationRule suppressWarningForExtensibleValueSetValidation() {
myRule.getValidator().setNoExtensibleWarnings(true);
return this;
}
} }
} }

View File

@ -117,6 +117,14 @@ class RequireValidationRule extends BaseTypedRule {
.toString(); .toString();
} }
public FhirInstanceValidator getValidator() {
return myValidator;
}
public void setAllowAnyExtensions() {
myValidator.setAnyExtensionsAllowed(true);
}
private static class TagOnSeverity { private static class TagOnSeverity {
private final int mySeverity; private final int mySeverity;
private final String myTagSystem; private final String myTagSystem;

View File

@ -264,13 +264,40 @@ public class RepositoryValidatingInterceptorR4Test extends BaseJpaR4Test {
} }
} }
@Test
public void testRequireValidation_AdditionalOptions() {
List<IRepositoryValidatingRule> rules = newRuleBuilder()
.forResourcesOfType("Observation")
.requireValidationToDeclaredProfiles()
.withBestPracticeWarningLevel("IGNORE")
.allowAnyExtensions()
.disableTerminologyChecks()
.errorOnUnknownProfiles()
.suppressNoBindingMessage()
.suppressWarningForExtensibleValueSetValidation()
.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);
try {
IIdType id = myObservationDao.create(obs).getId();
assertEquals("1", id.getVersionIdPart());
} catch (PreconditionFailedException e) {
// should not happen
fail(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
}
}
@Test @Test
public void testRequireValidation_FailNoRejectAndTag() { public void testRequireValidation_FailNoRejectAndTag() {
List<IRepositoryValidatingRule> rules = newRuleBuilder() List<IRepositoryValidatingRule> rules = newRuleBuilder()
.forResourcesOfType("Observation") .forResourcesOfType("Observation")
.requireValidationToDeclaredProfiles() .requireValidationToDeclaredProfiles()
.withBestPracticeWarningLevel("IGNORE") .withBestPracticeWarningLevel("IGNORE")
.dontReject() .neverReject()
.tagOnSeverity(ResultSeverityEnum.ERROR, "http://foo", "validation-error") .tagOnSeverity(ResultSeverityEnum.ERROR, "http://foo", "validation-error")
.build(); .build();
myValInterceptor.setRules(rules); myValInterceptor.setRules(rules);