Use R4 validator for DSTU3 validation

This commit is contained in:
James Agnew 2018-01-30 11:16:04 -06:00
parent e52f582769
commit 8d468de551
14 changed files with 939 additions and 4552 deletions

View File

@ -18,7 +18,7 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.IValidatorModule;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.utils.IResourceValidator.BestPracticeWarningLevel; import org.hl7.fhir.r4.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -66,7 +66,7 @@ public class BaseDstu3Config extends BaseConfig {
@Lazy @Lazy
public IValidatorModule instanceValidatorDstu3() { public IValidatorModule instanceValidatorDstu3() {
FhirInstanceValidator val = new FhirInstanceValidator(); FhirInstanceValidator val = new FhirInstanceValidator();
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning); val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
val.setValidationSupport(validationSupportChainDstu3()); val.setValidationSupport(validationSupportChainDstu3());
return val; return val;
} }

View File

@ -1,354 +1,335 @@
package org.hl7.fhir.dstu3.context; package org.hl7.fhir.dstu3.context;
import java.util.List; import org.hl7.fhir.dstu3.formats.IParser;
import java.util.Set; import org.hl7.fhir.dstu3.formats.ParserType;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.formats.IParser; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.formats.ParserType; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.dstu3.model.ConceptMap; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.dstu3.model.ExpansionProfile; import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.dstu3.model.MetadataResource; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StructureDefinition; import java.util.List;
import org.hl7.fhir.dstu3.model.ValueSet; import java.util.Set;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.TerminologyServiceErrorClass; /**
import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome; * This is the standard interface used for access to underlying FHIR
import org.hl7.fhir.dstu3.utils.INarrativeGenerator; * services through the tools and utilities provided by the reference
import org.hl7.fhir.dstu3.utils.IResourceValidator; * implementation.
import org.hl7.fhir.exceptions.FHIRException; *
import org.hl7.fhir.exceptions.TerminologyServiceException; * The functionality it provides is
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; * - get access to parsers, validators, narrative builders etc
* (you can't create these directly because they need access
* to the right context for their information)
/** *
* This is the standard interface used for access to underlying FHIR * - find resources that the tools need to carry out their tasks
* services through the tools and utilities provided by the reference *
* implementation. * - provide access to terminology services they need.
* * (typically, these terminology service requests are just
* The functionality it provides is * passed through to the local implementation's terminology
* - get access to parsers, validators, narrative builders etc * service)
* (you can't create these directly because they need access *
* to the right context for their information) * @author Grahame
* */
* - find resources that the tools need to carry out their tasks public interface IWorkerContext {
*
* - provide access to terminology services they need. /**
* (typically, these terminology service requests are just * Get the versions of the definitions loaded in context
* passed through to the local implementation's terminology * @return
* service) */
* public String getVersion();
* @author Grahame
*/ // -- Parsers (read and write instances) ----------------------------------------
public interface IWorkerContext {
/** /**
* Get the versions of the definitions loaded in context * Get a parser to read/write instances. Use the defined type (will be extended
* @return * as further types are added, though the only currently anticipate type is RDF)
*/ *
public String getVersion(); * XML/JSON - the standard renderers
* XHTML - render the narrative only (generate it if necessary)
// -- Parsers (read and write instances) ---------------------------------------- *
* @param type
* @return
/** */
* Get a parser to read/write instances. Use the defined type (will be extended public IParser getParser(ParserType type);
* as further types are added, though the only currently anticipate type is RDF)
* /**
* XML/JSON - the standard renderers * Get a parser to read/write instances. Determine the type
* XHTML - render the narrative only (generate it if necessary) * from the stated type. Supported value for type:
* * - the recommended MIME types
* @param type * - variants of application/xml and application/json
* @return * - _format values xml, json
*/ *
public IParser getParser(ParserType type); * @param type
* @return
/** */
* Get a parser to read/write instances. Determine the type public IParser getParser(String type);
* from the stated type. Supported value for type:
* - the recommended MIME types /**
* - variants of application/xml and application/json * Get a JSON parser
* - _format values xml, json *
* * @return
* @param type */
* @return public IParser newJsonParser();
*/
public IParser getParser(String type); /**
* Get an XML parser
/** *
* Get a JSON parser * @return
* */
* @return public IParser newXmlParser();
*/
public IParser newJsonParser(); /**
* Get a generator that can generate narrative for the instance
/** *
* Get an XML parser * @return a prepared generator
* */
* @return public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath);
*/
public IParser newXmlParser();
// -- resource fetchers ---------------------------------------------------
/**
* Get a generator that can generate narrative for the instance /**
* * Find an identified resource. The most common use of this is to access the the
* @return a prepared generator * standard conformance resources that are part of the standard - structure
*/ * definitions, value sets, concept maps, etc.
public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath); *
* Also, the narrative generator uses this, and may access any kind of resource
/** *
* Get a validator that can check whether a resource is valid * The URI is called speculatively for things that might exist, so not finding
* * a matching resouce, return null, not an error
* @return a prepared generator *
* @throws FHIRException * The URI can have one of 3 formats:
* @ * - a full URL e.g. http://acme.org/fhir/ValueSet/[id]
*/ * - a relative URL e.g. ValueSet/[id]
public IResourceValidator newValidator() throws FHIRException; * - a logical id e.g. [id]
*
// -- resource fetchers --------------------------------------------------- * It's an error if the second form doesn't agree with class_. It's an
* error if class_ is null for the last form
/** *
* Find an identified resource. The most common use of this is to access the the * @return
* standard conformance resources that are part of the standard - structure * @throws FHIRException
* definitions, value sets, concept maps, etc. * @throws Exception
* */
* Also, the narrative generator uses this, and may access any kind of resource public <T extends Resource> T fetchResource(Class<T> class_, String uri);
* public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
* The URI is called speculatively for things that might exist, so not finding
* a matching resouce, return null, not an error /**
* * find whether a resource is available.
* The URI can have one of 3 formats: *
* - a full URL e.g. http://acme.org/fhir/ValueSet/[id] * Implementations of the interface can assume that if hasResource ruturns
* - a relative URL e.g. ValueSet/[id] * true, the resource will usually be fetched subsequently
* - a logical id e.g. [id] *
* * @param class_
* It's an error if the second form doesn't agree with class_. It's an * @param uri
* error if class_ is null for the last form * @return
* */
* @param resource public <T extends Resource> boolean hasResource(Class<T> class_, String uri);
* @param Reference
* @return // -- profile services ---------------------------------------------------------
* @throws FHIRException
* @throws Exception public List<String> getResourceNames();
*/ public List<String> getTypeNames();
public <T extends Resource> T fetchResource(Class<T> class_, String uri); public List<StructureDefinition> allStructures();
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException; public List<MetadataResource> allConformanceResources();
/** // -- Terminology services ------------------------------------------------------
* find whether a resource is available.
* public ExpansionProfile getExpansionProfile();
* Implementations of the interface can assume that if hasResource ruturns public void setExpansionProfile(ExpansionProfile expProfile);
* true, the resource will usually be fetched subsequently
* // these are the terminology services used internally by the tools
* @param class_ /**
* @param uri * Find the code system definition for the nominated system uri.
* @return * return null if there isn't one (then the tool might try
*/ * supportsSystem)
public <T extends Resource> boolean hasResource(Class<T> class_, String uri); *
* @param system
// -- profile services --------------------------------------------------------- * @return
*/
public List<String> getResourceNames(); public CodeSystem fetchCodeSystem(String system);
public List<String> getTypeNames();
public List<StructureDefinition> allStructures(); /**
public List<MetadataResource> allConformanceResources(); * True if the underlying terminology service provider will do
* expansion and code validation for the terminology. Corresponds
// -- Terminology services ------------------------------------------------------ * to the extension
*
public ExpansionProfile getExpansionProfile(); * http://hl7.org/fhir/StructureDefinition/capabilitystatement-supported-system
public void setExpansionProfile(ExpansionProfile expProfile); *
* in the Conformance resource
// these are the terminology services used internally by the tools *
/** * @param system
* Find the code system definition for the nominated system uri. * @return
* return null if there isn't one (then the tool might try * @throws Exception
* supportsSystem) */
* public boolean supportsSystem(String system) throws TerminologyServiceException;
* @param system
* @return /**
*/ * find concept maps for a source
public CodeSystem fetchCodeSystem(String system); * @param url
* @return
/** */
* True if the underlying terminology service provider will do public List<ConceptMap> findMapsForSource(String url);
* expansion and code validation for the terminology. Corresponds
* to the extension /**
* * ValueSet Expansion - see $expand
* http://hl7.org/fhir/StructureDefinition/capabilitystatement-supported-system *
* * @param source
* in the Conformance resource * @return
* */
* @param system public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical);
* @return
* @throws Exception /**
*/ * Value set expanion inside the internal expansion engine - used
public boolean supportsSystem(String system) throws TerminologyServiceException; * for references to supported system (see "supportsSystem") for
* which there is no value set.
/** *
* find concept maps for a source * @param inc
* @param url * @return
* @return * @throws FHIRException
*/ */
public List<ConceptMap> findMapsForSource(String url); public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException;
/** public class ValidationResult {
* ValueSet Expansion - see $expand private ConceptDefinitionComponent definition;
* private IssueSeverity severity;
* @param source private String message;
* @return private TerminologyServiceErrorClass errorClass;
*/
public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical); public ValidationResult(IssueSeverity severity, String message) {
this.severity = severity;
/** this.message = message;
* Value set expanion inside the internal expansion engine - used }
* for references to supported system (see "supportsSystem") for
* which there is no value set. public ValidationResult(ConceptDefinitionComponent definition) {
* this.definition = definition;
* @param inc }
* @return
* @throws FHIRException public ValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
*/ this.severity = severity;
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException; this.message = message;
this.definition = definition;
public class ValidationResult { }
private ConceptDefinitionComponent definition;
private IssueSeverity severity; public ValidationResult(IssueSeverity severity, String message, TerminologyServiceErrorClass errorClass) {
private String message; this.severity = severity;
private TerminologyServiceErrorClass errorClass; this.message = message;
this.errorClass = errorClass;
public ValidationResult(IssueSeverity severity, String message) { }
this.severity = severity;
this.message = message; public boolean isOk() {
} return definition != null;
}
public ValidationResult(ConceptDefinitionComponent definition) {
this.definition = definition; public String getDisplay() {
} // We don't want to return question-marks because that prevents something more useful from being displayed (e.g. the code) if there's no display value
// return definition == null ? "??" : definition.getDisplay();
public ValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) { return definition == null ? null : definition.getDisplay();
this.severity = severity; }
this.message = message;
this.definition = definition; public ConceptDefinitionComponent asConceptDefinition() {
} return definition;
}
public ValidationResult(IssueSeverity severity, String message, TerminologyServiceErrorClass errorClass) {
this.severity = severity; public IssueSeverity getSeverity() {
this.message = message; return severity;
this.errorClass = errorClass; }
}
public String getMessage() {
public boolean isOk() { return message;
return definition != null; }
}
public boolean IsNoService() {
public String getDisplay() { return errorClass == TerminologyServiceErrorClass.NOSERVICE;
// We don't want to return question-marks because that prevents something more useful from being displayed (e.g. the code) if there's no display value }
// return definition == null ? "??" : definition.getDisplay();
return definition == null ? null : definition.getDisplay(); public TerminologyServiceErrorClass getErrorClass() {
} return errorClass;
}
public ConceptDefinitionComponent asConceptDefinition() {
return definition;
} }
public IssueSeverity getSeverity() { /**
return severity; * Validation of a code - consult the terminology service
} * to see whether it is known. If known, return a description of it
*
public String getMessage() { * note: always return a result, with either an error or a code description
return message; *
} * corresponds to 2 terminology service calls: $validate-code and $lookup
*
public boolean IsNoService() { * @param system
return errorClass == TerminologyServiceErrorClass.NOSERVICE; * @param code
} * @param display
* @return
public TerminologyServiceErrorClass getErrorClass() { */
return errorClass; public ValidationResult validateCode(String system, String code, String display);
}
/**
* Validation of a code - consult the terminology service
} * to see whether it is known. If known, return a description of it
* Also, check whether it's in the provided value set
/** *
* Validation of a code - consult the terminology service * note: always return a result, with either an error or a code description, or both (e.g. known code, but not in the value set)
* to see whether it is known. If known, return a description of it *
* * corresponds to 2 terminology service calls: $validate-code and $lookup
* note: always return a result, with either an error or a code description *
* * @param system
* corresponds to 2 terminology service calls: $validate-code and $lookup * @param code
* * @param display
* @param system * @return
* @param code */
* @param display public ValidationResult validateCode(String system, String code, String display, ValueSet vs);
* @return public ValidationResult validateCode(Coding code, ValueSet vs);
*/ public ValidationResult validateCode(CodeableConcept code, ValueSet vs);
public ValidationResult validateCode(String system, String code, String display);
/**
/** * Validation of a code - consult the terminology service
* Validation of a code - consult the terminology service * to see whether it is known. If known, return a description of it
* to see whether it is known. If known, return a description of it * Also, check whether it's in the provided value set fragment (for supported systems with no value set definition)
* Also, check whether it's in the provided value set *
* * note: always return a result, with either an error or a code description, or both (e.g. known code, but not in the value set)
* note: always return a result, with either an error or a code description, or both (e.g. known code, but not in the value set) *
* * corresponds to 2 terminology service calls: $validate-code and $lookup
* corresponds to 2 terminology service calls: $validate-code and $lookup *
* * @param system
* @param system * @param code
* @param code * @param display
* @param display * @return
* @return */
*/ public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi);
public ValidationResult validateCode(String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(Coding code, ValueSet vs); /**
public ValidationResult validateCode(CodeableConcept code, ValueSet vs); * returns the recommended tla for the type
*
/** * @param name
* Validation of a code - consult the terminology service * @return
* to see whether it is known. If known, return a description of it */
* Also, check whether it's in the provided value set fragment (for supported systems with no value set definition) public String getAbbreviation(String name);
*
* note: always return a result, with either an error or a code description, or both (e.g. known code, but not in the value set) // return a set of types that have tails
* public Set<String> typeTails();
* corresponds to 2 terminology service calls: $validate-code and $lookup
* public String oid2Uri(String code);
* @param system
* @param code public boolean hasCache();
* @param display
* @return public interface ILoggingService {
*/ public enum LogCategory {
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi); PROGRESS, TX, INIT, CONTEXT, HTML
}
/** public void logMessage(String message); // status messages, always display
* returns the recommended tla for the type public void logDebugMessage(LogCategory category, String message); // verbose; only when debugging
* }
* @param name
* @return public void setLogger(ILoggingService logger);
*/
public String getAbbreviation(String name); public boolean isNoTerminologyServer();
// return a set of types that have tails }
public Set<String> typeTails();
public String oid2Uri(String code);
public boolean hasCache();
public interface ILoggingService {
public enum LogCategory {
PROGRESS, TX, INIT, CONTEXT, HTML
}
public void logMessage(String message); // status messages, always display
public void logDebugMessage(LogCategory category, String message); // verbose; only when debugging
}
public void setLogger(ILoggingService logger);
public boolean isNoTerminologyServer();
}

View File

@ -18,7 +18,6 @@ import org.hl7.fhir.dstu3.terminologies.ValueSetExpander;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderFactory; import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.dstu3.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator; import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.dstu3.utils.IResourceValidator;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
@ -124,10 +123,6 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public IResourceValidator newValidator() {
throw new UnsupportedOperationException();
}
@Override @Override
public IParser newXmlParser() { public IParser newXmlParser() {
@ -204,8 +199,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
} }
} }
} }
boolean caseSensitive = true; boolean caseSensitive = true;
if (isNotBlank(theSystem)) { if (isNotBlank(theSystem)) {
CodeSystem system = fetchCodeSystem(theSystem); CodeSystem system = fetchCodeSystem(theSystem);

View File

@ -1,174 +0,0 @@
package org.hl7.fhir.dstu3.utils;
import java.io.IOException;
import java.io.InputStream;
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;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import com.google.gson.JsonObject;
/**
* Interface to the instance validator. This takes a resource, in one of many forms, and
* checks whether it is valid
*
* @author Grahame Grieve
*
*/
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, FHIRException;
ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url);
boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException;
}
public enum BestPracticeWarningLevel {
Ignore,
Hint,
Warning,
Error
}
public enum CheckDisplayOption {
Ignore,
Check,
CheckCaseAndSpace,
CheckCase,
CheckSpace
}
enum IdStatus {
OPTIONAL, REQUIRED, PROHIBITED
}
/**
* how much to check displays for coded elements
* @return
*/
CheckDisplayOption getCheckDisplay();
void setCheckDisplay(CheckDisplayOption checkDisplay);
/**
* whether the resource must have an id or not (depends on context)
*
* @return
*/
IdStatus getResourceIdRule();
void setResourceIdRule(IdStatus resourceIdRule);
/**
* whether the validator should enforce best practice guidelines
* as defined by various HL7 committees
*
*/
BestPracticeWarningLevel getBasePracticeWarningLevel();
IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value);
IValidatorResourceFetcher getFetcher();
IResourceValidator setFetcher(IValidatorResourceFetcher value);
boolean isNoBindingMsgSuppressed();
IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
public boolean isNoInvariantChecks();
public IResourceValidator setNoInvariantChecks(boolean value) ;
public boolean isNoTerminologyChecks();
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
* @return
*/
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
/**
* Validate suite
*
* you can validate one of the following representations of resources:
*
* stream - provide a format - this is the preferred choice
*
* Use one of these two if the content is known to be valid XML/JSON, and already parsed
* - a DOM element or Document
* - a Json Object
*
* In order to use these, the content must already be parsed - e.g. it must syntactically valid
* - a native resource
* - a elementmodel resource
*
* in addition, you can pass one or more profiles ti validate beyond the base standard - as structure definitions or canonical URLs
* @throws IOException
*/
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.elementmodel.Element element) throws FHIRException, IOException;
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.elementmodel.Element element, ValidationProfileSet profiles) throws FHIRException, IOException;
@Deprecated
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.elementmodel.Element element, String profile) throws FHIRException, IOException;
@Deprecated
void validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.elementmodel.Element element, StructureDefinition profile) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, ValidationProfileSet profiles) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, String profile) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, InputStream stream, FhirFormat format, StructureDefinition profile) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.model.Resource resource) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.model.Resource resource, ValidationProfileSet profiles) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.model.Resource resource, String profile) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.hl7.fhir.dstu3.model.Resource resource, StructureDefinition profile) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, ValidationProfileSet profiles) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, String profile) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Element element, StructureDefinition profile) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, ValidationProfileSet profiles) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, String profile) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, org.w3c.dom.Document document, StructureDefinition profile) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object) throws FHIRException, IOException;
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, ValidationProfileSet profiles) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, String profile) throws FHIRException, IOException;
@Deprecated
org.hl7.fhir.dstu3.elementmodel.Element validate(Object Context, List<ValidationMessage> errors, JsonObject object, StructureDefinition profile) throws FHIRException, IOException;
}

View File

@ -25,6 +25,11 @@
<artifactId>hapi-fhir-utilities</artifactId> <artifactId>hapi-fhir-utilities</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId>
<version>${project.version}</version>
</dependency>
<!-- <!--
Optional dependencies from RI codebase Optional dependencies from RI codebase
@ -105,12 +110,6 @@
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId> <artifactId>hapi-fhir-server</artifactId>

View File

@ -1,284 +1,609 @@
package org.hl7.fhir.dstu3.hapi.validation; package org.hl7.fhir.dstu3.hapi.validation;
import java.io.StringReader;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.dstu3.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.dstu3.utils.IResourceValidator.IdStatus;
import org.hl7.fhir.dstu3.validation.InstanceValidator;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.google.gson.*;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.IValidationContext; import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.IValidatorModule;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r4.context.IWorkerContext;
import org.hl7.fhir.r4.formats.IParser;
import org.hl7.fhir.r4.formats.ParserType;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.r4.utils.INarrativeGenerator;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.r4.utils.IResourceValidator.IdStatus;
import org.hl7.fhir.r4.validation.InstanceValidator;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
import java.util.*;
public class FhirInstanceValidator extends BaseValidatorBridge implements 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 boolean myAnyExtensionsAllowed = true; private boolean myAnyExtensionsAllowed = true;
private BestPracticeWarningLevel myBestPracticeWarningLevel; private BestPracticeWarningLevel myBestPracticeWarningLevel;
private DocumentBuilderFactory myDocBuilderFactory; private DocumentBuilderFactory myDocBuilderFactory;
private StructureDefinition myStructureDefintion; private StructureDefinition myStructureDefintion;
private IValidationSupport myValidationSupport; private IValidationSupport myValidationSupport;
private boolean noTerminologyChecks = false; private boolean noTerminologyChecks = false;
/** /**
* Constructor * Constructor
* * <p>
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support} * Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
*/ */
public FhirInstanceValidator() { public FhirInstanceValidator() {
this(new DefaultProfileValidationSupport()); this(new DefaultProfileValidationSupport());
} }
/** /**
* Constructor which uses the given validation support * Constructor which uses the given validation support
* *
* @param theValidationSupport * @param theValidationSupport The validation support
* The validation support */
*/ public FhirInstanceValidator(IValidationSupport theValidationSupport) {
public FhirInstanceValidator(IValidationSupport theValidationSupport) { myDocBuilderFactory = DocumentBuilderFactory.newInstance();
myDocBuilderFactory = DocumentBuilderFactory.newInstance(); myDocBuilderFactory.setNamespaceAware(true);
myDocBuilderFactory.setNamespaceAware(true); myValidationSupport = theValidationSupport;
myValidationSupport = theValidationSupport; }
}
private String determineResourceName(Document theDocument) { private String determineResourceName(Document theDocument) {
Element root = null; Element root = null;
NodeList list = theDocument.getChildNodes(); NodeList list = theDocument.getChildNodes();
for (int i = 0; i < list.getLength(); i++) { for (int i = 0; i < list.getLength(); i++) {
if (list.item(i) instanceof Element) { if (list.item(i) instanceof Element) {
root = (Element) list.item(i); root = (Element) list.item(i);
break; break;
} }
} }
root = theDocument.getDocumentElement(); root = theDocument.getDocumentElement();
return root.getLocalName(); return root.getLocalName();
} }
private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) { private StructureDefinition findStructureDefinitionForResourceName(final FhirContext theCtx, String resourceName) {
String sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName; String sdName = "http://hl7.org/fhir/StructureDefinition/" + resourceName;
StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName); StructureDefinition profile = myStructureDefintion != null ? myStructureDefintion : myValidationSupport.fetchStructureDefinition(theCtx, sdName);
return profile; return profile;
} }
/** /**
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}). * Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}).
* <p> * <p>
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is * 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 * 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 * reported at the ERROR level. If this setting is set to {@link BestPracticeWarningLevel#Ignore}, best practice
* guielines will be ignored. * guielines will be ignored.
* </p> * </p>
* *
* @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)} * @see {@link #setBestPracticeWarningLevel(BestPracticeWarningLevel)}
*/ */
public BestPracticeWarningLevel getBestPracticeWarningLevel() { public BestPracticeWarningLevel getBestPracticeWarningLevel() {
return myBestPracticeWarningLevel; return myBestPracticeWarningLevel;
} }
/** /**
* Returns the {@link IValidationSupport validation support} in use by this validator. Default is an instance of * Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used. * this level.
*/ * <p>
public IValidationSupport getValidationSupport() { * The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is
return myValidationSupport; * 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;
}
/** /**
* If set to {@literal true} (default is true) extensions which are not known to the * Returns the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
* validator (e.g. because they have not been explicitly declared in a profile) will * {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
* be validated but will not cause an error. */
*/ public IValidationSupport getValidationSupport() {
public boolean isAnyExtensionsAllowed() { return myValidationSupport;
return myAnyExtensionsAllowed; }
}
/** /**
* If set to {@literal true} (default is true) extensions which are not known to the * Sets the {@link IValidationSupport validation support} in use by this validator. Default is an instance of
* validator (e.g. because they have not been explicitly declared in a profile) will * {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used.
* be validated but will not cause an error. */
*/ public void setValidationSupport(IValidationSupport theValidationSupport) {
public void setAnyExtensionsAllowed(boolean theAnyExtensionsAllowed) { myValidationSupport = theValidationSupport;
myAnyExtensionsAllowed = theAnyExtensionsAllowed; }
}
/** /**
* If set to {@literal true} (default is false) the valueSet will not be validate * 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
public boolean isNoTerminologyChecks() { * be validated but will not cause an error.
return noTerminologyChecks; */
} public boolean isAnyExtensionsAllowed() {
return myAnyExtensionsAllowed;
}
/** /**
* If set to {@literal true} (default is false) the valueSet will not be validate * 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
public void setNoTerminologyChecks(final boolean theNoTerminologyChecks) { * be validated but will not cause an error.
noTerminologyChecks = theNoTerminologyChecks; */
} public void setAnyExtensionsAllowed(boolean theAnyExtensionsAllowed) {
myAnyExtensionsAllowed = theAnyExtensionsAllowed;
}
/** /**
* Sets the "best practice warning level". When validating, any deviations from best practices will be reported at * If set to {@literal true} (default is false) the valueSet will not be validate
* this level. */
* <p> public boolean isNoTerminologyChecks() {
* The FHIR Instance Validator has a number of checks for best practices in terms of FHIR usage. If this setting is return noTerminologyChecks;
* 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;
}
public void setStructureDefintion(StructureDefinition theStructureDefintion) { /**
myStructureDefintion = theStructureDefintion; * If set to {@literal true} (default is false) the valueSet will not be validate
} */
public void setNoTerminologyChecks(final boolean theNoTerminologyChecks) {
noTerminologyChecks = theNoTerminologyChecks;
}
/** public void setStructureDefintion(StructureDefinition theStructureDefintion) {
* Sets the {@link IValidationSupport validation support} in use by this validator. Default is an instance of myStructureDefintion = theStructureDefintion;
* {@link DefaultProfileValidationSupport} if the no-arguments constructor for this object was used. }
*/
public void setValidationSupport(IValidationSupport theValidationSupport) {
myValidationSupport = theValidationSupport;
}
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) { protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport); HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
WorkerContextWrapper wrappedWorkerContext = new WorkerContextWrapper(workerContext);
InstanceValidator v; InstanceValidator v;
IEvaluationContext evaluationCtx = new NullEvaluationContext(); FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
try { try {
v = new InstanceValidator(workerContext, evaluationCtx); v = new InstanceValidator(wrappedWorkerContext, evaluationCtx);
} catch (Exception e) { } catch (Exception e) {
throw new ConfigurationException(e); throw new ConfigurationException(e);
} }
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel()); v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed()); v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
v.setResourceIdRule(IdStatus.OPTIONAL); v.setResourceIdRule(IdStatus.OPTIONAL);
v.setNoTerminologyChecks(isNoTerminologyChecks()); v.setNoTerminologyChecks(isNoTerminologyChecks());
List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (theEncoding == EncodingEnum.XML) { if (theEncoding == EncodingEnum.XML) {
Document document; Document document;
try { try {
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder(); DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
InputSource src = new InputSource(new StringReader(theInput)); InputSource src = new InputSource(new StringReader(theInput));
document = builder.parse(src); document = builder.parse(src);
} catch (Exception e2) { } catch (Exception e2) {
ourLog.error("Failure to parse XML input", e2); ourLog.error("Failure to parse XML input", e2);
ValidationMessage m = new ValidationMessage(); ValidationMessage m = new ValidationMessage();
m.setLevel(IssueSeverity.FATAL); m.setLevel(IssueSeverity.FATAL);
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage()); m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
return Collections.singletonList(m); return Collections.singletonList(m);
} }
String resourceName = determineResourceName(document); String resourceName = determineResourceName(document);
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName); StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
if (profile != null) { if (profile != null) {
try { try {
v.validate(null, messages, document, profile); v.validate(null, messages, document, profile.getUrl());
} catch (Exception e) { } catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e); throw new InternalErrorException("Unexpected failure while validating resource", e);
} }
} }
} else if (theEncoding == EncodingEnum.JSON) { } else if (theEncoding == EncodingEnum.JSON) {
Gson gson = new GsonBuilder().create(); Gson gson = new GsonBuilder().create();
JsonObject json = gson.fromJson(theInput, JsonObject.class); JsonObject json = gson.fromJson(theInput, JsonObject.class);
String resourceName = json.get("resourceType").getAsString(); String resourceName = json.get("resourceType").getAsString();
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName); StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
if (profile != null) { if (profile != null) {
try { try {
v.validate(null, messages, json, profile); v.validate(null, messages, json, profile.getUrl());
} catch (Exception e) { } catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e); throw new InternalErrorException("Unexpected failure while validating resource", e);
} }
} }
} else { } else {
throw new IllegalArgumentException("Unknown encoding: " + theEncoding); throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
} }
for (int i = 0; i < messages.size(); i++) { for (int i = 0; i < messages.size(); i++) {
ValidationMessage next = messages.get(i); ValidationMessage next = messages.get(i);
if ("Binding has no source, so can't be checked".equals(next.getMessage())) { if ("Binding has no source, so can't be checked".equals(next.getMessage())) {
messages.remove(i); messages.remove(i);
i--; i--;
} }
} }
return messages; return messages;
} }
@Override @Override
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) { protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding()); return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
} }
public class NullEvaluationContext implements IEvaluationContext {
@Override private class WorkerContextWrapper implements IWorkerContext {
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException { private final HapiWorkerContext myWrap;
return null; private final VersionConvertor_30_40 myConverter;
} private volatile List<org.hl7.fhir.r4.model.StructureDefinition> myAllStructures;
@Override public WorkerContextWrapper(HapiWorkerContext theWorkerContext) {
public List<Base> executeFunction(Object theAppContext, String theFunctionName, List<List<Base>> theParameters) { myWrap = theWorkerContext;
return null; myConverter = new VersionConvertor_30_40();
} }
@Override @Override
public boolean log(String theArgument, List<Base> theFocus) { public List<org.hl7.fhir.r4.model.MetadataResource> allConformanceResources() {
return false; throw new UnsupportedOperationException();
} }
@Override @Override
public Base resolveConstant(Object theAppContext, String theName) throws PathEngineException { public List<org.hl7.fhir.r4.model.StructureDefinition> allStructures() {
return null;
}
@Override List<org.hl7.fhir.r4.model.StructureDefinition> retVal = myAllStructures;
public TypeDetails resolveConstantType(Object theAppContext, String theName) throws PathEngineException { if (retVal == null) {
return null; retVal = new ArrayList<>();
} for (StructureDefinition next : myWrap.allStructures()) {
try {
retVal.add(VersionConvertor_30_40.convertStructureDefinition(next));
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
myAllStructures = retVal;
}
@Override return retVal;
public FunctionDetails resolveFunction(String theFunctionName) { }
return null;
}
@Override @Override
public Base resolveReference(Object theAppContext, String theUrl) { public void cacheResource(org.hl7.fhir.r4.model.Resource res) throws FHIRException {
return null; throw new UnsupportedOperationException();
} }
} private ValidationResult convertValidationResult(org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult theResult) {
IssueSeverity issueSeverity = theResult.getSeverity();
String message = theResult.getMessage();
org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent conceptDefinition = null;
if (theResult.asConceptDefinition() != null) {
try {
conceptDefinition = VersionConvertor_30_40.convertConceptDefinitionComponent(theResult.asConceptDefinition());
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
ValidationResult retVal = new ValidationResult(issueSeverity, message, conceptDefinition);
return retVal;
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ValueSet source, boolean cacheOk, boolean heiarchical) {
ValueSet convertedSource = null;
try {
convertedSource = VersionConvertor_30_40.convertValueSet(source);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome expanded = myWrap.expandVS(convertedSource, cacheOk, heiarchical);
org.hl7.fhir.r4.model.ValueSet convertedResult = null;
if (expanded.getValueset() != null) {
try {
convertedResult = VersionConvertor_30_40.convertValueSet(expanded.getValueset());
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
String error = expanded.getError();
ValueSetExpander.TerminologyServiceErrorClass result = null;
return new ValueSetExpander.ValueSetExpansionOutcome(convertedResult, error, result);
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandVS(org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent binding, boolean cacheOk, boolean heiarchical) throws FHIRException {
throw new UnsupportedOperationException();
}
@Override
public org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandVS(org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent inc, boolean heirarchical) throws TerminologyServiceException {
ValueSet.ConceptSetComponent convertedInc = null;
if (inc != null) {
try {
convertedInc = VersionConvertor_30_40.convertConceptSetComponent(inc);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
ValueSet.ValueSetExpansionComponent expansion = myWrap.expandVS(convertedInc, heirarchical);
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent retVal = null;
if (expansion != null) {
try {
retVal = VersionConvertor_30_40.convertValueSetExpansionComponent(expansion);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
return retVal;
}
@Override
public org.hl7.fhir.r4.model.CodeSystem fetchCodeSystem(String system) {
CodeSystem fetched = myWrap.fetchCodeSystem(system);
if (fetched == null) {
return null;
}
try {
return VersionConvertor_30_40.convertCodeSystem(fetched);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public <T extends org.hl7.fhir.r4.model.Resource> T fetchResource(Class<T> class_, String uri) {
org.hl7.fhir.dstu3.model.Resource fetched;
switch (class_.getSimpleName()) {
case "StructureDefinition":
fetched = myWrap.fetchResource(StructureDefinition.class, uri);
break;
case "ValueSet":
fetched = myWrap.fetchResource(ValueSet.class, uri);
break;
case "CodeSystem":
fetched = myWrap.fetchResource(CodeSystem.class, uri);
break;
case "Questionnaire":
fetched = myWrap.fetchResource(Questionnaire.class, uri);
break;
default:
throw new UnsupportedOperationException("Don't know how to fetch " + class_.getSimpleName());
}
if (fetched == null) {
return null;
}
try {
return (T) VersionConvertor_30_40.convertResource(fetched);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public org.hl7.fhir.r4.model.Resource fetchResourceById(String type, String uri) {
throw new UnsupportedOperationException();
}
@Override
public <T extends org.hl7.fhir.r4.model.Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException {
T retVal = fetchResource(class_, uri);
if (retVal == null) {
throw new FHIRException("Can not find resource of type " + class_.getSimpleName() + " with uri " + uri);
}
return retVal;
}
@Override
public List<org.hl7.fhir.r4.model.ConceptMap> findMapsForSource(String url) {
throw new UnsupportedOperationException();
}
@Override
public String getAbbreviation(String name) {
return myWrap.getAbbreviation(name);
}
public VersionConvertor_30_40 getConverter() {
return myConverter;
}
@Override
public org.hl7.fhir.r4.model.ExpansionProfile getExpansionProfile() {
throw new UnsupportedOperationException();
}
@Override
public void setExpansionProfile(org.hl7.fhir.r4.model.ExpansionProfile expProfile) {
throw new UnsupportedOperationException();
}
@Override
public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(ParserType type) {
throw new UnsupportedOperationException();
}
@Override
public IParser getParser(String type) {
throw new UnsupportedOperationException();
}
@Override
public List<String> getResourceNames() {
return myWrap.getResourceNames();
}
@Override
public Set<String> getResourceNamesAsSet() {
return new HashSet<>(myWrap.getResourceNames());
}
@Override
public List<String> getTypeNames() {
return myWrap.getTypeNames();
}
@Override
public String getVersion() {
return myWrap.getVersion();
}
@Override
public boolean hasCache() {
return myWrap.hasCache();
}
@Override
public <T extends org.hl7.fhir.r4.model.Resource> boolean hasResource(Class<T> class_, String uri) {
throw new UnsupportedOperationException();
}
@Override
public boolean isNoTerminologyServer() {
return myWrap.isNoTerminologyServer();
}
@Override
public IParser newJsonParser() {
throw new UnsupportedOperationException();
}
@Override
public IResourceValidator newValidator() throws FHIRException {
throw new UnsupportedOperationException();
}
@Override
public IParser newXmlParser() {
throw new UnsupportedOperationException();
}
@Override
public String oid2Uri(String code) {
return myWrap.oid2Uri(code);
}
@Override
public void setLogger(ILoggingService logger) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsSystem(String system) throws TerminologyServiceException {
return myWrap.supportsSystem(system);
}
@Override
public TranslationServices translator() {
throw new UnsupportedOperationException();
}
@Override
public Set<String> typeTails() {
return myWrap.typeTails();
}
@Override
public ValidationResult validateCode(String system, String code, String display) {
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display);
return convertValidationResult(result);
}
@Override
public ValidationResult validateCode(String system, String code, String display, org.hl7.fhir.r4.model.ValueSet vs) {
ValueSet convertedVs = null;
try {
if (vs != null) {
convertedVs = VersionConvertor_30_40.convertValueSet(vs);
}
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display, convertedVs);
return convertValidationResult(result);
}
@Override
public ValidationResult validateCode(org.hl7.fhir.r4.model.Coding code, org.hl7.fhir.r4.model.ValueSet vs) {
Coding convertedCode = null;
ValueSet convertedVs = null;
try {
if (code != null) {
convertedCode = VersionConvertor_30_40.convertCoding(code);
}
if (vs != null) {
convertedVs = VersionConvertor_30_40.convertValueSet(vs);
}
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
return convertValidationResult(result);
}
@Override
public ValidationResult validateCode(org.hl7.fhir.r4.model.CodeableConcept code, org.hl7.fhir.r4.model.ValueSet vs) {
CodeableConcept convertedCode = null;
ValueSet convertedVs = null;
try {
if (code != null) {
convertedCode = VersionConvertor_30_40.convertCodeableConcept(code);
}
if (vs != null) {
convertedVs = VersionConvertor_30_40.convertValueSet(vs);
}
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(convertedCode, convertedVs);
return convertValidationResult(result);
}
@Override
public ValidationResult validateCode(String system, String code, String display, org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent vsi) {
ValueSet.ConceptSetComponent conceptSetComponent = null;
if (vsi != null) {
try {
conceptSetComponent = VersionConvertor_30_40.convertConceptSetComponent(vsi);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
org.hl7.fhir.dstu3.context.IWorkerContext.ValidationResult result = myWrap.validateCode(system, code, display, conceptSetComponent);
return convertValidationResult(result);
}
}
} }

View File

@ -241,7 +241,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding()); return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
} }
public class NullEvaluationContext implements IEvaluationContext { public static class NullEvaluationContext implements IEvaluationContext {
@Override @Override
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException { public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException {

View File

@ -286,7 +286,6 @@ public class ResponseValidatingInterceptorDstu3Test {
* Ignored until #264 is fixed * Ignored until #264 is fixed
*/ */
@Test @Test
@Ignore
public void testSearchJsonInvalidNoValidatorsSpecified() throws Exception { public void testSearchJsonInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setValue("002"); patient.addIdentifier().setValue("002");
@ -305,7 +304,6 @@ public class ResponseValidatingInterceptorDstu3Test {
ourLog.info("Response was:\n{}", responseContent); ourLog.info("Response was:\n{}", responseContent);
Assert.assertEquals(422, status.getStatusLine().getStatusCode()); Assert.assertEquals(422, status.getStatusLine().getStatusCode());
Assert.assertThat(status.toString(), Matchers.containsString("X-FHIR-Response-Validation"));
Assert.assertThat(responseContent, Matchers.containsString("<severity value=\"error\"/>")); Assert.assertThat(responseContent, Matchers.containsString("<severity value=\"error\"/>"));
} }
@ -384,7 +382,6 @@ public class ResponseValidatingInterceptorDstu3Test {
* Ignored until #264 is fixed * Ignored until #264 is fixed
*/ */
@Test @Test
@Ignore
public void testSearchXmlInvalidNoValidatorsSpecified() throws Exception { public void testSearchXmlInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setValue("002"); patient.addIdentifier().setValue("002");
@ -403,7 +400,7 @@ public class ResponseValidatingInterceptorDstu3Test {
ourLog.info("Response was:\n{}", responseContent); ourLog.info("Response was:\n{}", responseContent);
Assert.assertEquals(422, status.getStatusLine().getStatusCode()); Assert.assertEquals(422, status.getStatusLine().getStatusCode());
Assert.assertThat(status.toString(), Matchers.containsString("X-FHIR-Response-Validation")); Assert.assertThat(responseContent, Matchers.containsString("<severity value=\"error\"/>"));
} }
@Test @Test

View File

@ -20,6 +20,7 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
@ -283,7 +284,6 @@ public class ResponseValidatingInterceptorR4Test {
* Ignored until #264 is fixed * Ignored until #264 is fixed
*/ */
@Test @Test
@Ignore
public void testSearchJsonInvalidNoValidatorsSpecified() throws Exception { public void testSearchJsonInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setValue("002"); patient.addIdentifier().setValue("002");
@ -302,7 +302,6 @@ public class ResponseValidatingInterceptorR4Test {
ourLog.info("Response was:\n{}", responseContent); ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode()); assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-FHIR-Response-Validation"));
assertThat(responseContent, containsString("<severity value=\"error\"/>")); assertThat(responseContent, containsString("<severity value=\"error\"/>"));
} }
@ -381,7 +380,6 @@ public class ResponseValidatingInterceptorR4Test {
* Ignored until #264 is fixed * Ignored until #264 is fixed
*/ */
@Test @Test
@Ignore
public void testSearchXmlInvalidNoValidatorsSpecified() throws Exception { public void testSearchXmlInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setValue("002"); patient.addIdentifier().setValue("002");
@ -400,7 +398,7 @@ public class ResponseValidatingInterceptorR4Test {
ourLog.info("Response was:\n{}", responseContent); ourLog.info("Response was:\n{}", responseContent);
assertEquals(422, status.getStatusLine().getStatusCode()); assertEquals(422, status.getStatusLine().getStatusCode());
assertThat(status.toString(), containsString("X-FHIR-Response-Validation")); Assert.assertThat(responseContent, Matchers.containsString("<severity value=\"error\"/>"));
} }
@Test @Test

View File

@ -506,12 +506,11 @@ public class FhirInstanceValidatorDstu3Test {
@Test @Test
public void testValidateRawXmlResourceWithEmptyPrimitive() { public void testValidateRawXmlResourceWithEmptyPrimitive() {
// @formatter:off
String input = "<Patient xmlns=\"http://hl7.org/fhir\"><name><given/></name></Patient>"; String input = "<Patient xmlns=\"http://hl7.org/fhir\"><name><given/></name></Patient>";
// @formatter:on
ValidationResult output = myVal.validateWithResult(input); ValidationResult output = myVal.validateWithResult(input);
assertEquals(output.toString(), 2, output.getMessages().size()); ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(output.toOperationOutcome()));
assertEquals(output.toString(), 3, output.getMessages().size());
assertThat(output.getMessages().get(0).getMessage(), containsString("Element must have some content")); 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"));
} }
@ -677,7 +676,7 @@ public class FhirInstanceValidatorDstu3Test {
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output); List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
assertThat(errors.toString(), containsString("Element 'Observation.subject': minimum required = 1, but only found 0")); assertThat(errors.toString(), containsString("Element 'Observation.subject': minimum required = 1, but only found 0"));
assertThat(errors.toString(), containsString("Element 'Observation.context: max allowed = 0, but found 1")); assertThat(errors.toString(), containsString("Element 'Observation.context': max allowed = 0, but found 1"));
assertThat(errors.toString(), containsString("Element 'Observation.device': minimum required = 1, but only found 0")); assertThat(errors.toString(), containsString("Element 'Observation.device': minimum required = 1, but only found 0"));
assertThat(errors.toString(), containsString("")); assertThat(errors.toString(), containsString(""));
} }

View File

@ -170,7 +170,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
ValidationResult errors = myVal.validateWithResult(qa); ValidationResult errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString()); ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("minimum required = 1, but only found 0 - QuestionnaireResponse.item")); assertThat(errors.toString(), containsString("No LinkId, so can't be validated"));
} }
@Test @Test

View File

@ -287,7 +287,6 @@ public class ResourceValidatorDstu3Test {
* TODO: re-enable this * TODO: re-enable this
*/ */
@Test @Test
@Ignore
public void testValidateQuestionnaireWithCanonicalUrl() { public void testValidateQuestionnaireWithCanonicalUrl() {
String input = "{\n" + String input = "{\n" +
" \"resourceType\": \"Questionnaire\",\n" + " \"resourceType\": \"Questionnaire\",\n" +
@ -377,16 +376,15 @@ public class ResourceValidatorDstu3Test {
PatientProfileDstu3 myPatient = new PatientProfileDstu3(); PatientProfileDstu3 myPatient = new PatientProfileDstu3();
myPatient.setId("1"); myPatient.setId("1");
myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey"))); myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey")));
myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setSystem("furry-white"))); myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-white")));
myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393")); myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393"));
myPatient.addName().setFamily("FamilyName"); myPatient.addName().setFamily("FamilyName");
myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension")); myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));
IParser p = ourCtx.newJsonParser().setPrettyPrint(true); IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(myPatient); String messageString = p.encodeResourceToString(myPatient);
ourLog.info(messageString); // ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder( assertThat(messageString, stringContainsInOrder(
"meta", "meta",
"String Extension", "String Extension",
@ -399,7 +397,6 @@ public class ResourceValidatorDstu3Test {
"extension", "extension",
"meta" "meta"
))); )));
//@formatter:on
FhirValidator val = ourCtx.newValidator(); FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx)); val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
@ -426,7 +423,7 @@ public class ResourceValidatorDstu3Test {
PatientProfileDstu3 myPatient = new PatientProfileDstu3(); PatientProfileDstu3 myPatient = new PatientProfileDstu3();
myPatient.setId("1"); myPatient.setId("1");
myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey"))); myPatient.setColorPrimary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-grey")));
myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setSystem("furry-white"))); myPatient.setColorSecondary(new CodeableConcept().addCoding(new Coding().setSystem("http://example.com#animalColor").setCode("furry-white")));
myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393")); myPatient.setOwningOrganization(new Reference("Organization/2.25.79433498044103547197447759549862032393"));
myPatient.addName().setFamily("FamilyName"); myPatient.addName().setFamily("FamilyName");
myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension")); myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));

View File

@ -8,20 +8,9 @@
<body> <body>
<release version="3.3.0" date="TBD"> <release version="3.3.0" date="TBD">
<action type="add"> <action type="add">
The version of a few dependencies have been bumped to the
latest versions (dependent HAPI modules listed in brackets):
<![CDATA[
<ul>
<li>Hibernate (JPA): 5.2.10.Final -&gt; 5.2.12.Final</li>
<li>Spring (JPA): 5.0.0 -&gt; 5.0.3</li>
<li>Thymeleaf (Web Tespage Overlay): 3.0.7.RELEASE -&gt; 3.0.9.RELEASE</li>
</ul>
]]>
</action>
<action type="add">
This release corrects an inefficiency in the JPA Server, but requires a schema This release corrects an inefficiency in the JPA Server, but requires a schema
change in order to update. Prior to this version of HAPI FHIR, a CLOB column change in order to update. Prior to this version of HAPI FHIR, a CLOB column
containing the complete resource body was stored in two containing the complete resource body was stored in two
tables: HFJ_RESOURCE and HFJ_RES_VER. Because the same content was stored in two tables: HFJ_RESOURCE and HFJ_RES_VER. Because the same content was stored in two
places, the database consumed more space than is needed to. places, the database consumed more space than is needed to.
<![CDATA[<br/><br/>]]> <![CDATA[<br/><br/>]]>
@ -32,6 +21,26 @@
set them to nullable if you want an easy means of rolling back). Naturally set them to nullable if you want an easy means of rolling back). Naturally
you should back your database up prior to making this change. you should back your database up prior to making this change.
</action> </action>
<action type="fix">
The validation module has been refactored to use the R4 (currently maintained)
validator even for DSTU3 validation. This is done by using an automatic
converter which converts StructureDefinition/ValueSet/CodeSystem resources
which are used as inputs to the validator. This change should fix a number
of known issues with the validator, as they have been fixed in R4 but
not in DSTU3. This also makes our validator much more maintainable
since it is now one codebase.
</action>
<action type="add">
The version of a few dependencies have been bumped to the
latest versions (dependent HAPI modules listed in brackets):
<![CDATA[
<ul>
<li>Hibernate (JPA): 5.2.10.Final -&gt; 5.2.12.Final</li>
<li>Spring (JPA): 5.0.0 -&gt; 5.0.3</li>
<li>Thymeleaf (Web Tespage Overlay): 3.0.7.RELEASE -&gt; 3.0.9.RELEASE</li>
</ul>
]]>
</action>
<action type="fix"> <action type="fix">
Fix a crash in the JSON parser when parsing extensions on repeatable Fix a crash in the JSON parser when parsing extensions on repeatable
elements (e.g. Patient.address.line) where there is an extension on the elements (e.g. Patient.address.line) where there is an extension on the