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 org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -66,7 +66,7 @@ public class BaseDstu3Config extends BaseConfig {
@Lazy
public IValidatorModule instanceValidatorDstu3() {
FhirInstanceValidator val = new FhirInstanceValidator();
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
val.setValidationSupport(validationSupportChainDstu3());
return val;
}

View File

@ -1,354 +1,335 @@
package org.hl7.fhir.dstu3.context;
import java.util.List;
import java.util.Set;
import org.hl7.fhir.dstu3.formats.IParser;
import org.hl7.fhir.dstu3.formats.ParserType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.ExpansionProfile;
import org.hl7.fhir.dstu3.model.MetadataResource;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.ValueSet;
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;
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.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
/**
* This is the standard interface used for access to underlying FHIR
* services through the tools and utilities provided by the reference
* implementation.
*
* The functionality it provides is
* - 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)
*
* - find resources that the tools need to carry out their tasks
*
* - provide access to terminology services they need.
* (typically, these terminology service requests are just
* passed through to the local implementation's terminology
* service)
*
* @author Grahame
*/
public interface IWorkerContext {
/**
* Get the versions of the definitions loaded in context
* @return
*/
public String getVersion();
// -- Parsers (read and write instances) ----------------------------------------
/**
* Get a parser to read/write instances. Use the defined type (will be extended
* as further types are added, though the only currently anticipate type is RDF)
*
* XML/JSON - the standard renderers
* XHTML - render the narrative only (generate it if necessary)
*
* @param type
* @return
*/
public IParser getParser(ParserType type);
/**
* Get a parser to read/write instances. Determine the type
* from the stated type. Supported value for type:
* - the recommended MIME types
* - variants of application/xml and application/json
* - _format values xml, json
*
* @param type
* @return
*/
public IParser getParser(String type);
/**
* Get a JSON parser
*
* @return
*/
public IParser newJsonParser();
/**
* Get an XML parser
*
* @return
*/
public IParser newXmlParser();
/**
* Get a generator that can generate narrative for the instance
*
* @return a prepared generator
*/
public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath);
/**
* Get a validator that can check whether a resource is valid
*
* @return a prepared generator
* @throws FHIRException
* @
*/
public IResourceValidator newValidator() throws FHIRException;
// -- resource fetchers ---------------------------------------------------
/**
* Find an identified resource. The most common use of this is to access the the
* standard conformance resources that are part of the standard - structure
* definitions, value sets, concept maps, etc.
*
* Also, the narrative generator uses this, and may access any kind of resource
*
* The URI is called speculatively for things that might exist, so not finding
* a matching resouce, return null, not an error
*
* 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]
* - a logical id e.g. [id]
*
* 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
*
* @param resource
* @param Reference
* @return
* @throws FHIRException
* @throws Exception
*/
public <T extends Resource> T fetchResource(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
/**
* find whether a resource is available.
*
* Implementations of the interface can assume that if hasResource ruturns
* true, the resource will usually be fetched subsequently
*
* @param class_
* @param uri
* @return
*/
public <T extends Resource> boolean hasResource(Class<T> class_, String uri);
// -- profile services ---------------------------------------------------------
public List<String> getResourceNames();
public List<String> getTypeNames();
public List<StructureDefinition> allStructures();
public List<MetadataResource> allConformanceResources();
// -- Terminology services ------------------------------------------------------
public ExpansionProfile getExpansionProfile();
public void setExpansionProfile(ExpansionProfile expProfile);
// these are the terminology services used internally by the tools
/**
* Find the code system definition for the nominated system uri.
* return null if there isn't one (then the tool might try
* supportsSystem)
*
* @param system
* @return
*/
public CodeSystem fetchCodeSystem(String system);
/**
* True if the underlying terminology service provider will do
* expansion and code validation for the terminology. Corresponds
* to the extension
*
* http://hl7.org/fhir/StructureDefinition/capabilitystatement-supported-system
*
* in the Conformance resource
*
* @param system
* @return
* @throws Exception
*/
public boolean supportsSystem(String system) throws TerminologyServiceException;
/**
* find concept maps for a source
* @param url
* @return
*/
public List<ConceptMap> findMapsForSource(String url);
/**
* ValueSet Expansion - see $expand
*
* @param source
* @return
*/
public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical);
/**
* Value set expanion inside the internal expansion engine - used
* for references to supported system (see "supportsSystem") for
* which there is no value set.
*
* @param inc
* @return
* @throws FHIRException
*/
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException;
public class ValidationResult {
private ConceptDefinitionComponent definition;
private IssueSeverity severity;
private String message;
private TerminologyServiceErrorClass errorClass;
public ValidationResult(IssueSeverity severity, String message) {
this.severity = severity;
this.message = message;
}
public ValidationResult(ConceptDefinitionComponent definition) {
this.definition = definition;
}
public ValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
this.severity = severity;
this.message = message;
this.definition = definition;
}
public ValidationResult(IssueSeverity severity, String message, TerminologyServiceErrorClass errorClass) {
this.severity = severity;
this.message = message;
this.errorClass = errorClass;
}
public boolean isOk() {
return definition != null;
}
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();
return definition == null ? null : definition.getDisplay();
}
public ConceptDefinitionComponent asConceptDefinition() {
return definition;
}
public IssueSeverity getSeverity() {
return severity;
}
public String getMessage() {
return message;
}
public boolean IsNoService() {
return errorClass == TerminologyServiceErrorClass.NOSERVICE;
}
public TerminologyServiceErrorClass getErrorClass() {
return errorClass;
}
}
/**
* Validation of a code - consult the terminology service
* to see whether it is known. If known, return a description of it
*
* note: always return a result, with either an error or a code description
*
* corresponds to 2 terminology service calls: $validate-code and $lookup
*
* @param system
* @param code
* @param display
* @return
*/
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
*
* 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
*
* @param system
* @param code
* @param display
* @return
*/
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);
/**
* 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 fragment (for supported systems with no value set definition)
*
* 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
*
* @param system
* @param code
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi);
/**
* returns the recommended tla for the type
*
* @param name
* @return
*/
public String getAbbreviation(String name);
// 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();
}
package org.hl7.fhir.dstu3.context;
import org.hl7.fhir.dstu3.formats.IParser;
import org.hl7.fhir.dstu3.formats.ParserType;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
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;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import java.util.List;
import java.util.Set;
/**
* This is the standard interface used for access to underlying FHIR
* services through the tools and utilities provided by the reference
* implementation.
*
* The functionality it provides is
* - 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)
*
* - find resources that the tools need to carry out their tasks
*
* - provide access to terminology services they need.
* (typically, these terminology service requests are just
* passed through to the local implementation's terminology
* service)
*
* @author Grahame
*/
public interface IWorkerContext {
/**
* Get the versions of the definitions loaded in context
* @return
*/
public String getVersion();
// -- Parsers (read and write instances) ----------------------------------------
/**
* Get a parser to read/write instances. Use the defined type (will be extended
* as further types are added, though the only currently anticipate type is RDF)
*
* XML/JSON - the standard renderers
* XHTML - render the narrative only (generate it if necessary)
*
* @param type
* @return
*/
public IParser getParser(ParserType type);
/**
* Get a parser to read/write instances. Determine the type
* from the stated type. Supported value for type:
* - the recommended MIME types
* - variants of application/xml and application/json
* - _format values xml, json
*
* @param type
* @return
*/
public IParser getParser(String type);
/**
* Get a JSON parser
*
* @return
*/
public IParser newJsonParser();
/**
* Get an XML parser
*
* @return
*/
public IParser newXmlParser();
/**
* Get a generator that can generate narrative for the instance
*
* @return a prepared generator
*/
public INarrativeGenerator getNarrativeGenerator(String prefix, String basePath);
// -- resource fetchers ---------------------------------------------------
/**
* Find an identified resource. The most common use of this is to access the the
* standard conformance resources that are part of the standard - structure
* definitions, value sets, concept maps, etc.
*
* Also, the narrative generator uses this, and may access any kind of resource
*
* The URI is called speculatively for things that might exist, so not finding
* a matching resouce, return null, not an error
*
* 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]
* - a logical id e.g. [id]
*
* 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
*
* @return
* @throws FHIRException
* @throws Exception
*/
public <T extends Resource> T fetchResource(Class<T> class_, String uri);
public <T extends Resource> T fetchResourceWithException(Class<T> class_, String uri) throws FHIRException;
/**
* find whether a resource is available.
*
* Implementations of the interface can assume that if hasResource ruturns
* true, the resource will usually be fetched subsequently
*
* @param class_
* @param uri
* @return
*/
public <T extends Resource> boolean hasResource(Class<T> class_, String uri);
// -- profile services ---------------------------------------------------------
public List<String> getResourceNames();
public List<String> getTypeNames();
public List<StructureDefinition> allStructures();
public List<MetadataResource> allConformanceResources();
// -- Terminology services ------------------------------------------------------
public ExpansionProfile getExpansionProfile();
public void setExpansionProfile(ExpansionProfile expProfile);
// these are the terminology services used internally by the tools
/**
* Find the code system definition for the nominated system uri.
* return null if there isn't one (then the tool might try
* supportsSystem)
*
* @param system
* @return
*/
public CodeSystem fetchCodeSystem(String system);
/**
* True if the underlying terminology service provider will do
* expansion and code validation for the terminology. Corresponds
* to the extension
*
* http://hl7.org/fhir/StructureDefinition/capabilitystatement-supported-system
*
* in the Conformance resource
*
* @param system
* @return
* @throws Exception
*/
public boolean supportsSystem(String system) throws TerminologyServiceException;
/**
* find concept maps for a source
* @param url
* @return
*/
public List<ConceptMap> findMapsForSource(String url);
/**
* ValueSet Expansion - see $expand
*
* @param source
* @return
*/
public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical);
/**
* Value set expanion inside the internal expansion engine - used
* for references to supported system (see "supportsSystem") for
* which there is no value set.
*
* @param inc
* @return
* @throws FHIRException
*/
public ValueSetExpansionComponent expandVS(ConceptSetComponent inc, boolean heiarchical) throws TerminologyServiceException;
public class ValidationResult {
private ConceptDefinitionComponent definition;
private IssueSeverity severity;
private String message;
private TerminologyServiceErrorClass errorClass;
public ValidationResult(IssueSeverity severity, String message) {
this.severity = severity;
this.message = message;
}
public ValidationResult(ConceptDefinitionComponent definition) {
this.definition = definition;
}
public ValidationResult(IssueSeverity severity, String message, ConceptDefinitionComponent definition) {
this.severity = severity;
this.message = message;
this.definition = definition;
}
public ValidationResult(IssueSeverity severity, String message, TerminologyServiceErrorClass errorClass) {
this.severity = severity;
this.message = message;
this.errorClass = errorClass;
}
public boolean isOk() {
return definition != null;
}
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();
return definition == null ? null : definition.getDisplay();
}
public ConceptDefinitionComponent asConceptDefinition() {
return definition;
}
public IssueSeverity getSeverity() {
return severity;
}
public String getMessage() {
return message;
}
public boolean IsNoService() {
return errorClass == TerminologyServiceErrorClass.NOSERVICE;
}
public TerminologyServiceErrorClass getErrorClass() {
return errorClass;
}
}
/**
* Validation of a code - consult the terminology service
* to see whether it is known. If known, return a description of it
*
* note: always return a result, with either an error or a code description
*
* corresponds to 2 terminology service calls: $validate-code and $lookup
*
* @param system
* @param code
* @param display
* @return
*/
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
*
* 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
*
* @param system
* @param code
* @param display
* @return
*/
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);
/**
* 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 fragment (for supported systems with no value set definition)
*
* 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
*
* @param system
* @param code
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi);
/**
* returns the recommended tla for the type
*
* @param name
* @return
*/
public String getAbbreviation(String name);
// 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.ValueSetExpanderSimple;
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.TerminologyServiceException;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
@ -124,10 +123,6 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
throw new UnsupportedOperationException();
}
@Override
public IResourceValidator newValidator() {
throw new UnsupportedOperationException();
}
@Override
public IParser newXmlParser() {
@ -204,8 +199,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
}
}
}
boolean caseSensitive = true;
if (isNotBlank(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>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId>
<version>${project.version}</version>
</dependency>
<!--
Optional dependencies from RI codebase
@ -105,12 +110,6 @@
<artifactId>woodstox-core-asl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>

View File

@ -1,284 +1,609 @@
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.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.validation.IValidationContext;
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 {
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 BestPracticeWarningLevel myBestPracticeWarningLevel;
private DocumentBuilderFactory myDocBuilderFactory;
private StructureDefinition myStructureDefintion;
private IValidationSupport myValidationSupport;
private boolean noTerminologyChecks = false;
private boolean myAnyExtensionsAllowed = true;
private BestPracticeWarningLevel myBestPracticeWarningLevel;
private DocumentBuilderFactory myDocBuilderFactory;
private StructureDefinition myStructureDefintion;
private IValidationSupport myValidationSupport;
private boolean noTerminologyChecks = false;
/**
* Constructor
*
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
*/
public FhirInstanceValidator() {
this(new DefaultProfileValidationSupport());
}
/**
* Constructor
* <p>
* Uses {@link DefaultProfileValidationSupport} for {@link IValidationSupport validation support}
*/
public FhirInstanceValidator() {
this(new DefaultProfileValidationSupport());
}
/**
* 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 which uses the given validation support
*
* @param theValidationSupport The validation support
*/
public FhirInstanceValidator(IValidationSupport theValidationSupport) {
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
myDocBuilderFactory.setNamespaceAware(true);
myValidationSupport = theValidationSupport;
}
private String determineResourceName(Document theDocument) {
Element root = null;
private String determineResourceName(Document theDocument) {
Element root = null;
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();
}
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 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;
}
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 "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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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 false) the valueSet will not be validate
*/
public boolean isNoTerminologyChecks() {
return noTerminologyChecks;
}
/**
* 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;
}
/**
* If set to {@literal true} (default is false) the valueSet will not be validate
*/
public void setNoTerminologyChecks(final boolean theNoTerminologyChecks) {
noTerminologyChecks = theNoTerminologyChecks;
}
/**
* 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;
}
/**
* 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;
}
/**
* If set to {@literal true} (default is false) the valueSet will not be validate
*/
public boolean isNoTerminologyChecks() {
return noTerminologyChecks;
}
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;
}
/**
* 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;
}
public void setStructureDefintion(StructureDefinition theStructureDefintion) {
myStructureDefintion = theStructureDefintion;
}
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
HapiWorkerContext workerContext = new HapiWorkerContext(theCtx, myValidationSupport);
WorkerContextWrapper wrappedWorkerContext = new WorkerContextWrapper(workerContext);
InstanceValidator v;
IEvaluationContext evaluationCtx = new NullEvaluationContext();
try {
v = new InstanceValidator(workerContext, evaluationCtx);
} catch (Exception e) {
throw new ConfigurationException(e);
}
InstanceValidator v;
FHIRPathEngine.IEvaluationContext evaluationCtx = new org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator.NullEvaluationContext();
try {
v = new InstanceValidator(wrappedWorkerContext, evaluationCtx);
} catch (Exception e) {
throw new ConfigurationException(e);
}
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
v.setResourceIdRule(IdStatus.OPTIONAL);
v.setNoTerminologyChecks(isNoTerminologyChecks());
v.setBestPracticeWarningLevel(getBestPracticeWarningLevel());
v.setAnyExtensionsAllowed(isAnyExtensionsAllowed());
v.setResourceIdRule(IdStatus.OPTIONAL);
v.setNoTerminologyChecks(isNoTerminologyChecks());
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
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);
}
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);
}
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);
String resourceName = determineResourceName(document);
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
if (profile != null) {
try {
v.validate(null, messages, document, profile.getUrl());
} 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);
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);
}
String resourceName = json.get("resourceType").getAsString();
StructureDefinition profile = findStructureDefinitionForResourceName(theCtx, resourceName);
if (profile != null) {
try {
v.validate(null, messages, json, profile.getUrl());
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else {
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
}
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;
}
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
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
}
@Override
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
}
public class NullEvaluationContext implements IEvaluationContext {
@Override
public TypeDetails checkFunction(Object theAppContext, String theFunctionName, List<TypeDetails> theParameters) throws PathEngineException {
return null;
}
private class WorkerContextWrapper implements IWorkerContext {
private final HapiWorkerContext myWrap;
private final VersionConvertor_30_40 myConverter;
private volatile List<org.hl7.fhir.r4.model.StructureDefinition> myAllStructures;
@Override
public List<Base> executeFunction(Object theAppContext, String theFunctionName, List<List<Base>> theParameters) {
return null;
}
public WorkerContextWrapper(HapiWorkerContext theWorkerContext) {
myWrap = theWorkerContext;
myConverter = new VersionConvertor_30_40();
}
@Override
public boolean log(String theArgument, List<Base> theFocus) {
return false;
}
@Override
public List<org.hl7.fhir.r4.model.MetadataResource> allConformanceResources() {
throw new UnsupportedOperationException();
}
@Override
public Base resolveConstant(Object theAppContext, String theName) throws PathEngineException {
return null;
}
@Override
public List<org.hl7.fhir.r4.model.StructureDefinition> allStructures() {
@Override
public TypeDetails resolveConstantType(Object theAppContext, String theName) throws PathEngineException {
return null;
}
List<org.hl7.fhir.r4.model.StructureDefinition> retVal = myAllStructures;
if (retVal == 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
public FunctionDetails resolveFunction(String theFunctionName) {
return null;
}
return retVal;
}
@Override
public Base resolveReference(Object theAppContext, String theUrl) {
return null;
}
@Override
public void cacheResource(org.hl7.fhir.r4.model.Resource res) throws FHIRException {
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());
}
public class NullEvaluationContext implements IEvaluationContext {
public static class NullEvaluationContext implements IEvaluationContext {
@Override
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
*/
@Test
@Ignore
public void testSearchJsonInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
@ -305,7 +304,6 @@ public class ResponseValidatingInterceptorDstu3Test {
ourLog.info("Response was:\n{}", responseContent);
Assert.assertEquals(422, status.getStatusLine().getStatusCode());
Assert.assertThat(status.toString(), Matchers.containsString("X-FHIR-Response-Validation"));
Assert.assertThat(responseContent, Matchers.containsString("<severity value=\"error\"/>"));
}
@ -384,7 +382,6 @@ public class ResponseValidatingInterceptorDstu3Test {
* Ignored until #264 is fixed
*/
@Test
@Ignore
public void testSearchXmlInvalidNoValidatorsSpecified() throws Exception {
Patient patient = new Patient();
patient.addIdentifier().setValue("002");
@ -403,7 +400,7 @@ public class ResponseValidatingInterceptorDstu3Test {
ourLog.info("Response was:\n{}", responseContent);
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

View File

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

View File

@ -506,12 +506,11 @@ public class FhirInstanceValidatorDstu3Test {
@Test
public void testValidateRawXmlResourceWithEmptyPrimitive() {
// @formatter:off
String input = "<Patient xmlns=\"http://hl7.org/fhir\"><name><given/></name></Patient>";
// @formatter:on
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(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);
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(""));
}

View File

@ -170,7 +170,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
ValidationResult errors = myVal.validateWithResult(qa);
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

View File

@ -287,7 +287,6 @@ public class ResourceValidatorDstu3Test {
* TODO: re-enable this
*/
@Test
@Ignore
public void testValidateQuestionnaireWithCanonicalUrl() {
String input = "{\n" +
" \"resourceType\": \"Questionnaire\",\n" +
@ -377,16 +376,15 @@ public class ResourceValidatorDstu3Test {
PatientProfileDstu3 myPatient = new PatientProfileDstu3();
myPatient.setId("1");
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.addName().setFamily("FamilyName");
myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));
IParser p = ourCtx.newJsonParser().setPrettyPrint(true);
String messageString = p.encodeResourceToString(myPatient);
ourLog.info(messageString);
// ourLog.info(messageString);
//@formatter:off
assertThat(messageString, stringContainsInOrder(
"meta",
"String Extension",
@ -399,7 +397,6 @@ public class ResourceValidatorDstu3Test {
"extension",
"meta"
)));
//@formatter:on
FhirValidator val = ourCtx.newValidator();
val.registerValidatorModule(new SchemaBaseValidator(ourCtx));
@ -426,7 +423,7 @@ public class ResourceValidatorDstu3Test {
PatientProfileDstu3 myPatient = new PatientProfileDstu3();
myPatient.setId("1");
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.addName().setFamily("FamilyName");
myPatient.addExtension().setUrl("http://foo.com/example").setValue(new StringType("String Extension"));

View File

@ -8,20 +8,9 @@
<body>
<release version="3.3.0" date="TBD">
<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
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
places, the database consumed more space than is needed to.
<![CDATA[<br/><br/>]]>
@ -32,6 +21,26 @@
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.
</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">
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