From 59da23070215f5ecf137ce711b55625c1064a261 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 5 Jan 2016 07:20:54 -0500 Subject: [PATCH] Improving validation documentation --- .../main/java/example/ValidatorExamples.java | 40 +++++ .../uhn/fhir/validation/IValidatorModule.java | 18 +- .../fhir/validation/SchemaBaseValidator.java | 2 +- .../validation/FhirInstanceValidatorTest.java | 29 +++- src/site/xdoc/doc_rest_server_interceptor.xml | 4 +- src/site/xdoc/doc_validation.xml | 157 ++++++++++-------- 6 files changed, 177 insertions(+), 73 deletions(-) diff --git a/examples/src/main/java/example/ValidatorExamples.java b/examples/src/main/java/example/ValidatorExamples.java index 49e368e410f..a38dcaf7a5b 100644 --- a/examples/src/main/java/example/ValidatorExamples.java +++ b/examples/src/main/java/example/ValidatorExamples.java @@ -29,12 +29,43 @@ import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.fhir.validation.IValidatorModule; +import ca.uhn.fhir.validation.SchemaBaseValidator; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; +import ca.uhn.fhir.validation.schematron.SchematronBaseValidator; @SuppressWarnings("serial") public class ValidatorExamples { + public void validationIntro() { + // START SNIPPET: validationIntro + FhirContext ctx = FhirContext.forDstu2(); + + // Ask the context for a validator + FhirValidator validator = ctx.newValidator(); + + // Create some modules and register them + IValidatorModule module1 = new SchemaBaseValidator(ctx); + validator.registerValidatorModule(module1); + IValidatorModule module2 = new SchematronBaseValidator(ctx); + validator.registerValidatorModule(module2); + + // Pass a resource in to be validated. The resource can + // be an IBaseResource instance, or can be a raw String + // containing a serialized resource as text. + Patient resource = new Patient(); + ValidationResult result = validator.validateWithResult(resource); + String resourceText = ""; + ValidationResult result2 = validator.validateWithResult(resourceText); + + // The result object now contains the validation results + for (SingleValidationMessage next : result.getMessages()) { + System.out.println(next.getLocationString() + " " + next.getMessage()); + } + // END SNIPPET: validationIntro + } + // START SNIPPET: serverValidation public class MyRestfulServer extends RestfulServer { @@ -96,6 +127,15 @@ public class ValidatorExamples { // Request a validator and apply it FhirValidator val = ctx.newValidator(); + // Create the Schema/Schematron modules and register them. Note that + // you might want to consider keeping these modules around as long-term + // objects: they parse and then store schemas, which can be an expensive + // operation. + IValidatorModule module1 = new SchemaBaseValidator(ctx); + IValidatorModule module2 = new SchematronBaseValidator(ctx); + val.registerValidatorModule(module1); + val.registerValidatorModule(module2); + ValidationResult result = val.validateWithResult(p); if (result.isSuccessful()) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidatorModule.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidatorModule.java index bf3c84bf19f..2872920867e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidatorModule.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidatorModule.java @@ -26,12 +26,28 @@ import ca.uhn.fhir.model.api.Bundle; */ /** - * Registers + * An individual validation module, which applies validation rules against + * resources and adds failure/informational messages as it goes. + * + * See Validation + * for a list of available modules. You may also create your own. */ public interface IValidatorModule { + /** + * Validate the actual resource. + * + * The {@link IValidationContext} can be used to access the resource being validated, + * and is populated with the results. + */ void validateResource(IValidationContext theCtx); + /** + * This method applies only to DSTU1 Atom Bundles. All other validation will pass through + * {@link #validateResource(IValidationContext)} inclusing DSTU2+ Bundle resources. If you + * will not be validating DSTU1 Bundles, you may implement this method as + * a NO-OP. + */ void validateBundle(IValidationContext theContext); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java index cebd80a4693..2a720404fda 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/SchemaBaseValidator.java @@ -75,7 +75,7 @@ public class SchemaBaseValidator implements IValidatorModule { private Map myKeyToSchema = new HashMap(); private FhirContext myCtx; - SchemaBaseValidator(FhirContext theContext) { + public SchemaBaseValidator(FhirContext theContext) { myCtx = theContext; } diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java index e95ee60fd8e..9f14076b0b8 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/validation/FhirInstanceValidatorTest.java @@ -11,8 +11,10 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.io.IOUtils; import org.hl7.fhir.dstu21.hapi.validation.DefaultProfileValidationSupport; @@ -51,9 +53,11 @@ public class FhirInstanceValidatorTest { private FhirValidator myVal; private ArrayList myValidConcepts; + private Set myValidSystems = new HashSet(); private Map mySupportedCodeSystemsForExpansion; private void addValidConcept(String theSystem, String theCode) { + myValidSystems.add(theSystem); myValidConcepts.add(theSystem + "___" + theCode); } @@ -87,8 +91,8 @@ public class FhirInstanceValidatorTest { when(myMockSupport.isCodeSystemSupported(any(FhirContext.class), any(String.class))).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock theInvocation) throws Throwable { - boolean retVal = mySupportedCodeSystemsForExpansion.containsKey(theInvocation.getArguments()[0]); - ourLog.info("isCodeSystemSupported({}) : {}", new Object[] { theInvocation.getArguments()[0], retVal }); + boolean retVal = myValidSystems.contains(theInvocation.getArguments()[1]); + ourLog.info("isCodeSystemSupported({}) : {}", new Object[] { theInvocation.getArguments()[1], retVal }); return retVal; } }); @@ -289,6 +293,27 @@ public class FhirInstanceValidatorTest { assertThat(errors.toString(), containsString("")); } + @Test + public void testValidateResourceContainingLoincCode() { + addValidConcept("http://loinc.org", "1234567"); + + Observation input = new Observation(); +// input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation"); + + input.addIdentifier().setSystem("http://acme").setValue("12345"); + input.getEncounter().setReference("http://foo.com/Encounter/9"); + input.setStatus(ObservationStatus.FINAL); + input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345"); + + myInstanceVal.setValidationSupport(myMockSupport); + ValidationResult output = myVal.validateWithResult(input); + List errors = logResultsAndReturnAll(output); + + assertThat(errors.toString(), containsString("information")); + assertThat(errors.toString(), containsString("Unknown code: http://loinc.org / 12345")); + assertEquals(1, errors.size()); + } + @Test public void testValidateResourceWithDefaultValueset() { Observation input = new Observation(); diff --git a/src/site/xdoc/doc_rest_server_interceptor.xml b/src/site/xdoc/doc_rest_server_interceptor.xml index 37d0bf15aa3..d451941af40 100644 --- a/src/site/xdoc/doc_rest_server_interceptor.xml +++ b/src/site/xdoc/doc_rest_server_interceptor.xml @@ -273,7 +273,9 @@

See the Validation Page for information on - available IValidationModule validation modules. Any of the Resource Validators + available + IValidatorModule + validation modules. Any of the Resource Validators listed on that page can be enabled in these interceptors (note that the Parser Validators can not).

diff --git a/src/site/xdoc/doc_validation.xml b/src/site/xdoc/doc_validation.xml index 7de39533ba3..8f7c50503a4 100644 --- a/src/site/xdoc/doc_validation.xml +++ b/src/site/xdoc/doc_validation.xml @@ -14,6 +14,12 @@ sections below.

    +
  • + Resource Validation + is validation of the raw or parsed resource against + the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet) + as well as against custom profiles which have been developed. +
  • Parser Validation is validation at runtime during the parsing @@ -25,85 +31,40 @@ that no data is being lost during parsing, but is less comprehensive than resource validation against raw text data.
  • -
  • - Resource Validation - is validation of the raw or parsed resource against - the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet). -
-
- -

- Parser validation is controlled by calling - setParserErrorHandler(IParserErrorHandler) - on - either the FhirContext or on individual parser instances. This method - takes an - IParserErrorHandler - , which is a callback that - will be invoked any time a parse issue is detected. -

-

- There are two implementations of - IParserErrorHandler - worth - mentioning. You can also supply your own implementation if you want. -

-
    -
  • - LenientErrorHandler - logs any errors but does not abort parsing. By default this handler is used, and it - logs errors at "warning" level. It can also be configured to silently - ignore issues. -
  • -
  • - StrictErrorHandler - throws a - DataFormatException - if any errors are detected. -
  • -
- -

- The following example shows how to configure a parser to use strict validation. -

- - - - - -

- You can also configure the error handler at the FhirContext level, which is useful - for clients. -

- - - - - -

- FhirContext level validators can also be useful on servers. -

- - - - - -
- -
+

HAPI provides a built-in and configurable mechanism for validating resources. This mechanism is called the - Resource Validator - . + Resource Validator.

+

+ The resource validator is an extendible and modular system, and you + can configure it in a number of ways in order to get the specific + type of validation you want to achieve. +

+ +

+ The validator can be manually invoked at any time by creating a + validator and configuring it with one or more + IValidatorModule + instances. +

+ + + + + + +
+ +

FHIR resource definitions are distributed with a set of XML schema files (XSD) @@ -262,6 +223,66 @@

+
+ +

+ Parser validation is controlled by calling + setParserErrorHandler(IParserErrorHandler) + on + either the FhirContext or on individual parser instances. This method + takes an + IParserErrorHandler + , which is a callback that + will be invoked any time a parse issue is detected. +

+

+ There are two implementations of + IParserErrorHandler + worth + mentioning. You can also supply your own implementation if you want. +

+
    +
  • + LenientErrorHandler + logs any errors but does not abort parsing. By default this handler is used, and it + logs errors at "warning" level. It can also be configured to silently + ignore issues. +
  • +
  • + StrictErrorHandler + throws a + DataFormatException + if any errors are detected. +
  • +
+ +

+ The following example shows how to configure a parser to use strict validation. +

+ + + + + +

+ You can also configure the error handler at the FhirContext level, which is useful + for clients. +

+ + + + + +

+ FhirContext level validators can also be useful on servers. +

+ + + + + +
+