Improving validation documentation

This commit is contained in:
jamesagnew 2016-01-05 07:20:54 -05:00
parent 685bd9345b
commit 59da230702
6 changed files with 177 additions and 73 deletions

View File

@ -29,12 +29,43 @@ import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.validation.FhirValidator; 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.SingleValidationMessage;
import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.ValidationResult;
import ca.uhn.fhir.validation.schematron.SchematronBaseValidator;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class ValidatorExamples { 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 = "<Patient.....>";
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 // START SNIPPET: serverValidation
public class MyRestfulServer extends RestfulServer { public class MyRestfulServer extends RestfulServer {
@ -96,6 +127,15 @@ public class ValidatorExamples {
// Request a validator and apply it // Request a validator and apply it
FhirValidator val = ctx.newValidator(); 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); ValidationResult result = val.validateWithResult(p);
if (result.isSuccessful()) { if (result.isSuccessful()) {

View File

@ -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 <a href="http://jamesagnew.github.io/hapi-fhir/doc_validation.html">Validation</a>
* for a list of available modules. You may also create your own.
*/ */
public interface IValidatorModule { 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<IBaseResource> theCtx); void validateResource(IValidationContext<IBaseResource> 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<Bundle> theContext); void validateBundle(IValidationContext<Bundle> theContext);
} }

View File

@ -75,7 +75,7 @@ public class SchemaBaseValidator implements IValidatorModule {
private Map<String, Schema> myKeyToSchema = new HashMap<String, Schema>(); private Map<String, Schema> myKeyToSchema = new HashMap<String, Schema>();
private FhirContext myCtx; private FhirContext myCtx;
SchemaBaseValidator(FhirContext theContext) { public SchemaBaseValidator(FhirContext theContext) {
myCtx = theContext; myCtx = theContext;
} }

View File

@ -11,8 +11,10 @@ import static org.mockito.Mockito.when;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu21.hapi.validation.DefaultProfileValidationSupport; import org.hl7.fhir.dstu21.hapi.validation.DefaultProfileValidationSupport;
@ -51,9 +53,11 @@ public class FhirInstanceValidatorTest {
private FhirValidator myVal; private FhirValidator myVal;
private ArrayList<String> myValidConcepts; private ArrayList<String> myValidConcepts;
private Set<String> myValidSystems = new HashSet<String>();
private Map<String, ValueSetExpansionComponent> mySupportedCodeSystemsForExpansion; private Map<String, ValueSetExpansionComponent> mySupportedCodeSystemsForExpansion;
private void addValidConcept(String theSystem, String theCode) { private void addValidConcept(String theSystem, String theCode) {
myValidSystems.add(theSystem);
myValidConcepts.add(theSystem + "___" + theCode); myValidConcepts.add(theSystem + "___" + theCode);
} }
@ -87,8 +91,8 @@ public class FhirInstanceValidatorTest {
when(myMockSupport.isCodeSystemSupported(any(FhirContext.class), any(String.class))).thenAnswer(new Answer<Boolean>() { when(myMockSupport.isCodeSystemSupported(any(FhirContext.class), any(String.class))).thenAnswer(new Answer<Boolean>() {
@Override @Override
public Boolean answer(InvocationOnMock theInvocation) throws Throwable { public Boolean answer(InvocationOnMock theInvocation) throws Throwable {
boolean retVal = mySupportedCodeSystemsForExpansion.containsKey(theInvocation.getArguments()[0]); boolean retVal = myValidSystems.contains(theInvocation.getArguments()[1]);
ourLog.info("isCodeSystemSupported({}) : {}", new Object[] { theInvocation.getArguments()[0], retVal }); ourLog.info("isCodeSystemSupported({}) : {}", new Object[] { theInvocation.getArguments()[1], retVal });
return retVal; return retVal;
} }
}); });
@ -289,6 +293,27 @@ public class FhirInstanceValidatorTest {
assertThat(errors.toString(), containsString("")); 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<SingleValidationMessage> errors = logResultsAndReturnAll(output);
assertThat(errors.toString(), containsString("information"));
assertThat(errors.toString(), containsString("Unknown code: http://loinc.org / 12345"));
assertEquals(1, errors.size());
}
@Test @Test
public void testValidateResourceWithDefaultValueset() { public void testValidateResourceWithDefaultValueset() {
Observation input = new Observation(); Observation input = new Observation();

View File

@ -273,7 +273,9 @@
</p> </p>
<p> <p>
See the <a href="./doc_validation.html">Validation Page</a> for information on See the <a href="./doc_validation.html">Validation Page</a> for information on
available IValidationModule validation modules. Any of the <b>Resource Validators</b> available
<a href="./apidocs/ca/uhn/fhir/validation/IValidatorModule.html">IValidatorModule</a>
validation modules. Any of the <b>Resource Validators</b>
listed on that page can be enabled in these interceptors (note that the <b>Parser Validators</b> listed on that page can be enabled in these interceptors (note that the <b>Parser Validators</b>
can not). can not).
</p> </p>

View File

@ -14,6 +14,12 @@
sections below. sections below.
</p> </p>
<ul> <ul>
<li>
<b>Resource Validation</b>
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.
</li>
<li> <li>
<b>Parser Validation</b> <b>Parser Validation</b>
is validation at runtime during the parsing 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 that no data is being lost during parsing, but is less comprehensive than resource validation
against raw text data. against raw text data.
</li> </li>
<li>
<b>Resource Validation</b>
is validation of the raw or parsed resource against
the official FHIR validation rules (e.g. Schema/Schematron/Profile/StructureDefinition/ValueSet).
</li>
</ul> </ul>
</section> </section>
<section name="Parser Validation">
<p>
Parser validation is controlled by calling
<code>setParserErrorHandler(IParserErrorHandler)</code>
on
either the FhirContext or on individual parser instances. This method
takes an
<code>IParserErrorHandler</code>
, which is a callback that
will be invoked any time a parse issue is detected.
</p>
<p>
There are two implementations of
<code>IParserErrorHandler</code>
worth
mentioning. You can also supply your own implementation if you want.
</p>
<ul>
<li>
<a href="./apidocs/ca/uhn/fhir/parser/LenientErrorHandler.html">LenientErrorHandler</a>
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.
</li>
<li>
<a href="./apidocs/ca/uhn/fhir/parser/StrictErrorHandler.html">StrictErrorHandler</a>
throws a
<code>DataFormatException</code>
if any errors are detected.
</li>
</ul>
<p>
The following example shows how to configure a parser to use strict validation.
</p>
<macro name="snippet">
<param name="id" value="parserValidation" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
<p>
You can also configure the error handler at the FhirContext level, which is useful
for clients.
</p>
<macro name="snippet">
<param name="id" value="clientValidation" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
<p>
FhirContext level validators can also be useful on servers.
</p>
<macro name="snippet">
<param name="id" value="serverValidation" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
</section>
<!-- RESOURCE VALIDATION --> <!-- RESOURCE VALIDATION -->
<section name="Resource Validation (Schema/Schematron)"> <section name="Resource Validation">
<p> <p>
HAPI provides a built-in and configurable mechanism for validating resources. HAPI provides a built-in and configurable mechanism for validating resources.
This mechanism is called the This mechanism is called the
<i>Resource Validator</i> <i>Resource Validator.</i>
.
</p> </p>
<p>
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.
</p>
<p>
The validator can be manually invoked at any time by creating a
validator and configuring it with one or more
<a href="./apidocs/ca/uhn/fhir/validation/IValidatorModule.html">IValidatorModule</a>
instances.
</p>
<macro name="snippet">
<param name="id" value="validationIntro" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
</section>
<section name="Resource Validation Module: Schema/Schematron">
<p> <p>
FHIR resource definitions are distributed with a set of XML schema files (XSD) FHIR resource definitions are distributed with a set of XML schema files (XSD)
@ -262,6 +223,66 @@
</section> </section>
<section name="Parser Validation">
<p>
Parser validation is controlled by calling
<code>setParserErrorHandler(IParserErrorHandler)</code>
on
either the FhirContext or on individual parser instances. This method
takes an
<code>IParserErrorHandler</code>
, which is a callback that
will be invoked any time a parse issue is detected.
</p>
<p>
There are two implementations of
<code>IParserErrorHandler</code>
worth
mentioning. You can also supply your own implementation if you want.
</p>
<ul>
<li>
<a href="./apidocs/ca/uhn/fhir/parser/LenientErrorHandler.html">LenientErrorHandler</a>
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.
</li>
<li>
<a href="./apidocs/ca/uhn/fhir/parser/StrictErrorHandler.html">StrictErrorHandler</a>
throws a
<code>DataFormatException</code>
if any errors are detected.
</li>
</ul>
<p>
The following example shows how to configure a parser to use strict validation.
</p>
<macro name="snippet">
<param name="id" value="parserValidation" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
<p>
You can also configure the error handler at the FhirContext level, which is useful
for clients.
</p>
<macro name="snippet">
<param name="id" value="clientValidation" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
<p>
FhirContext level validators can also be useful on servers.
</p>
<macro name="snippet">
<param name="id" value="serverValidation" />
<param name="file" value="examples/src/main/java/example/ValidatorExamples.java" />
</macro>
</section>
</body> </body>
</document> </document>