Improving validation documentation
This commit is contained in:
parent
685bd9345b
commit
59da230702
|
@ -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()) {
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue