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.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 = "<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
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()) {

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 {
/**
* 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);
/**
* 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);
}

View File

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

View File

@ -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<String> myValidConcepts;
private Set<String> myValidSystems = new HashSet<String>();
private Map<String, ValueSetExpansionComponent> 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<Boolean>() {
@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<SingleValidationMessage> 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();

View File

@ -273,7 +273,9 @@
</p>
<p>
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>
can not).
</p>

View File

@ -14,6 +14,12 @@
sections below.
</p>
<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>
<b>Parser Validation</b>
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.
</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>
</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 -->
<section name="Resource Validation (Schema/Schematron)">
<section name="Resource Validation">
<p>
HAPI provides a built-in and configurable mechanism for validating resources.
This mechanism is called the
<i>Resource Validator</i>
.
<i>Resource Validator.</i>
</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>
FHIR resource definitions are distributed with a set of XML schema files (XSD)
@ -262,6 +223,66 @@
</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>
</document>