Add configuration property to DSTU3 FhirInstanceValidator to allow client code to change unknown extension handling behaviour.
This commit is contained in:
parent
9c595e18f9
commit
bb9cd7c198
|
@ -8,21 +8,23 @@ import javax.servlet.ServletException;
|
|||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
|
@ -39,7 +41,7 @@ public class ValidatorExamples {
|
|||
|
||||
public void validationIntro() {
|
||||
// START SNIPPET: validationIntro
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
// Ask the context for a validator
|
||||
FhirValidator validator = ctx.newValidator();
|
||||
|
@ -74,7 +76,7 @@ public class ValidatorExamples {
|
|||
|
||||
// Create a context, set the error handler and instruct
|
||||
// the server to use it
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
ctx.setParserErrorHandler(new StrictErrorHandler());
|
||||
setFhirContext(ctx);
|
||||
}
|
||||
|
@ -85,19 +87,19 @@ public class ValidatorExamples {
|
|||
@SuppressWarnings("unused")
|
||||
public void enableValidation() {
|
||||
// START SNIPPET: clientValidation
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
ctx.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
// This client will have strict parser validation enabled
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu2");
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3");
|
||||
// END SNIPPET: clientValidation
|
||||
|
||||
}
|
||||
|
||||
public void parserValidation() {
|
||||
// START SNIPPET: parserValidation
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
// Create a parser and configure it to use the strict error handler
|
||||
IParser parser = ctx.newXmlParser();
|
||||
|
@ -114,13 +116,13 @@ public class ValidatorExamples {
|
|||
public void validateResource() {
|
||||
// START SNIPPET: basicValidation
|
||||
// As always, you need a context
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
// Create and populate a new patient object
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("Smith").addGiven("John").addGiven("Q");
|
||||
p.addName().setFamily("Smith").addGiven("John").addGiven("Q");
|
||||
p.addIdentifier().setSystem("urn:foo:identifiers").setValue("12345");
|
||||
p.addTelecom().setSystem(ContactPointSystemEnum.PHONE).setValue("416 123-4567");
|
||||
p.addTelecom().setSystem(ContactPointSystem.PHONE).setValue("416 123-4567");
|
||||
|
||||
// Request a validator and apply it
|
||||
FhirValidator val = ctx.newValidator();
|
||||
|
@ -168,20 +170,27 @@ public class ValidatorExamples {
|
|||
|
||||
private static void instanceValidator() throws Exception {
|
||||
// START SNIPPET: instanceValidator
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
// Create a FhirInstanceValidator and register it to a validator
|
||||
FhirValidator validator = ctx.newValidator();
|
||||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator();
|
||||
validator.registerValidatorModule(instanceValidator);
|
||||
|
||||
/*
|
||||
* If you want, you can configure settings on the validator to adjust
|
||||
* its behaviour during validation
|
||||
*/
|
||||
instanceValidator.setAnyExtensionsAllowed(true);
|
||||
|
||||
|
||||
/*
|
||||
* Let's create a resource to validate. This Observation has some fields
|
||||
* populated, but it is missing Observation.status, which is mandatory.
|
||||
*/
|
||||
Observation obs = new Observation();
|
||||
obs.getCode().addCoding().setSystem("http://loinc.org").setCode("12345-6");
|
||||
obs.setValue(new StringDt("This is a value"));
|
||||
obs.setValue(new StringType("This is a value"));
|
||||
|
||||
// Validate
|
||||
ValidationResult result = validator.validateWithResult(obs);
|
||||
|
@ -205,7 +214,7 @@ public class ValidatorExamples {
|
|||
|
||||
private static void instanceValidatorCustom() throws Exception {
|
||||
// START SNIPPET: instanceValidatorCustom
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
// Create a FhirInstanceValidator and register it to a validator
|
||||
FhirValidator validator = ctx.newValidator();
|
||||
|
@ -213,36 +222,48 @@ public class ValidatorExamples {
|
|||
validator.registerValidatorModule(instanceValidator);
|
||||
|
||||
IValidationSupport valSupport = new IValidationSupport() {
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
// TODO: Implement
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
|
||||
// TODO: Implement
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent expandValueSet(FhirContext theContext, org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent theInclude) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
|
||||
// TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
||||
// TODO: implement
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -261,7 +282,7 @@ public class ValidatorExamples {
|
|||
@SuppressWarnings("unused")
|
||||
private static void validateFiles() throws Exception {
|
||||
// START SNIPPET: validateFiles
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
// Create a validator and configure it
|
||||
FhirValidator validator = ctx.newValidator();
|
||||
|
|
|
@ -130,6 +130,16 @@
|
|||
<artifactId>junit</artifactId> <groupId>junit</groupId> </exclusion> <exclusion> <artifactId>jdom</artifactId> <groupId>org.jdom</groupId> </exclusion> <exclusion> <artifactId>gson</artifactId> <groupId>com.google.code.gson</groupId>
|
||||
</exclusion> </exclusions> </dependency> -->
|
||||
|
||||
<!--
|
||||
For some reason JavaDoc crashed during site generation unless we have this dependency
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
<artifactId>javax.interceptor-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- Test Database -->
|
||||
<dependency>
|
||||
|
|
|
@ -1083,8 +1083,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
*
|
||||
* @param theEntity
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theTag
|
||||
* The tag
|
||||
* @param theResource
|
||||
* The resource being persisted
|
||||
*/
|
||||
protected void postPersist(ResourceTable theEntity, T theResource) {
|
||||
// nothing
|
||||
|
|
|
@ -163,7 +163,6 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
* @param theId
|
||||
* @param theRequestDetails
|
||||
* TODO
|
||||
* @return
|
||||
* @throws ResourceNotFoundException
|
||||
* If the ID is not known to the server
|
||||
*/
|
||||
|
|
|
@ -37,216 +37,235 @@ import ca.uhn.fhir.validation.IValidatorModule;
|
|||
|
||||
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
|
||||
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
|
||||
*/
|
||||
public FhirInstanceValidator() {
|
||||
this(new DefaultProfileValidationSupport());
|
||||
}
|
||||
private boolean myAnyExtensionsAllowed = true;
|
||||
private BestPracticeWarningLevel myBestPracticeWarningLevel;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
private StructureDefinition myStructureDefintion;
|
||||
private IValidationSupport myValidationSupport;
|
||||
|
||||
/**
|
||||
* Constructor which uses the given validation support
|
||||
*
|
||||
* @param theValidationSupport
|
||||
* The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
|
||||
*/
|
||||
public FhirInstanceValidator() {
|
||||
this(new DefaultProfileValidationSupport());
|
||||
}
|
||||
|
||||
private String determineResourceName(Document theDocument) {
|
||||
Element root = null;
|
||||
/**
|
||||
* Constructor which uses the given validation support
|
||||
*
|
||||
* @param theValidationSupport
|
||||
* The validation support
|
||||
*/
|
||||
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
root = (Element) list.item(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
root = theDocument.getDocumentElement();
|
||||
return root.getLocalName();
|
||||
}
|
||||
private String determineResourceName(Document theDocument) {
|
||||
Element root = null;
|
||||
|
||||
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
|
||||
String sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName);
|
||||
return profile;
|
||||
}
|
||||
NodeList list = theDocument.getChildNodes();
|
||||
for (int i = 0; i < list.getLength(); i++) {
|
||||
if (list.item(i) instanceof Element) {
|
||||
root = (Element) list.item(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
root = theDocument.getDocumentElement();
|
||||
return root.getLocalName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
|
||||
* <p>
|
||||
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
|
||||
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
|
||||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)}
|
||||
*/
|
||||
public BestPracticeWarningLevel getBestPracticeWarningLevel() {
|
||||
return myBestPracticeWarningLevel;
|
||||
}
|
||||
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
|
||||
String sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
|
||||
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName);
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
|
||||
*/
|
||||
public IValidationSupport getValidationSupport() {
|
||||
return myValidationSupport;
|
||||
}
|
||||
/**
|
||||
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
|
||||
* <p>
|
||||
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
|
||||
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
|
||||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)}
|
||||
*/
|
||||
public BestPracticeWarningLevel getBestPracticeWarningLevel() {
|
||||
return myBestPracticeWarningLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
|
||||
* this level.
|
||||
* <p>
|
||||
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
|
||||
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
|
||||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @param theBestPracticeWarningLevel
|
||||
* The level, must not be <code>null</code>
|
||||
*/
|
||||
public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
|
||||
Validate.notNull(theBestPracticeWarningLevel);
|
||||
myBestPracticeWarningLevel = theBestPracticeWarningLevel;
|
||||
}
|
||||
/**
|
||||
* Returns the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
|
||||
*/
|
||||
public IValidationSupport getValidationSupport() {
|
||||
return myValidationSupport;
|
||||
}
|
||||
|
||||
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
|
||||
myStructureDefintion = theStructureDefintion;
|
||||
}
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
* validator (e.g. because they have not been explicitly declared in a profile) will
|
||||
* be validated but will not cause an error.
|
||||
*/
|
||||
public boolean isAnyExtensionsAllowed() {
|
||||
return myAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
|
||||
*/
|
||||
public void setValidationSupport(IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
/**
|
||||
* If set to {@literal true} (default is true) extensions which are not known to the
|
||||
* validator (e.g. because they have not been explicitly declared in a profile) will
|
||||
* be validated but will not cause an error.
|
||||
*/
|
||||
public void setAnyExtensionsAllowed(boolean theAnyExtensionsAllowed) {
|
||||
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
|
||||
}
|
||||
|
||||
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
/**
|
||||
* Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
|
||||
* this level.
|
||||
* <p>
|
||||
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
|
||||
* set to {@link BestPracticeWarningLevel#Error}, any resource data which does not meet these best practices will be
|
||||
* reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
|
||||
* guielines will be ignored.
|
||||
* </p>
|
||||
*
|
||||
* @param theBestPracticeWarningLevel
|
||||
* The level, must not be <code>null</code>
|
||||
*/
|
||||
public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
|
||||
Validate.notNull(theBestPracticeWarningLevel);
|
||||
myBestPracticeWarningLevel = theBestPracticeWarningLevel;
|
||||
}
|
||||
|
||||
InstanceValidator v;
|
||||
IEvaluationContext evaluationCtx = new NullEvaluationContext();
|
||||
try {
|
||||
v = new InstanceValidator(workerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
|
||||
myStructureDefintion = theStructureDefintion;
|
||||
}
|
||||
|
||||
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
|
||||
v.setAnyExtensionsAllowed(true);
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
/**
|
||||
* Sets the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
|
||||
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
|
||||
*/
|
||||
public void setValidationSupport(IValidationSupport theValidationSupport) {
|
||||
myValidationSupport = theValidationSupport;
|
||||
}
|
||||
|
||||
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
||||
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
|
||||
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
InstanceValidator v;
|
||||
IEvaluationContext evaluationCtx = new NullEvaluationContext();
|
||||
try {
|
||||
v = new InstanceValidator(workerContext, evaluationCtx);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
String resourceName = determineResourceName(document);
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile);
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
|
||||
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
|
||||
v.setResourceIdRule(IdStatus.OPTIONAL);
|
||||
|
||||
String resourceName = json.get("resourceType").getAsString();
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile);
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
|
||||
}
|
||||
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ValidationMessage next = messages.get(i);
|
||||
if ("Binding has no source, so can't be checked".equals(next.getMessage())) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
||||
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
|
||||
}
|
||||
String resourceName = determineResourceName(document);
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, document, profile);
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
|
||||
public class NullEvaluationContext implements IEvaluationContext {
|
||||
String resourceName = json.get("resourceType").getAsString();
|
||||
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
|
||||
if (profile != null) {
|
||||
try {
|
||||
v.validate(null, messages, json, profile);
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ValidationMessage next = messages.get(i);
|
||||
if ("Binding has no source, so can't be checked".equals(next.getMessage())) {
|
||||
messages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Base> executeFunction(Object theAppContext, String theFunctionName, List<List<Base>> theParameters) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
|
||||
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean log(String theArgument, List<Base> theFocus) {
|
||||
return false;
|
||||
}
|
||||
public class NullEvaluationContext implements IEvaluationContext {
|
||||
|
||||
@Override
|
||||
public Base resolveConstant(Object theAppContext, String theName) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeDetails resolveConstantType(Object theAppContext, String theName) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public List<Base> executeFunction(Object theAppContext, String theFunctionName, List<List<Base>> theParameters) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionDetails resolveFunction(String theFunctionName) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public boolean log(String theArgument, List<Base> theFocus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Base resolveReference(Object theAppContext, String theUrl) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Base resolveConstant(Object theAppContext, String theName) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public TypeDetails resolveConstantType(Object theAppContext, String theName) throws PathEngineException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionDetails resolveFunction(String theFunctionName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Base resolveReference(Object theAppContext, String theUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
|||
import org.hl7.fhir.dstu3.elementmodel.Element;
|
||||
import org.hl7.fhir.dstu3.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.dstu3.model.StructureDefinition;
|
||||
import org.hl7.fhir.dstu3.utils.IResourceValidator.ReferenceValidationPolicy;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
|
@ -23,8 +24,26 @@ import com.google.gson.JsonObject;
|
|||
*/
|
||||
public interface IResourceValidator {
|
||||
|
||||
public enum ReferenceValidationPolicy {
|
||||
IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID;
|
||||
|
||||
public boolean checkExists() {
|
||||
return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID;
|
||||
}
|
||||
|
||||
public boolean checkType() {
|
||||
return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
|
||||
}
|
||||
|
||||
public boolean checkValid() {
|
||||
return this == CHECK_VALID;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IValidatorResourceFetcher {
|
||||
Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, IOException;
|
||||
Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, IOException, FHIRException;
|
||||
ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url);
|
||||
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
|
||||
}
|
||||
|
||||
public enum BestPracticeWarningLevel {
|
||||
|
@ -46,6 +65,7 @@ public interface IResourceValidator {
|
|||
OPTIONAL, REQUIRED, PROHIBITED
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* how much to check displays for coded elements
|
||||
|
@ -69,20 +89,19 @@ public interface IResourceValidator {
|
|||
*
|
||||
*/
|
||||
BestPracticeWarningLevel getBasePracticeWarningLevel();
|
||||
void setBestPracticeWarningLevel(BestPracticeWarningLevel value);
|
||||
IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value);
|
||||
|
||||
IValidatorResourceFetcher getFetcher();
|
||||
void setFetcher(IValidatorResourceFetcher value);
|
||||
IResourceValidator setFetcher(IValidatorResourceFetcher value);
|
||||
|
||||
boolean isNoBindingMsgSuppressed();
|
||||
void setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
|
||||
IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
|
||||
|
||||
public boolean isNoInvariantChecks();
|
||||
public void setNoInvariantChecks(boolean value) ;
|
||||
public IResourceValidator setNoInvariantChecks(boolean value) ;
|
||||
|
||||
public boolean isNoTerminologyChecks();
|
||||
public void setNoTerminologyChecks(boolean noTerminologyChecks);
|
||||
|
||||
public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
|
||||
|
||||
/**
|
||||
* Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning
|
||||
|
|
|
@ -11,6 +11,8 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
|
||||
|
@ -82,6 +84,7 @@ import org.hl7.fhir.dstu3.utils.FHIRLexer.FHIRLexerException;
|
|||
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
|
||||
import org.hl7.fhir.dstu3.utils.FHIRPathEngine.IEvaluationContext;
|
||||
import org.hl7.fhir.dstu3.utils.IResourceValidator;
|
||||
import org.hl7.fhir.dstu3.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.dstu3.utils.ValidationProfileSet;
|
||||
import org.hl7.fhir.dstu3.utils.ValidationProfileSet.ProfileRegistration;
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
|
@ -151,6 +154,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
private boolean noBindingMsgSuppressed;
|
||||
private HashMap<Element, ResourceProfiles> resourceProfilesMap;
|
||||
private IValidatorResourceFetcher fetcher;
|
||||
long time = 0;
|
||||
|
||||
/*
|
||||
* Keeps track of whether a particular profile has been checked or not yet
|
||||
|
@ -287,23 +291,33 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
fpe.setHostServices(hostServices);
|
||||
source = Source.InstanceValidator;
|
||||
}
|
||||
|
||||
|
||||
public InstanceValidator(ValidationEngine engine) {
|
||||
super();
|
||||
this.context = engine.getContext();
|
||||
fpe = engine.getFpe();
|
||||
source = Source.InstanceValidator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isNoInvariantChecks() {
|
||||
return noInvariantChecks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNoInvariantChecks(boolean value) {
|
||||
public IResourceValidator setNoInvariantChecks(boolean value) {
|
||||
this.noInvariantChecks = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IValidatorResourceFetcher getFetcher() {
|
||||
return this.fetcher;
|
||||
}
|
||||
|
||||
public void setFetcher(IValidatorResourceFetcher value) {
|
||||
public IResourceValidator setFetcher(IValidatorResourceFetcher value) {
|
||||
this.fetcher = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean allowUnknownExtension(String url) {
|
||||
|
@ -788,6 +802,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, isAbsolute(system), "Coding.system must be an absolute reference, not a local reference");
|
||||
|
||||
if (system != null && code != null && !noTerminologyChecks) {
|
||||
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), "The Coding references a value set, not a code system (\""+system+"\")");
|
||||
try {
|
||||
if (checkCode(errors, element, path, code, system, display))
|
||||
if (theElementCntext != null && theElementCntext.hasBinding()) {
|
||||
|
@ -841,6 +856,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
|
||||
private boolean isValueSet(String url) {
|
||||
try {
|
||||
ValueSet vs = context.fetchResourceWithException(ValueSet.class, url);
|
||||
return vs != null;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContactPoint(List<ValidationMessage> errors, String path, Element focus, ContactPoint fixed) {
|
||||
checkFixedValue(errors, path + ".system", focus.getNamedChild("system"), fixed.getSystemElement(), "system", focus);
|
||||
checkFixedValue(errors, path + ".value", focus.getNamedChild("value"), fixed.getValueElement(), "value", focus);
|
||||
|
@ -1156,12 +1180,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), "end", focus);
|
||||
}
|
||||
|
||||
private void checkPrimitive(List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile) throws FHIRException {
|
||||
private void checkPrimitive(Object appContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile) throws FHIRException, IOException {
|
||||
if (isBlank(e.primitiveValue())) {
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "primitive types must have a value or must have child extensions");
|
||||
return;
|
||||
}
|
||||
if (type.equals("boolean")) {
|
||||
String regex = context.getExtensionString(ToolingExtensions.EXT_REGEX);
|
||||
if (regex!=null)
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().matches(regex), "Element value '" + e.primitiveValue() + "' does not meet regex '" + regex + "'");
|
||||
|
||||
if (type.equals("boolean")) {
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, "true".equals(e.primitiveValue()) || "false".equals(e.primitiveValue()), "boolean values must be 'true' or 'false'");
|
||||
}
|
||||
if (type.equals("uri")) {
|
||||
|
@ -1169,6 +1197,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !e.primitiveValue().startsWith("uuid:"), "URI values cannot start with uuid:");
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().equals(e.primitiveValue().trim()), "URI values cannot have leading or trailing whitespace");
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength()==0 || e.primitiveValue().length() <= context.getMaxLength(), "value is longer than permitted maximum length of " + context.getMaxLength());
|
||||
|
||||
// now, do we check the URI target?
|
||||
if (fetcher != null) {
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, fetcher.resolveURL(appContext, path, e.primitiveValue()), "URL value '"+e.primitiveValue()+"' does not resolve");
|
||||
}
|
||||
}
|
||||
if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) {
|
||||
if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, "@value cannot be empty")) {
|
||||
|
@ -1345,58 +1378,81 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return;
|
||||
}
|
||||
|
||||
String refType = ref.startsWith("#")? "contained": (localResolve(ref, stack, errors, path)!=null ? "bundle" : "remote");
|
||||
Element we = resolve(appContext, ref, stack, errors, path);
|
||||
Element we = localResolve(ref, stack, errors, path);
|
||||
String refType;
|
||||
if (ref.startsWith("#")) {
|
||||
refType = "contained";
|
||||
} else {
|
||||
if (we == null) {
|
||||
refType = "remote";
|
||||
} else {
|
||||
refType = "bundle";
|
||||
}
|
||||
}
|
||||
String ft;
|
||||
if (we != null)
|
||||
ft = we.getType();
|
||||
else
|
||||
ft = tryParse(ref);
|
||||
ReferenceValidationPolicy pol = refType.equals("contained") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(appContext, path, ref);
|
||||
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, we!=null || !refType.equals("contained"), "Unable to resolve contained resource");
|
||||
if (hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft!=null, "Unable to determine type of target resource")) {
|
||||
boolean ok = false;
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (TypeRefComponent type : container.getType()) {
|
||||
if (!ok && type.getCode().equals("Reference")) {
|
||||
// we validate as much as we can. First, can we infer a type from the profile? (Need to change this to targetProfile when Grahame's ready)
|
||||
if (!type.hasTargetProfile() || type.getTargetProfile().equals("http://hl7.org/fhir/StructureDefinition/Resource"))
|
||||
ok = true;
|
||||
else {
|
||||
String pr = type.getTargetProfile(); // Need to change to targetProfile when Grahame's ready
|
||||
if (pol.checkExists()) {
|
||||
if (we == null) {
|
||||
if (fetcher == null)
|
||||
throw new FHIRException("Resource resolution services not provided");
|
||||
we = fetcher.fetch(appContext, ref);
|
||||
}
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, we != null, "Unable to resolve resource '"+ref+"'");
|
||||
}
|
||||
|
||||
String bt = getBaseType(profile, pr);
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + bt);
|
||||
if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, bt != null, "Unable to resolve the profile reference '" + pr + "'")) {
|
||||
b.append(bt);
|
||||
ok = bt.equals(ft);
|
||||
if (ok && we!=null) {
|
||||
doResourceProfile(appContext, we, pr, errors, stack.push(we, -1, null, null), path, element);
|
||||
if (we != null && pol.checkType()) {
|
||||
if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft!=null, "Unable to determine type of target resource")) {
|
||||
boolean ok = false;
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
for (TypeRefComponent type : container.getType()) {
|
||||
if (!ok && type.getCode().equals("Reference")) {
|
||||
// we validate as much as we can. First, can we infer a type from the profile?
|
||||
if (!type.hasTargetProfile() || type.getTargetProfile().equals("http://hl7.org/fhir/StructureDefinition/Resource"))
|
||||
ok = true;
|
||||
else {
|
||||
String pr = type.getTargetProfile();
|
||||
|
||||
String bt = getBaseType(profile, pr);
|
||||
StructureDefinition sd = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + bt);
|
||||
if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, bt != null, "Unable to resolve the profile reference '" + pr + "'")) {
|
||||
b.append(bt);
|
||||
ok = bt.equals(ft);
|
||||
if (ok && we!=null && pol.checkValid()) {
|
||||
doResourceProfile(appContext, we, pr, errors, stack.push(we, -1, null, null), path, element);
|
||||
}
|
||||
} else
|
||||
ok = true; // suppress following check
|
||||
if (ok && type.hasAggregation()) {
|
||||
boolean modeOk;
|
||||
for (Enumeration<AggregationMode> mode : type.getAggregation()) {
|
||||
if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained"))
|
||||
ok = true;
|
||||
else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled"))
|
||||
ok = true;
|
||||
else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled")||refType.equals("remote")))
|
||||
ok = true;
|
||||
}
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference");
|
||||
}
|
||||
} else
|
||||
ok = true; // suppress following check
|
||||
if (ok && type.hasAggregation()) {
|
||||
boolean modeOk;
|
||||
for (Enumeration<AggregationMode> mode : type.getAggregation()) {
|
||||
if (mode.getValue().equals(AggregationMode.CONTAINED) && refType.equals("contained"))
|
||||
ok = true;
|
||||
else if (mode.getValue().equals(AggregationMode.BUNDLED) && refType.equals("bundled"))
|
||||
ok = true;
|
||||
else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled")||refType.equals("remote")))
|
||||
ok = true;
|
||||
}
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Reference is " + refType + " which isn't supported by the specified aggregation mode(s) for the reference");
|
||||
}
|
||||
}
|
||||
if (!ok && type.getCode().equals("*")) {
|
||||
ok = true; // can refer to anything
|
||||
}
|
||||
}
|
||||
if (!ok && type.getCode().equals("*")) {
|
||||
ok = true; // can refer to anything
|
||||
}
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Invalid Resource target type. Found " + ft + ", but expected one of (" + b.toString() + ")");
|
||||
}
|
||||
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, "Invalid Resource target type. Found " + ft + ", but expected one of (" + b.toString() + ")");
|
||||
}
|
||||
if (pol == ReferenceValidationPolicy.CHECK_VALID) {
|
||||
// todo....
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void doResourceProfile(Object appContext, Element resource, String profile, List<ValidationMessage> errors, NodeStack stack, String path, Element element) throws FHIRException, IOException {
|
||||
ResourceProfiles resourceProfiles = addResourceProfile(errors, resource, profile, path, element, stack);
|
||||
if (resourceProfiles.isProcessed()) {
|
||||
|
@ -1571,27 +1627,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return element;
|
||||
|
||||
List<String> nodes = new ArrayList<String>();
|
||||
String s = discriminator;
|
||||
do {
|
||||
String node = null;
|
||||
if (s.contains(".")) {
|
||||
node = s.substring(0, s.indexOf('.'));
|
||||
if (node.contains("(")) {
|
||||
if (!s.contains(")"))
|
||||
throw new DefinitionException("Discriminator has open bracket but no closing bracket: " + discriminator);
|
||||
node = s.substring(0,s.indexOf(')') + 1);
|
||||
s = s.substring(s.indexOf(")") + 1);
|
||||
if (s.startsWith("."))
|
||||
s = s.substring(1);
|
||||
} else
|
||||
s = s.substring(s.indexOf('.') + 1);
|
||||
} else {
|
||||
node = s;
|
||||
s = null;
|
||||
}
|
||||
if (!node.startsWith("@"))
|
||||
nodes.add(node);
|
||||
} while (s !=null && !s.isEmpty());
|
||||
Matcher matcher = Pattern.compile("([a-zA-Z0-0]+(\\([^\\(^\\)]*\\))?)(\\.([a-zA-Z0-0]+(\\([^\\(^\\)]*\\))?))*").matcher(discriminator);
|
||||
while (matcher.find()) {
|
||||
if (!matcher.group(1).startsWith("@"))
|
||||
nodes.add(matcher.group(1));
|
||||
if (matcher.groupCount()>4 && matcher.group(4)!= null && !matcher.group(4).startsWith("@"))
|
||||
nodes.add(matcher.group(4));
|
||||
}
|
||||
|
||||
for (String fullnode : nodes) {
|
||||
String node = fullnode.contains("(") ? fullnode.substring(0, fullnode.indexOf('(')) : fullnode;
|
||||
|
@ -2015,7 +2057,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return null;
|
||||
}
|
||||
|
||||
private Element resolve(Object appContext, String ref, NodeStack stack, List<ValidationMessage> errors, String path) throws FHIRFormatError, DefinitionException, IOException {
|
||||
private Element resolve(Object appContext, String ref, NodeStack stack, List<ValidationMessage> errors, String path) throws IOException, FHIRException {
|
||||
Element local = localResolve(ref, stack, errors, path);
|
||||
if (local!=null)
|
||||
return local;
|
||||
|
@ -2149,8 +2191,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
this.anyExtensionsAllowed = anyExtensionsAllowed;
|
||||
}
|
||||
|
||||
public void setBestPracticeWarningLevel(BestPracticeWarningLevel value) {
|
||||
public IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value) {
|
||||
bpWarnings = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2197,8 +2240,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
* @throws IOException
|
||||
* @throws FHIRException
|
||||
*/
|
||||
private boolean sliceMatches(Object appContext, Element element, String path, ElementDefinition slice, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, NodeStack stack) throws DefinitionException, FHIRException, IOException {
|
||||
if (!slice.getSlicing().hasDiscriminator())
|
||||
private boolean sliceMatches(Object appContext, Element element, String path, ElementDefinition slicer, ElementDefinition ed, StructureDefinition profile, List<ValidationMessage> errors, NodeStack stack) throws DefinitionException, FHIRException, IOException {
|
||||
if (!slicer.getSlicing().hasDiscriminator())
|
||||
return false; // cannot validate in this case
|
||||
|
||||
ExpressionNode n = (ExpressionNode) ed.getUserData("slice.expression.cache");
|
||||
|
@ -2206,7 +2249,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
long t = System.nanoTime();
|
||||
// GG: this approach is flawed because it treats discriminators individually rather than collectively
|
||||
String expression = "true";
|
||||
for (ElementDefinitionSlicingDiscriminatorComponent s : slice.getSlicing().getDiscriminator()) {
|
||||
for (ElementDefinitionSlicingDiscriminatorComponent s : slicer.getSlicing().getDiscriminator()) {
|
||||
String discriminator = s.getPath();
|
||||
if (s.getType() == DiscriminatorType.PROFILE)
|
||||
throw new FHIRException("Validating against slices with discriminators based on profiles is not yet supported by the FHIRPath engine: " + discriminator);
|
||||
|
@ -2226,7 +2269,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
type = criteriaElement.getType().get(0).getCode();
|
||||
}
|
||||
if (type==null)
|
||||
throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + slice.getSliceName() + " does not declare a type");
|
||||
// Slicer won't ever have a slice name, so this error needs to be reworked
|
||||
throw new DefinitionException("Discriminator (" + discriminator + ") is based on type, but slice " + slicer.getSliceName() + " does not declare a type");
|
||||
if (discriminator.isEmpty())
|
||||
expression = expression + " and this is " + type;
|
||||
else
|
||||
|
@ -2249,7 +2293,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
} else if (fixed instanceof BooleanType) {
|
||||
expression = expression + ((BooleanType)fixed).asStringValue();
|
||||
} else
|
||||
throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + slice.getSliceName() + ": " + fixed.getClass().getName());
|
||||
throw new DefinitionException("Unsupported fixed value type for discriminator(" + discriminator + ") for slice " + slicer.getSliceName() + ": " + fixed.getClass().getName());
|
||||
} else if (criteriaElement.hasBinding() && criteriaElement.getBinding().hasStrength() && criteriaElement.getBinding().getStrength().equals(BindingStrength.REQUIRED) && criteriaElement.getBinding().getValueSetReference()!=null) {
|
||||
expression = expression + " and " + discriminator + " in '" + criteriaElement.getBinding().getValueSetReference().getReference() + "'";
|
||||
} else if (criteriaElement.hasMin() && criteriaElement.getMin()>0) {
|
||||
|
@ -2257,7 +2301,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
} else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) {
|
||||
expression = expression + " and " + discriminator + ".exists().not()";
|
||||
} else {
|
||||
throw new DefinitionException("Could not match discriminator (" + discriminator + ") for slice " + slice.getSliceName() + " - does not have fixed value, binding or existence assertions");
|
||||
throw new DefinitionException("Could not match discriminator (" + discriminator + ") for slice " + slicer.getSliceName() + " - does not have fixed value, binding or existence assertions");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2893,7 +2937,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
// String firstBase = null;
|
||||
// firstBase = ebase == null ? base : ebase;
|
||||
|
||||
long time = 0;
|
||||
private void validateElement(Object appContext, List<ValidationMessage> errors, StructureDefinition profile, ElementDefinition definition, StructureDefinition cprofile, ElementDefinition context,
|
||||
Element resource, Element element, String actualType, NodeStack stack, boolean inCodeableConcept) throws FHIRException, FHIRException, IOException {
|
||||
element.markValidation(profile, definition);
|
||||
|
@ -2932,7 +2975,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
|
||||
// 2. assign children to a definition
|
||||
// for each definition, for each child, check whether it belongs in the slice
|
||||
ElementDefinition slice = null;
|
||||
ElementDefinition slicer = null;
|
||||
boolean unsupportedSlicing = false;
|
||||
List<String> problematicPaths = new ArrayList<String>();
|
||||
String slicingPath = null;
|
||||
|
@ -2949,26 +2992,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
slicingPath = null;
|
||||
// where are we with slicing
|
||||
if (ed.hasSlicing()) {
|
||||
if (slice != null && slice.getPath().equals(ed.getPath()))
|
||||
throw new DefinitionException("Slice encountered midway through path on " + slice.getPath());
|
||||
slice = ed;
|
||||
if (slicer != null && slicer.getPath().equals(ed.getPath()))
|
||||
throw new DefinitionException("Slice encountered midway through path on " + slicer.getPath());
|
||||
slicer = ed;
|
||||
process = false;
|
||||
sliceOffset = i;
|
||||
} else if (slice != null && !slice.getPath().equals(ed.getPath()))
|
||||
slice = null;
|
||||
} else if (slicer != null && !slicer.getPath().equals(ed.getPath()))
|
||||
slicer = null;
|
||||
|
||||
// if (process) {
|
||||
for (ElementInfo ei : children) {
|
||||
boolean match = false;
|
||||
if (slice == null || slice == ed) {
|
||||
if (slicer == null || slicer == ed) {
|
||||
match = nameMatches(ei.name, tail(ed.getPath()));
|
||||
} else {
|
||||
// ei.slice = slice;
|
||||
if (nameMatches(ei.name, tail(ed.getPath())))
|
||||
try {
|
||||
match = sliceMatches(appContext, ei.element, ei.path, slice, ed, profile, errors, stack);
|
||||
match = sliceMatches(appContext, ei.element, ei.path, slicer, ed, profile, errors, stack);
|
||||
if (match)
|
||||
ei.slice = slice;
|
||||
ei.slice = slicer;
|
||||
} catch (FHIRException e) {
|
||||
warning(errors, IssueType.PROCESSING, ei.line(), ei.col(), ei.path, false, e.getMessage());
|
||||
unsupportedSlicing = true;
|
||||
|
@ -2976,7 +3019,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
if (match) {
|
||||
if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, ei.definition == null || ei.definition == slice, "Profile " + profile.getUrl() + ", Element matches more than one slice")) {
|
||||
if (rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, ei.definition == null || ei.definition == slicer, "Profile " + profile.getUrl() + ", Element matches more than one slice")) {
|
||||
ei.definition = ed;
|
||||
if (ei.slice == null) {
|
||||
ei.index = i;
|
||||
|
@ -2995,8 +3038,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
int lastSlice = -1;
|
||||
for (ElementInfo ei : children) {
|
||||
String sliceInfo = "";
|
||||
if (slice != null)
|
||||
sliceInfo = " (slice: " + slice.getPath()+")";
|
||||
if (slicer != null)
|
||||
sliceInfo = " (slice: " + slicer.getPath()+")";
|
||||
if (ei.path.endsWith(".extension"))
|
||||
rule(errors, IssueType.INVALID, ei.line(), ei.col(), ei.path, ei.definition != null, "Element is unknown or does not match any slice (url=\"" + ei.element.getNamedChildValue("url") + "\")" + (profile==null ? "" : " for profile " + profile.getUrl()));
|
||||
else if (!unsupportedSlicing)
|
||||
|
@ -3131,7 +3174,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
|
||||
if (type != null) {
|
||||
if (isPrimitiveType(type)) {
|
||||
checkPrimitive(errors, ei.path, type, ei.definition, ei.element, profile);
|
||||
checkPrimitive(appContext, errors, ei.path, type, ei.definition, ei.element, profile);
|
||||
} else {
|
||||
if (type.equals("Identifier"))
|
||||
checkIdentifier(errors, ei.path, ei.element, ei.definition);
|
||||
|
@ -3631,8 +3674,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return noBindingMsgSuppressed;
|
||||
}
|
||||
|
||||
public void setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed) {
|
||||
public IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed) {
|
||||
this.noBindingMsgSuppressed = noBindingMsgSuppressed;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3640,8 +3684,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
return noTerminologyChecks;
|
||||
}
|
||||
|
||||
public void setNoTerminologyChecks(boolean noTerminologyChecks) {
|
||||
public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks) {
|
||||
this.noTerminologyChecks = noTerminologyChecks;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void checkAllInvariants(){
|
||||
|
@ -3666,4 +3711,5 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.hl7.fhir.dstu3.validation;
|
||||
|
||||
import org.hl7.fhir.dstu3.context.IWorkerContext;
|
||||
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
|
||||
|
||||
/**
|
||||
* Placeholder class - Do not use
|
||||
*/
|
||||
class ValidationEngine {
|
||||
|
||||
private ValidationEngine() {
|
||||
|
||||
}
|
||||
|
||||
public IWorkerContext getContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public FHIRPathEngine getFpe() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem;
|
|||
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.ContactPoint;
|
||||
import org.hl7.fhir.dstu3.model.Extension;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
|
@ -96,19 +97,19 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ContactPoint t = p.addTelecom();
|
||||
t.setSystem(org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem.URL);
|
||||
t.setValue("http://infoway-inforoute.ca");
|
||||
|
||||
|
||||
ValidationResult results = myVal.validateWithResult(p);
|
||||
List<SingleValidationMessage> outcome = logResultsAndReturnNonInformationalOnes(results);
|
||||
assertThat(outcome, empty());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #370
|
||||
*/
|
||||
@Test
|
||||
public void testValidateRelatedPerson() {
|
||||
|
||||
|
||||
/*
|
||||
* Try with a code that is in http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype
|
||||
* and therefore should validate
|
||||
|
@ -116,7 +117,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
RelatedPerson rp = new RelatedPerson();
|
||||
rp.getPatient().setReference("Patient/1");
|
||||
rp.getRelationship().addCoding().setSystem("http://hl7.org/fhir/v2/0131").setCode("c");
|
||||
|
||||
|
||||
ValidationResult results = myVal.validateWithResult(rp);
|
||||
List<SingleValidationMessage> outcome = logResultsAndReturnNonInformationalOnes(results);
|
||||
assertThat(outcome, empty());
|
||||
|
@ -127,25 +128,24 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
rp = new RelatedPerson();
|
||||
rp.getPatient().setReference("Patient/1");
|
||||
rp.getRelationship().addCoding().setSystem("http://hl7.org/fhir/v2/0131").setCode("C");
|
||||
|
||||
|
||||
results = myVal.validateWithResult(rp);
|
||||
outcome = logResultsAndReturnNonInformationalOnes(results);
|
||||
assertThat(outcome, empty());
|
||||
|
||||
|
||||
/*
|
||||
* Now a bad code
|
||||
*/
|
||||
rp = new RelatedPerson();
|
||||
rp.getPatient().setReference("Patient/1");
|
||||
rp.getRelationship().addCoding().setSystem("http://hl7.org/fhir/v2/0131").setCode("GAGAGAGA");
|
||||
|
||||
|
||||
results = myVal.validateWithResult(rp);
|
||||
outcome = logResultsAndReturnNonInformationalOnes(results);
|
||||
assertThat(outcome, not(empty()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
// @Ignore
|
||||
public void testValidateBuiltInProfiles() throws Exception {
|
||||
|
@ -163,13 +163,13 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ids.add(next.getId());
|
||||
|
||||
if (next instanceof StructureDefinition) {
|
||||
StructureDefinition sd = (StructureDefinition)next;
|
||||
StructureDefinition sd = (StructureDefinition) next;
|
||||
if (sd.getKind() == StructureDefinitionKind.LOGICAL) {
|
||||
ourLog.info("Skipping logical type: {}", next.getId());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ourLog.info("Validating {}", next.getId());
|
||||
ourLog.trace(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(next));
|
||||
|
||||
|
@ -213,11 +213,11 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
assertEquals(1, fpOutput.size());
|
||||
bool = (BooleanType) fpOutput.get(0);
|
||||
assertTrue(bool.getValue());
|
||||
//
|
||||
// fpOutput = fp.evaluate(bundle, "component.where(code = %resource.code).empty()");
|
||||
// assertEquals(1, fpOutput.size());
|
||||
// bool = (BooleanType) fpOutput.get(0);
|
||||
// assertTrue(bool.getValue());
|
||||
//
|
||||
// fpOutput = fp.evaluate(bundle, "component.where(code = %resource.code).empty()");
|
||||
// assertEquals(1, fpOutput.size());
|
||||
// bool = (BooleanType) fpOutput.get(0);
|
||||
// assertTrue(bool.getValue());
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(inputString);
|
||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||
|
@ -337,7 +337,8 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
|
||||
int index = 0;
|
||||
for (SingleValidationMessage next : theOutput.getMessages()) {
|
||||
ourLog.info("Result {}: {} - {}:{} {} - {}", new Object[] { index, next.getSeverity(), defaultString(next.getLocationLine()), defaultString(next.getLocationCol()), next.getLocationString(), next.getMessage() });
|
||||
ourLog.info("Result {}: {} - {}:{} {} - {}",
|
||||
new Object[] { index, next.getSeverity(), defaultString(next.getLocationLine()), defaultString(next.getLocationCol()), next.getLocationString(), next.getMessage() });
|
||||
index++;
|
||||
|
||||
retVal.add(next);
|
||||
|
@ -404,6 +405,73 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
assertEquals(output.toString(), 0, output.getMessages().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRawJsonResourceWithUnknownExtension() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("1");
|
||||
|
||||
Extension ext = patient.addExtension();
|
||||
ext.setUrl("http://hl7.org/fhir/v3/ethnicity");
|
||||
ext.setValue(new CodeType("Hispanic or Latino"));
|
||||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
|
||||
/*
|
||||
* {
|
||||
* "resourceType": "Patient",
|
||||
* "id": "1",
|
||||
* "extension": [
|
||||
* {
|
||||
* "url": "http://hl7.org/fhir/v3/ethnicity",
|
||||
* "valueCode": "Hispanic or Latino"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(encoded);
|
||||
assertEquals(output.toString(), 1, output.getMessages().size());
|
||||
|
||||
assertEquals("Unknown extension http://hl7.org/fhir/v3/ethnicity", output.getMessages().get(0).getMessage());
|
||||
assertEquals(ResultSeverityEnum.INFORMATION, output.getMessages().get(0).getSeverity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRawJsonResourceWithUnknownExtensionNotAllowed() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("1");
|
||||
|
||||
Extension ext = patient.addExtension();
|
||||
ext.setUrl("http://hl7.org/fhir/v3/ethnicity");
|
||||
ext.setValue(new CodeType("Hispanic or Latino"));
|
||||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
|
||||
/*
|
||||
* {
|
||||
* "resourceType": "Patient",
|
||||
* "id": "1",
|
||||
* "extension": [
|
||||
* {
|
||||
* "url": "http://hl7.org/fhir/v3/ethnicity",
|
||||
* "valueCode": "Hispanic or Latino"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
|
||||
myInstanceVal.setAnyExtensionsAllowed(false);
|
||||
ValidationResult output = myVal.validateWithResult(encoded);
|
||||
assertEquals(output.toString(), 1, output.getMessages().size());
|
||||
|
||||
assertEquals("The extension http://hl7.org/fhir/v3/ethnicity is unknown, and not allowed here", output.getMessages().get(0).getMessage());
|
||||
assertEquals(ResultSeverityEnum.ERROR, output.getMessages().get(0).getSeverity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRawXmlWithMissingRootNamespace() {
|
||||
//@formatter:off
|
||||
|
@ -422,13 +490,13 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
+ " <birthDate value=\"1974-12-25\"/>"
|
||||
+ "</Patient>";
|
||||
//@formatter:on
|
||||
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
assertEquals(output.toString(), 1, output.getMessages().size());
|
||||
assertEquals("This cannot be parsed as a FHIR object (no namespace)", output.getMessages().get(0).getMessage());
|
||||
ourLog.info(output.getMessages().get(0).getLocationString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateRawJsonResourceBadAttributes() {
|
||||
//@formatter:off
|
||||
|
@ -482,7 +550,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
ValidationResult output = myVal.validateWithResult(input);
|
||||
assertEquals(output.toString(), 2, output.getMessages().size());
|
||||
assertThat(output.getMessages().get(0).getMessage(), containsString("Element must have some content"));
|
||||
assertThat(output.getMessages().get(1).getMessage(), containsString("primitive types must have a value or must have child extensions"));
|
||||
assertThat(output.getMessages().get(1).getMessage(), containsString("primitive types must have a value or must have child extensions"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -629,7 +697,9 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
//@formatter:on
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
logResultsAndReturnAll(output);
|
||||
assertEquals("The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status, and a code is required from this value set) (error message = Unknown code[notvalidcode] in system[null])", output.getMessages().get(0).getMessage());
|
||||
assertEquals(
|
||||
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status, and a code is required from this value set) (error message = Unknown code[notvalidcode] in system[null])",
|
||||
output.getMessages().get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -711,8 +781,7 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||
assertThat(errors.toString(), errors.size(), greaterThan(0));
|
||||
assertEquals("Unknown code: http://acme.org / 9988877", errors.get(0).getMessage());
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -725,7 +794,9 @@ public class FhirInstanceValidatorDstu3Test {
|
|||
List<SingleValidationMessage> all = logResultsAndReturnAll(output);
|
||||
assertEquals(1, all.size());
|
||||
assertEquals("Patient.identifier.type", all.get(0).getLocationString());
|
||||
assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type, and a code should come from this value set unless it has no suitable code) (codes = http://example.com/foo/bar#bar)", all.get(0).getMessage());
|
||||
assertEquals(
|
||||
"None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type, and a code should come from this value set unless it has no suitable code) (codes = http://example.com/foo/bar#bar)",
|
||||
all.get(0).getMessage());
|
||||
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
||||
|
||||
}
|
||||
|
|
5
pom.xml
5
pom.xml
|
@ -451,6 +451,11 @@
|
|||
<artifactId>javax.el-api</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.interceptor</groupId>
|
||||
<artifactId>javax.interceptor-api</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.json</groupId>
|
||||
<artifactId>javax.json-api</artifactId>
|
||||
|
|
|
@ -58,6 +58,11 @@
|
|||
(e.g. requests for the second page of results for a search operation).
|
||||
Thanks to Eeva Turkka for reporting!
|
||||
</action>
|
||||
<action type="add">
|
||||
Add configuration property to DSTU3 FhirInstanceValidator to
|
||||
allow client code to change unknown extension handling behaviour.
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.4" date="2017-04-19">
|
||||
<action type="add">
|
||||
This release brings the DSTU3 structures up to FHIR R3 (FHIR 3.0.1) definitions. Note that
|
||||
|
|
|
@ -192,11 +192,10 @@
|
|||
<section name="Resource Validation (Profile/StructureDefinition)">
|
||||
|
||||
<p>
|
||||
As of HAPI FHIR 1.2, HAPI supports validation against StructureDefinition
|
||||
HAPI also supports validation against StructureDefinition
|
||||
resources. This functionality uses the HL7 "InstanceValidator", which is able
|
||||
to
|
||||
check a resource for conformance to a given profile
|
||||
(StructureDefinitions and ValueSets),
|
||||
to check a resource for conformance to FHIR profiles
|
||||
(StructureDefinitions, ValueSets, and CodeSystems),
|
||||
including validating fields, extensions, and codes for conformance to their given ValueSets.
|
||||
</p>
|
||||
<p>
|
||||
|
@ -206,9 +205,9 @@
|
|||
</p>
|
||||
|
||||
<p class="doc_info_bubble">
|
||||
This style of validation is still experimental, and should be used with caution.
|
||||
It is very powerful, but is still under active development and may continue to
|
||||
change over time.
|
||||
The instance validator is experimental in the DSTU2 mode, but has become very stable
|
||||
and full-featured in DSTU3 mode. Use with caution when validating DSTU2 resources using
|
||||
instance validator.
|
||||
</p>
|
||||
|
||||
<subsection name="Preparation">
|
||||
|
@ -243,7 +242,7 @@
|
|||
|
||||
<p>
|
||||
To execute the validator, you simply create an instance of
|
||||
<a href="./apidocs-hl7org-dstu2/ca/uhn/fhir/validation/FhirInstanceValidator.html">FhirInstanceValidator</a>
|
||||
<a href="./apidocs-dstu3/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidator.html">FhirInstanceValidator</a>
|
||||
and register it to new validator, as shown in the example below.
|
||||
</p>
|
||||
|
||||
|
|
Loading…
Reference in New Issue