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:
parent
391a6d1a3f
commit
66e1a51e01
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue