Validate contains change (#3209)

* wip

* The updated changes to the validator that were pulled in from the core dependencies are not backwards compat. Need to bump HAPI version.

* pointing to stable core version

* review feedback

* Add changelog

Co-authored-by: markiantorno <miantorno@pop-os.localdomain>
This commit is contained in:
Mark Iantorno 2021-12-06 11:25:00 -05:00 committed by GitHub
parent c4ec767b9b
commit d60d07b697
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 806 additions and 977 deletions

View File

@ -52,7 +52,7 @@ public class FhirValidator {
private static volatile Boolean ourPhPresentOnClasspath;
private final FhirContext myContext;
private List<IValidatorModule> myValidators = new ArrayList<>();
private IInterceptorBroadcaster myInterceptorBraodcaster;
private IInterceptorBroadcaster myInterceptorBroadcaster;
/**
* Constructor (this should not be called directly, but rather {@link FhirContext#newValidator()} should be called to obtain an instance of {@link FhirValidator})
@ -227,13 +227,13 @@ public class FhirValidator {
}
private ValidationResult invokeValidationCompletedHooks(IBaseResource theResourceParsed, String theResourceRaw, ValidationResult theValidationResult) {
if (myInterceptorBraodcaster != null) {
if (myInterceptorBraodcaster.hasHooks(Pointcut.VALIDATION_COMPLETED)) {
if (myInterceptorBroadcaster != null) {
if (myInterceptorBroadcaster.hasHooks(Pointcut.VALIDATION_COMPLETED)) {
HookParams params = new HookParams()
.add(IBaseResource.class, theResourceParsed)
.add(String.class, theResourceRaw)
.add(ValidationResult.class, theValidationResult);
Object newResult = myInterceptorBraodcaster.callHooksAndReturnObject(Pointcut.VALIDATION_COMPLETED, params);
Object newResult = myInterceptorBroadcaster.callHooksAndReturnObject(Pointcut.VALIDATION_COMPLETED, params);
if (newResult != null) {
theValidationResult = (ValidationResult) newResult;
}
@ -272,6 +272,6 @@ public class FhirValidator {
* @since 5.5.0
*/
public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBraodcaster) {
myInterceptorBraodcaster = theInterceptorBraodcaster;
myInterceptorBroadcaster = theInterceptorBraodcaster;
}
}

View File

@ -0,0 +1,16 @@
---
type: change
issue: 3205
title: "These changes are related to bumping the dependency on org.hl7.fhir.core to version 5.3.0.
The main updates that resulted in the majority of the changes in this PR were the addition of the interface IValidationPolicyAdvisor,
and the refactoring of some of the main validation classes (for cleaning purposes). Also, many of the error messages were
updated, so the resulting tests in HAPI had to be modified to reflect these message changes.
In regard to the new interface IValidationPolicyAdvisor, by implementing this interface and passing it through the
validation context in HAPI to the InstanceValidator in the core libraries, users can now control the behavior of the
validator when validating references, contained resources, and coded content. Previously, only references were controlled
in this way, and users controlled this by overriding the validation fetcher.
Test cases were added in the validation test cases repository to demonstrate it's functionality. In particular, the two
test cases contained-resource-bad-id, and contained-resource-bad-id-ignore. The definitions and expected outcomes of
these test cases are in the manifest.json in that repository."

View File

@ -1,3 +1,4 @@
# Changelog: 2020
<th:block th:insert="fragment_changelog.md :: changelog('2020', '2020')"/>

View File

@ -33,11 +33,12 @@ import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
import ca.uhn.fhir.jpa.validation.ValidatorPolicyAdvisor;
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
import ca.uhn.fhir.validation.IInstanceValidatorModule;
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@ -95,7 +96,8 @@ public abstract class BaseConfigDstu3Plus extends BaseConfig {
public IInstanceValidatorModule instanceValidator() {
FhirInstanceValidator val = new FhirInstanceValidator(validationSupportChain());
val.setValidatorResourceFetcher(jpaValidatorResourceFetcher());
val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
val.setValidatorPolicyAdvisor(jpaValidatorPolicyAdvisor());
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
val.setValidationSupport(validationSupportChain());
return val;
}
@ -106,6 +108,12 @@ public abstract class BaseConfigDstu3Plus extends BaseConfig {
return new ValidatorResourceFetcher();
}
@Bean
@Lazy
public ValidatorPolicyAdvisor jpaValidatorPolicyAdvisor() {
return new ValidatorPolicyAdvisor();
}
@Bean
public abstract ITermReadSvc terminologyService();

View File

@ -22,7 +22,7 @@ import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerVali
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.common.hapi.validation.validator.HapiToHl7OrgDstu2ValidatingSupportWrapper;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -91,7 +91,7 @@ public class BaseDstu2Config extends BaseConfig {
public IInstanceValidatorModule instanceValidator(ValidationSupportChain theValidationSupportChain) {
CachingValidationSupport cachingValidationSupport = new CachingValidationSupport(new HapiToHl7OrgDstu2ValidatingSupportWrapper(theValidationSupportChain));
FhirInstanceValidator retVal = new FhirInstanceValidator(cachingValidationSupport);
retVal.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
retVal.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
return retVal;
}

View File

@ -21,7 +21,7 @@ import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Disabled;
@ -121,15 +121,15 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
@AfterEach
public void after() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
val.setBestPracticeWarningLevel(org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel.Warning);
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
}
@Test
public void testValidateWithCanonicalReference() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel a = IResourceValidator.BestPracticeWarningLevel.Ignore;
val.setBestPracticeWarningLevel(a);
BestPracticeWarningLevel bestPracticeWarningLevel = BestPracticeWarningLevel.Ignore;
val.setBestPracticeWarningLevel(bestPracticeWarningLevel);
ValueSet vs = new ValueSet();
vs.setId("MYVS");

View File

@ -159,7 +159,15 @@ import org.hl7.fhir.r4.model.Substance;
import org.hl7.fhir.r4.model.Task;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -605,7 +613,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
protected void validate(IBaseResource theResource) {
FhirValidator validatorModule = myFhirCtx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myValidationSupport);
instanceValidator.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore);
instanceValidator.setBestPracticeWarningLevel(BestPracticeWarningLevel.Ignore);
instanceValidator.setValidatorPolicyAdvisor(new ValidationPolicyAdvisor());
validatorModule.registerValidatorModule(instanceValidator);
ValidationResult result = validatorModule.validateWithResult(theResource);
if (!result.isSuccessful()) {
@ -613,6 +622,23 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
}
}
public class ValidationPolicyAdvisor implements IValidationPolicyAdvisor {
@Override
public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) {
return ReferenceValidationPolicy.CHECK_VALID;
}
@Override
public CodedContentValidationPolicy policyForCodedContent(IResourceValidator iResourceValidator, Object o, String s, ElementDefinition elementDefinition, org.hl7.fhir.r5.model.StructureDefinition structureDefinition, BindingKind bindingKind, org.hl7.fhir.r5.model.ValueSet valueSet, List<String> list) {
return CodedContentValidationPolicy.CODE;
}
@Override
public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, String containerType, String containerId, Element.SpecialElement containingResourceType, String path, String url) {
return ContainedReferenceValidationPolicy.CHECK_VALID;
}
}
@SuppressWarnings("unchecked")
protected void upload(String theClasspath) throws IOException {
String resource = loadResource(theClasspath);

View File

@ -65,7 +65,8 @@ import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.UriType;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@ -109,7 +110,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
@AfterEach
public void after() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setMaximumExpansionSize(DaoConfig.DEFAULT_MAX_EXPANSION_SIZE);
@ -117,7 +118,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
BaseTermReadSvcImpl.setInvokeOnNextCallForUnitTest(null);
myValidationSettings.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.IGNORE);
myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.IGNORE);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(UnknownCodeSystemWarningValidationSupport.DEFAULT_SEVERITY);
@ -481,25 +482,25 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertEquals("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
// Valid code with no system
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertThat(encode(oo), containsString("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult)"));
// Valid code with wrong system
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertEquals("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
// Code that exists but isn't in the valueset
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertEquals("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
// Invalid code in built-in VS/CS
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
@ -531,7 +532,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
@Test
public void testValidateProfileTargetType_PolicyCheckValid() throws IOException {
myValidationSettings.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_VALID);
myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID);
StructureDefinition profile = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-vitalsigns-all-loinc.json");
myStructureDefinitionDao.create(profile, mySrd);
@ -599,7 +600,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
@Test
public void testValidateProfileTargetType_PolicyCheckExistsAndType() throws IOException {
myValidationSettings.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE);
myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE);
StructureDefinition profile = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-vitalsigns-all-loinc.json");
myStructureDefinitionDao.create(profile, mySrd);
@ -667,7 +668,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
@Test
public void testValidateProfileTargetType_PolicyCheckExists() throws IOException {
myValidationSettings.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_EXISTS);
myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_EXISTS);
StructureDefinition profile = loadResourceFromClasspath(StructureDefinition.class, "/r4/profile-vitalsigns-all-loinc.json");
myStructureDefinitionDao.create(profile, mySrd);
@ -860,7 +861,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
} catch (PreconditionFailedException e) {
outcome = (OperationOutcome) e.getOperationOutcome();
ourLog.info("Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
assertEquals("None of the codings provided are in the value set http://example.com/valueset (http://example.com/valueset), and a coding from this value set is required) (codes = http://example.com/foo-foo#some-code)", outcome.getIssueFirstRep().getDiagnostics());
assertEquals("None of the codings provided are in the value set 'MessageCategory' (http://example.com/valueset), and a coding from this value set is required) (codes = http://example.com/foo-foo#some-code)", outcome.getIssueFirstRep().getDiagnostics());
assertEquals(OperationOutcome.IssueSeverity.ERROR, outcome.getIssueFirstRep().getSeverity());
}
}
@ -933,25 +934,25 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertEquals("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
// Valid code with no system
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertThat(encode(oo), containsString("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult)"));
// Valid code with wrong system
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertEquals("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
// Code that exists but isn't in the valueset
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
oo = validateAndReturnOutcome(obs);
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
assertEquals("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
// Invalid code in built-in VS/CS
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
@ -1089,7 +1090,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
ourLog.info(myFhirCtx.newJsonParser().encodeResourceToString(allergy));
OperationOutcome oo = validateAndReturnOutcome(allergy);
assertThat(encode(oo), containsString("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/allergyintolerance-clinical"));
assertThat(encode(oo), containsString("None of the codings provided are in the value set 'AllergyIntolerance Clinical Status Codes' (http://hl7.org/fhir/ValueSet/allergyintolerance-clinical|4.0.1)"));
}
@SuppressWarnings("unchecked")
@ -1199,7 +1200,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
// It would be ok for this to produce 0 issues, or just an information message too
assertEquals(1, OperationOutcomeUtil.getIssueCount(myFhirCtx, oo));
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://foo#bar)", OperationOutcomeUtil.getFirstIssueDetails(myFhirCtx, oo));
assertEquals("None of the codings provided are in the value set 'IdentifierType' (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://foo#bar)", OperationOutcomeUtil.getFirstIssueDetails(myFhirCtx, oo));
}
@ -1321,7 +1322,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
@Test
public void testValidateWithCanonicalReference() {
FhirInstanceValidator val = AopTestUtils.getTargetObject(myValidatorModule);
val.setBestPracticeWarningLevel(org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel.Ignore);
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Ignore);
ValueSet vs = new ValueSet();
vs.setId("MYVS");
@ -1686,7 +1687,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
} catch (PreconditionFailedException e) {
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
assertThat(oo.getIssueFirstRep().getDiagnostics(), containsString("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/condition-clinical"));
assertThat(oo.getIssueFirstRep().getDiagnostics(),
containsString("None of the codings provided are in the value set 'Condition Clinical Status Codes' (http://hl7.org/fhir/ValueSet/condition-clinical|4.0.1), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/condition-clinical/wrong-system#notrealcode)"));
}
}

View File

@ -133,7 +133,7 @@ import org.hl7.fhir.r5.model.Substance;
import org.hl7.fhir.r5.model.Task;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -515,7 +515,7 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil
protected void validate(IBaseResource theResource) {
FhirValidator validatorModule = myFhirCtx.newValidator();
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myValidationSupport);
instanceValidator.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore);
instanceValidator.setBestPracticeWarningLevel(BestPracticeWarningLevel.Ignore);
validatorModule.registerValidatorModule(instanceValidator);
ValidationResult result = validatorModule.validateWithResult(theResource);
if (!result.isSuccessful()) {

View File

@ -14,7 +14,7 @@ import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -370,7 +370,7 @@ public class RepositoryValidatingInterceptorR4Test extends BaseJpaR4Test {
.requireAtLeastProfile("http://hl7.org/fhir/StructureDefinition/Observation")
.and()
.requireValidationToDeclaredProfiles()
.withBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore)
.withBestPracticeWarningLevel(BestPracticeWarningLevel.Ignore)
.build();
myValInterceptor.setRules(rules);

View File

@ -15,7 +15,7 @@ import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -118,7 +118,7 @@ public class ValidationMessageSuppressingInterceptorTest extends BaseResourcePro
List<IRepositoryValidatingRule> rules = myApplicationContext.getBean(RepositoryValidatingRuleBuilder.REPOSITORY_VALIDATING_RULE_BUILDER, RepositoryValidatingRuleBuilder.class)
.forResourcesOfType("Encounter")
.requireValidationToDeclaredProfiles().withBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Ignore)
.requireValidationToDeclaredProfiles().withBestPracticeWarningLevel(BestPracticeWarningLevel.Ignore)
.build();
RepositoryValidatingInterceptor repositoryValidatingInterceptor = new RepositoryValidatingInterceptor();

View File

@ -22,7 +22,7 @@ import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
import org.hibernate.search.engine.cfg.BackendSettings;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
@ -96,7 +96,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
@Bean
public ValidationSettings validationSettings() {
ValidationSettings retVal = super.validationSettings();
retVal.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_VALID);
retVal.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID);
return retVal;
}

View File

@ -21,7 +21,7 @@ import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
import org.hibernate.search.engine.cfg.BackendSettings;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -93,7 +93,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
@Bean
public ValidationSettings validationSettings() {
ValidationSettings retVal = super.validationSettings();
retVal.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_VALID);
retVal.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID);
return retVal;
}

View File

@ -18,7 +18,7 @@ import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings;
import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
import org.hibernate.search.engine.cfg.BackendSettings;
import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -90,7 +90,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
@Bean
public ValidationSettings validationSettings() {
ValidationSettings retVal = super.validationSettings();
retVal.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_VALID);
retVal.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID);
return retVal;
}

View File

@ -21,7 +21,7 @@ import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
import org.hibernate.search.engine.cfg.BackendSettings;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.hl7.fhir.dstu2.model.Subscription;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -92,7 +92,7 @@ public class TestR5Config extends BaseJavaConfigR5 {
@Bean
public ValidationSettings validationSettings() {
ValidationSettings retVal = super.validationSettings();
retVal.setLocalReferenceValidationDefaultPolicy(IResourceValidator.ReferenceValidationPolicy.CHECK_VALID);
retVal.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID);
return retVal;
}

View File

@ -23,12 +23,13 @@ package ca.uhn.fhir.jpa.interceptor.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.validation.ValidatorPolicyAdvisor;
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import org.apache.commons.lang3.Validate;
import org.apache.commons.text.WordUtils;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
@ -57,6 +58,8 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
@Autowired
private ValidatorResourceFetcher myValidatorResourceFetcher;
@Autowired
private ValidatorPolicyAdvisor myValidationPolicyAdvisor;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
/**
@ -175,7 +178,8 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
* @see ValidationResultEnrichingInterceptor
*/
public FinalizedRequireValidationRule requireValidationToDeclaredProfiles() {
RequireValidationRule rule = new RequireValidationRule(myFhirContext, myType, myValidationSupport, myValidatorResourceFetcher, myInterceptorBroadcaster);
RequireValidationRule rule = new RequireValidationRule(myFhirContext, myType, myValidationSupport,
myValidatorResourceFetcher, myValidationPolicyAdvisor, myInterceptorBroadcaster);
myRules.add(rule);
return new FinalizedRequireValidationRule(rule);
}
@ -205,9 +209,9 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
*/
@Nonnull
public FinalizedRequireValidationRule withBestPracticeWarningLevel(String theBestPracticeWarningLevel) {
IResourceValidator.BestPracticeWarningLevel level = null;
BestPracticeWarningLevel level = null;
if (isNotBlank(theBestPracticeWarningLevel)) {
level = IResourceValidator.BestPracticeWarningLevel.valueOf(WordUtils.capitalize(theBestPracticeWarningLevel.toLowerCase()));
level = BestPracticeWarningLevel.valueOf(WordUtils.capitalize(theBestPracticeWarningLevel.toLowerCase()));
}
return withBestPracticeWarningLevel(level);
}
@ -215,13 +219,13 @@ public final class RepositoryValidatingRuleBuilder implements IRuleRoot {
/**
* Sets the "Best Practice Warning Level", which is the severity at which any "best practices" that
* are specified in the FHIR specification will be added to the validation outcome. Set to
* {@link org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel#Error} to
* {@link org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel#Error} to
* cause any best practice notices to result in a validation failure.
* Set to {@link org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel#Ignore}
* Set to {@link org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel#Ignore}
* to not include any best practice notifications.
*/
@Nonnull
public FinalizedRequireValidationRule withBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel bestPracticeWarningLevel) {
public FinalizedRequireValidationRule withBestPracticeWarningLevel(BestPracticeWarningLevel bestPracticeWarningLevel) {
myRule.setBestPracticeWarningLevel(bestPracticeWarningLevel);
return this;
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.interceptor.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.validation.ValidatorPolicyAdvisor;
import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor;
@ -36,7 +37,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import javax.annotation.Nonnull;
import java.util.ArrayList;
@ -49,17 +50,23 @@ class RequireValidationRule extends BaseTypedRule {
private ResultSeverityEnum myRejectOnSeverity = ResultSeverityEnum.ERROR;
private List<TagOnSeverity> myTagOnSeverity = Collections.emptyList();
public RequireValidationRule(FhirContext theFhirContext, String theType, IValidationSupport theValidationSupport, ValidatorResourceFetcher theValidatorResourceFetcher, IInterceptorBroadcaster theInterceptorBroadcaster) {
public RequireValidationRule(FhirContext theFhirContext,
String theType,
IValidationSupport theValidationSupport,
ValidatorResourceFetcher theValidatorResourceFetcher,
ValidatorPolicyAdvisor theValidationPolicyAdvisor,
IInterceptorBroadcaster theInterceptorBroadcaster) {
super(theFhirContext, theType);
myInterceptorBroadcaster = theInterceptorBroadcaster;
myValidator = new FhirInstanceValidator(theValidationSupport);
myValidator.setValidatorResourceFetcher(theValidatorResourceFetcher);
myValidator.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
myValidator.setValidatorPolicyAdvisor(theValidationPolicyAdvisor);
myValidator.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
}
void setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel theBestPracticeWarningLevel) {
void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
myValidator.setBestPracticeWarningLevel(theBestPracticeWarningLevel);
}

View File

@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.validation;
* #L%
*/
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.thymeleaf.util.Validate;
import javax.annotation.Nonnull;
public class ValidationSettings {
private IResourceValidator.ReferenceValidationPolicy myLocalReferenceValidationDefaultPolicy = IResourceValidator.ReferenceValidationPolicy.IGNORE;
private ReferenceValidationPolicy myLocalReferenceValidationDefaultPolicy = ReferenceValidationPolicy.IGNORE;
/**
* Supplies a default policy for validating local references. Default is {@literal IResourceValidator.ReferenceValidationPolicy.IGNORE}.
@ -40,7 +40,7 @@ public class ValidationSettings {
* @since 5.1.0
*/
@Nonnull
public IResourceValidator.ReferenceValidationPolicy getLocalReferenceValidationDefaultPolicy() {
public ReferenceValidationPolicy getLocalReferenceValidationDefaultPolicy() {
return myLocalReferenceValidationDefaultPolicy;
}
@ -54,7 +54,7 @@ public class ValidationSettings {
*
* @since 5.1.0
*/
public void setLocalReferenceValidationDefaultPolicy(@Nonnull IResourceValidator.ReferenceValidationPolicy theLocalReferenceValidationDefaultPolicy) {
public void setLocalReferenceValidationDefaultPolicy(@Nonnull ReferenceValidationPolicy theLocalReferenceValidationDefaultPolicy) {
Validate.notNull(theLocalReferenceValidationDefaultPolicy, "theLocalReferenceValidationDefaultPolicy must not be null");
myLocalReferenceValidationDefaultPolicy = theLocalReferenceValidationDefaultPolicy;
}

View File

@ -0,0 +1,48 @@
package ca.uhn.fhir.jpa.validation;
import ca.uhn.fhir.context.FhirContext;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class ValidatorPolicyAdvisor implements IValidationPolicyAdvisor {
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorPolicyAdvisor.class);
@Autowired
private ValidationSettings myValidationSettings;
@Autowired
private FhirContext myFhirContext;
@Override
public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) {
int slashIdx = url.indexOf("/");
if (slashIdx > 0 && myFhirContext.getResourceTypes().contains(url.substring(0, slashIdx))) {
return myValidationSettings.getLocalReferenceValidationDefaultPolicy();
}
return ReferenceValidationPolicy.IGNORE;
}
@Override
public CodedContentValidationPolicy policyForCodedContent(IResourceValidator iResourceValidator, Object o, String s, ElementDefinition elementDefinition, StructureDefinition structureDefinition, BindingKind bindingKind, ValueSet valueSet, List<String> list) {
return CodedContentValidationPolicy.CODE;
}
@Override
public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, String containerType, String containerId, Element.SpecialElement containingResourceType, String path, String url) {
return ContainedReferenceValidationPolicy.CHECK_VALID;
}
}

View File

@ -35,7 +35,8 @@ import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.JsonParser;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -46,23 +47,21 @@ import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.Locale;
public class ValidatorResourceFetcher implements IResourceValidator.IValidatorResourceFetcher {
public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class);
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private ValidationSettings myValidationSettings;
@Autowired
private FhirContext myFhirContext;
@Autowired
private IValidationSupport myValidationSupport;
private VersionSpecificWorkerContextWrapper myVersionSpecificCOntextWrapper;
private VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper;
@PostConstruct
public void start() {
myVersionSpecificCOntextWrapper = VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myValidationSupport);
myVersionSpecificContextWrapper = VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myValidationSupport);
}
@Override
@ -79,23 +78,12 @@ public class ValidatorResourceFetcher implements IResourceValidator.IValidatorRe
}
try {
return new JsonParser(myVersionSpecificCOntextWrapper).parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType);
return new JsonParser(myVersionSpecificContextWrapper).parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType);
} catch (Exception e) {
throw new FHIRException(e);
}
}
@Override
public IResourceValidator.ReferenceValidationPolicy validationPolicy(IResourceValidator iResourceValidator,
Object appContext, String path, String url) {
int slashIdx = url.indexOf("/");
if (slashIdx > 0 && myFhirContext.getResourceTypes().contains(url.substring(0, slashIdx))) {
return myValidationSettings.getLocalReferenceValidationDefaultPolicy();
}
return IResourceValidator.ReferenceValidationPolicy.IGNORE;
}
@Override
public boolean resolveURL(IResourceValidator iResourceValidator, Object o, String s, String s1, String s2) throws IOException, FHIRException {
return true;
@ -107,7 +95,7 @@ public class ValidatorResourceFetcher implements IResourceValidator.IValidatorRe
}
@Override
public IResourceValidator.IValidatorResourceFetcher setLocale(Locale locale) {
public IValidatorResourceFetcher setLocale(Locale locale) {
// ignore
return this;
}

View File

@ -28,7 +28,7 @@ import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.terminologies.ValueSetExpander;
import org.hl7.fhir.dstu3.utils.INarrativeGenerator;
import org.hl7.fhir.dstu3.utils.IResourceValidator;
import org.hl7.fhir.dstu3.utils.validation.IResourceValidator;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.i18n.I18nBase;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;

View File

@ -31,7 +31,7 @@ import org.hl7.fhir.r4.model.StructureMap;
import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.r4.utils.IResourceValidator;
import org.hl7.fhir.r4.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.i18n.I18nBase;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;

View File

@ -31,7 +31,7 @@ import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.i18n.I18nBase;
@ -93,6 +93,15 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
}
}
@Override
public CodeSystem fetchCodeSystem(String theSystem, String version) {
if (myValidationSupport == null) {
return null;
} else {
return (CodeSystem) myValidationSupport.fetchCodeSystem(theSystem);
}
}
@Override
public List<ConceptMap> findMapsForSource(String theUrl) {
throw new UnsupportedOperationException();
@ -170,7 +179,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
String system = theCode.getSystem();
String code = theCode.getCode();
String display = theCode.getDisplay();
return validateCode(theOptions, system, code, display, theVs);
return validateCode(theOptions, system, null, code, display, theVs);
}
@Override
@ -179,8 +188,15 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theCode, String theDisplay) {
IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null);
public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet theValueSet, boolean cacheOk, boolean heiarchical, boolean incompleteOk) {
return null;
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion,
String theCode, String theDisplay) {
IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport),
convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, null);
if (result == null) {
return null;
}
@ -193,13 +209,15 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theCode, String theDisplay, ValueSet theVs) {
public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theVersion,
String theCode, String theDisplay, ValueSet theVs) {
IValidationSupport.CodeValidationResult outcome;
if (isNotBlank(theVs.getUrl())) {
outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs.getUrl());
outcome = myValidationSupport.validateCode(new ValidationSupportContext(myValidationSupport),
convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs.getUrl());
} else {
outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs);
outcome = myValidationSupport.validateCodeInValueSet(new ValidationSupportContext(myValidationSupport),
convertConceptValidationOptions(theOptions), theSystem, theCode, theDisplay, theVs);
}
if (outcome != null && outcome.isOk()) {
@ -209,12 +227,13 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
return new ValidationResult(theSystem, definition);
}
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + Constants.codeSystemWithDefaultDescription(theSystem) + "]");
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" +
Constants.codeSystemWithDefaultDescription(theSystem) + "]");
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) {
return validateCode(theOptions, null, code, null, vs);
return validateCode(theOptions, null, null, code, null, vs);
}
@Override

View File

@ -0,0 +1,40 @@
package org.hl7.fhir.common.hapi.validation.validator;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import java.util.List;
/**
* Implementation of the base {@link IValidationPolicyAdvisor}. This is used as the default for all validation operations
* done within the core libraries, as without a default, it will ignore some validation operations.
*/
public class FhirDefaultPolicyAdvisor implements IValidationPolicyAdvisor {
@Override
public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path,
String url) {
return ReferenceValidationPolicy.IGNORE;
}
@Override
public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, String containerType, String containerId,
Element.SpecialElement containingResourceType, String path, String url) {
return ContainedReferenceValidationPolicy.CHECK_VALID;
}
@Override
public CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator, Object appContext, String stackPath,
ElementDefinition definition, StructureDefinition structure, BindingKind kind,
ValueSet valueSet, List<String> systems) {
return CodedContentValidationPolicy.CODE;
}
}

View File

@ -13,8 +13,9 @@ import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import javax.annotation.Nonnull;
@ -35,7 +36,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta
private boolean errorForUnknownProfiles = true;
private boolean assumeValidRestReferences;
private List<String> myExtensionDomains = Collections.emptyList();
private IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher;
private IValidatorResourceFetcher validatorResourceFetcher;
private IValidationPolicyAdvisor validatorPolicyAdvisor;
/**
* Constructor
@ -227,6 +229,8 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta
.setBestPracticeWarningLevel(getBestPracticeWarningLevel())
.setErrorForUnknownProfiles(isErrorForUnknownProfiles())
.setExtensionDomains(getExtensionDomains())
.setValidatorResourceFetcher(validatorResourceFetcher)
.setValidationPolicyAdvisor(validatorPolicyAdvisor)
.setNoTerminologyChecks(isNoTerminologyChecks())
.setNoExtensibleWarnings(isNoExtensibleWarnings())
.setNoBindingMsgSuppressed(isNoBindingMsgSuppressed())
@ -245,11 +249,19 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta
return wrappedWorkerContext;
}
public IResourceValidator.IValidatorResourceFetcher getValidatorResourceFetcher() {
public IValidationPolicyAdvisor getValidatorPolicyAdvisor() {
return validatorPolicyAdvisor;
}
public void setValidatorPolicyAdvisor(IValidationPolicyAdvisor validatorPolicyAdvisor) {
this.validatorPolicyAdvisor = validatorPolicyAdvisor;
}
public IValidatorResourceFetcher getValidatorResourceFetcher() {
return validatorResourceFetcher;
}
public void setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) {
public void setValidatorResourceFetcher(IValidatorResourceFetcher validatorResourceFetcher) {
this.validatorResourceFetcher = validatorResourceFetcher;
}

View File

@ -16,8 +16,11 @@ import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.IdStatus;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.instance.InstanceValidator;
import org.slf4j.Logger;
@ -34,7 +37,7 @@ import java.util.List;
class ValidatorWrapper {
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorWrapper.class);
private IResourceValidator.BestPracticeWarningLevel myBestPracticeWarningLevel;
private BestPracticeWarningLevel myBestPracticeWarningLevel;
private boolean myAnyExtensionsAllowed;
private boolean myErrorForUnknownProfiles;
private boolean myNoTerminologyChecks;
@ -42,7 +45,8 @@ class ValidatorWrapper {
private boolean myNoExtensibleWarnings;
private boolean myNoBindingMsgSuppressed;
private Collection<? extends String> myExtensionDomains;
private IResourceValidator.IValidatorResourceFetcher myValidatorResourceFetcher;
private IValidatorResourceFetcher myValidatorResourceFetcher;
private IValidationPolicyAdvisor myValidationPolicyAdvisor;
/**
* Constructor
@ -60,7 +64,7 @@ class ValidatorWrapper {
return this;
}
public ValidatorWrapper setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel theBestPracticeWarningLevel) {
public ValidatorWrapper setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
myBestPracticeWarningLevel = theBestPracticeWarningLevel;
return this;
}
@ -95,8 +99,12 @@ class ValidatorWrapper {
return this;
}
public ValidatorWrapper setValidationPolicyAdvisor(IValidationPolicyAdvisor validationPolicyAdvisor) {
this.myValidationPolicyAdvisor = validationPolicyAdvisor;
return this;
}
public ValidatorWrapper setValidatorResourceFetcher(IResourceValidator.IValidatorResourceFetcher validatorResourceFetcher) {
public ValidatorWrapper setValidatorResourceFetcher(IValidatorResourceFetcher validatorResourceFetcher) {
this.myValidatorResourceFetcher = validatorResourceFetcher;
return this;
}
@ -114,11 +122,12 @@ class ValidatorWrapper {
v.setAssumeValidRestReferences(isAssumeValidRestReferences());
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
v.setAnyExtensionsAllowed(myAnyExtensionsAllowed);
v.setResourceIdRule(IResourceValidator.IdStatus.OPTIONAL);
v.setResourceIdRule(IdStatus.OPTIONAL);
v.setNoTerminologyChecks(myNoTerminologyChecks);
v.setErrorForUnknownProfiles(myErrorForUnknownProfiles);
v.getExtensionDomains().addAll(myExtensionDomains);
v.setFetcher(myValidatorResourceFetcher);
v.setPolicyAdvisor(myValidationPolicyAdvisor);
v.setNoExtensibleWarnings(myNoExtensibleWarnings);
v.setNoBindingMsgSuppressed(myNoBindingMsgSuppressed);
v.setAllowXsiLocation(true);

View File

@ -24,12 +24,13 @@ import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.ParserType;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.i18n.I18nBase;
@ -329,6 +330,19 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
}
}
@Override
public CodeSystem fetchCodeSystem(String system, String verison) {
IBaseResource fetched = myValidationSupportContext.getRootValidationSupport().fetchCodeSystem(system);
if (fetched == null) {
return null;
}
try {
return (org.hl7.fhir.r5.model.CodeSystem) myModelConverter.toCanonical(fetched);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public <T extends Resource> T fetchResource(Class<T> class_, String uri) {
@ -514,14 +528,18 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String system, String code, String display) {
ConceptValidationOptions validationOptions = convertConceptValidationOptions(theOptions);
public ValueSetExpander.ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical, boolean incompleteOk) {
return null;
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String system, String version, String code, String display) {
ConceptValidationOptions validationOptions = convertConceptValidationOptions(theOptions);
return doValidation(null, validationOptions, system, code, display);
}
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String theCode, String display, org.hl7.fhir.r5.model.ValueSet theValueSet) {
public ValidationResult validateCode(ValidationOptions theOptions, String theSystem, String version, String theCode, String display, ValueSet theValueSet) {
IBaseResource convertedVs = null;
try {

View File

@ -0,0 +1,10 @@
package org.hl7.fhir.common.hapi.validation.validator;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_14_50;
public class VersionTypeAdvisorDstu21 extends BaseAdvisor_14_50 {
@Override
public boolean failFastOnNullOrUnknownEntry() {
return false;
}
}

View File

@ -7,7 +7,7 @@ import org.hl7.fhir.r5.model.Resource;
public class VersionTypeConverterDstu21 implements VersionSpecificWorkerContextWrapper.IVersionTypeConverter {
@Override
public Resource toCanonical(IBaseResource theNonCanonical) {
return VersionConvertorFactory_14_50.convertResource((org.hl7.fhir.dstu2016may.model.Resource) theNonCanonical);
return VersionConvertorFactory_14_50.convertResource((org.hl7.fhir.dstu2016may.model.Resource) theNonCanonical, new VersionTypeAdvisorDstu21());
}
@Override

View File

@ -53,7 +53,9 @@ import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.dstu3.utils.FHIRPathEngine;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
@ -595,7 +597,8 @@ public class FhirInstanceValidatorDstu3Test {
ValidationResult output = myVal.validateWithResult(input);
List<SingleValidationMessage> issues = logResultsAndReturnNonInformationalOnes(output);
assertThat(issues.toString(), containsString("None of the codings provided are in the value set http://phr.kanta.fi/ValueSet/fiphr-vs-medicationcontext"));
assertThat(issues.stream().map(SingleValidationMessage::getMessage).collect(Collectors.toList()).toString(),
containsString("None of the codings provided are in the value set 'Value Set Finnish PHR Medication Context' (http://phr.kanta.fi/ValueSet/fiphr-vs-medicationcontext), and a coding from this value set is required) (codes = http://phr.kanta.fi/fiphr-cs-medicationcontext#13)"));
}
@Test
@ -665,6 +668,9 @@ public class FhirInstanceValidatorDstu3Test {
return false;
} else if (t.getMessage().contains("The valueSet reference http://www.rfc-editor.org/bcp/bcp13.txt on element")) {
return false;
} else if (t.getMessage().contains("The Unicode sequence has unterminated bi-di control characters")) {
// Some DSTU3 structures conain bi-di control characters, and a check for this was added recently.
return false;
} else {
return true;
}
@ -1180,7 +1186,7 @@ public class FhirInstanceValidatorDstu3Test {
ValidationResult output = myVal.validateWithResult(input);
logResultsAndReturnAll(output);
assertEquals(
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')",
"The value provided ('notvalidcode') is not in the value set 'ObservationStatus' (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')",
output.getMessages().get(0).getMessage());
}
@ -1277,7 +1283,7 @@ public class FhirInstanceValidatorDstu3Test {
assertEquals(1, all.size());
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
assertEquals(
"None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
"None of the codings provided are in the value set 'Identifier Type Codes' (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
all.get(0).getMessage());
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
@ -1310,14 +1316,18 @@ public class FhirInstanceValidatorDstu3Test {
public void testInvocationOfValidatorFetcher() throws IOException {
String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8);
IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class);
when(resourceFetcher.validationPolicy(any(), any(), any(), any())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
myInstanceVal.setValidatorResourceFetcher(resourceFetcher);
IValidationPolicyAdvisor policyAdvisor = mock(IValidationPolicyAdvisor.class);
IValidatorResourceFetcher fetcher = mock(IValidatorResourceFetcher.class);
when(policyAdvisor.policyForReference(any(), any(), any(), any())).thenReturn(ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
when(policyAdvisor.policyForReference(any(), any(), any(), any())).thenReturn(ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
myInstanceVal.setValidatorResourceFetcher(fetcher);
myInstanceVal.setValidatorPolicyAdvisor(policyAdvisor);
myVal.validateWithResult(input);
verify(resourceFetcher, times(3)).resolveURL(any(), any(), anyString(), anyString(), anyString());
verify(resourceFetcher, times(4)).validationPolicy(any(), any(), anyString(), anyString());
verify(resourceFetcher, times(4)).fetch(any(), any(), anyString());
verify(fetcher, times(3)).resolveURL(any(), any(), anyString(), anyString(), anyString());
verify(policyAdvisor, times(4)).policyForReference(any(), any(), anyString(), anyString());
verify(fetcher, times(4)).fetch(any(), any(), anyString());
}
@Test

View File

@ -165,7 +165,7 @@ public class ResourceValidatorDstu3Test {
ValidationResult output = val.validateWithResult(p);
List<SingleValidationMessage> all = logResultsAndReturnNonInformationalOnes(output);
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/marital-status (http://hl7.org/fhir/ValueSet/marital-status), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
assertEquals("None of the codings provided are in the value set 'Marital Status Codes' (http://hl7.org/fhir/ValueSet/marital-status), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity());
}

View File

@ -60,7 +60,11 @@ import org.hl7.fhir.r4.model.ValueSet;
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
import org.hl7.fhir.r4.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.jupiter.api.AfterAll;
@ -68,6 +72,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.MockingDetails;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@ -464,7 +470,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
// With BPs enabled
val = ourCtx.newValidator();
instanceModule = new FhirInstanceValidator(myValidationSupport);
IResourceValidator.BestPracticeWarningLevel level = IResourceValidator.BestPracticeWarningLevel.Error;
BestPracticeWarningLevel level = BestPracticeWarningLevel.Error;
instanceModule.setBestPracticeWarningLevel(level);
val.registerValidatorModule(instanceModule);
result = val.validateWithResult(input);
@ -628,7 +634,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
ValidationResult output = myVal.validateWithResult(input);
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
assertEquals(1, errors.size());
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/report-codes (http://hl7.org/fhir/ValueSet/report-codes), and a coding is recommended to come from this value set) (codes = http://loinc.org#1-8)", errors.get(0).getMessage());
assertEquals("None of the codings provided are in the value set 'LOINC Diagnostic Report Codes' (http://hl7.org/fhir/ValueSet/report-codes), and a coding is recommended to come from this value set) (codes = http://loinc.org#1-8)", errors.get(0).getMessage());
}
@Test
@ -1200,7 +1206,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
ValidationResult output = myVal.validateWithResult(input);
logResultsAndReturnAll(output);
assertEquals(
"The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status|4.0.1 (http://hl7.org/fhir/ValueSet/observation-status), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')",
"The value provided ('notvalidcode') is not in the value set 'ObservationStatus' (http://hl7.org/fhir/ValueSet/observation-status|4.0.1), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')",
output.getMessages().get(0).getMessage());
}
@ -1332,7 +1338,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
List<SingleValidationMessage> all = logResultsAndReturnAll(output);
assertEquals(1, all.size());
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
assertThat(all.get(0).getMessage(), containsString("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type"));
assertThat(all.get(0).getMessage(), containsString("None of the codings provided are in the value set 'IdentifierType' (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)"));
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
}
@ -1353,7 +1359,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
all = logResultsAndReturnNonInformationalOnes(output);
assertEquals(2, all.size());
assertThat(all.get(0).getMessage(), containsString("Validation failed for 'http://unitsofmeasure.org#Heck'"));
assertThat(all.get(1).getMessage(), containsString("The value provided ('Heck') is not in the value set http://hl7.org/fhir/ValueSet/ucum-bodytemp"));
assertThat(all.get(1).getMessage(), containsString("The value provided ('Heck') is not in the value set 'Body Temperature Units' (http://hl7.org/fhir/ValueSet/ucum-bodytemp|4.0.1), and a code is required from this value set) (error message = Failed to expand ValueSet 'http://hl7.org/fhir/ValueSet/ucum-bodytemp' (in-memory). Could not validate code null#Heck. Error was: Unable to expand ValueSet because CodeSystem could not be found: http://unitsofmeasure.org)"));
}
@ -1396,14 +1402,17 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
String encoded = loadResource("/r4/r4-caredove-bundle.json");
IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class);
when(resourceFetcher.validationPolicy(any(), any(), any(), any())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
IValidatorResourceFetcher resourceFetcher = mock(IValidatorResourceFetcher.class);
IValidationPolicyAdvisor policyAdvisor = mock(IValidationPolicyAdvisor.class);
when(policyAdvisor.policyForReference(any(), any(), any(), any())).thenReturn(ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
when(policyAdvisor.policyForContained(any(), any(), any(), any(), any(), any(), any())).thenReturn(ContainedReferenceValidationPolicy.CHECK_TYPE);
myInstanceVal.setValidatorResourceFetcher(resourceFetcher);
myInstanceVal.setValidatorPolicyAdvisor(policyAdvisor);
myVal.validateWithResult(encoded);
verify(resourceFetcher, times(15)).resolveURL(any(), any(), anyString(), anyString(), anyString());
verify(resourceFetcher, times(12)).validationPolicy(any(), any(), anyString(), anyString());
verify(resourceFetcher, times(12)).fetch(any(), any(), anyString());
verify(resourceFetcher, times(12)).resolveURL(any(), any(), anyString(), anyString(), anyString());
verify(policyAdvisor, times(12)).policyForContained(any(), any(), any(), any(), any(), any(), any());
}
@Test
@ -1451,7 +1460,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
ValidationResult output = myVal.validateWithResult(input);
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
assertEquals(1, errors.size(), errors.toString());
assertThat(errors.get(0).getMessage(), containsString("The value provided ('BLAH') is not in the value set http://hl7.org/fhir/ValueSet/currencies"));
assertThat(errors.get(0).getMessage(), containsString("The value provided ('BLAH') is not in the value set 'CurrencyCode' (http://hl7.org/fhir/ValueSet/currencies|4.0.1), and a code is required from this value set) (error message = Unknown code 'BLAH' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/currencies')"));
}

View File

@ -43,7 +43,11 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
@ -304,7 +308,7 @@ public class FhirInstanceValidatorR5Test {
// With BPs enabled
val = ourCtx.newValidator();
instanceModule = new FhirInstanceValidator(myValidationSupport);
IResourceValidator.BestPracticeWarningLevel level = IResourceValidator.BestPracticeWarningLevel.Error;
BestPracticeWarningLevel level = BestPracticeWarningLevel.Error;
instanceModule.setBestPracticeWarningLevel(level);
val.registerValidatorModule(instanceModule);
result = val.validateWithResult(input);
@ -446,14 +450,17 @@ public class FhirInstanceValidatorR5Test {
String input = IOUtils.toString(FhirInstanceValidator.class.getResourceAsStream("/vitals.json"), Charsets.UTF_8);
IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class);
when(resourceFetcher.validationPolicy(any(), any(), any(), any())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
IValidatorResourceFetcher resourceFetcher = mock(IValidatorResourceFetcher.class);
IValidationPolicyAdvisor policyAdvisor = mock(IValidationPolicyAdvisor.class);
when(policyAdvisor.policyForReference(any(), any(), any(), any())).thenReturn(ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
myInstanceVal.setValidatorResourceFetcher(resourceFetcher);
myInstanceVal.setValidatorPolicyAdvisor(policyAdvisor);
myVal.validateWithResult(input);
verify(resourceFetcher, times(13)).resolveURL(any(), any(), anyString(), anyString(), anyString());
verify(resourceFetcher, times(4)).validationPolicy(any(), any(), anyString(), anyString());
verify(resourceFetcher, times(3)).fetch(any(), any(), anyString());
//verify(resourceFetcher, times(13)).resolveURL(any(), any(), anyString(), anyString(), anyString());
verify(policyAdvisor, times(4)).policyForReference(any(), any(), anyString(), anyString());
//verify(resourceFetcher, times(3)).fetch(any(), any(), anyString());
}
@Test
@ -862,7 +869,7 @@ public class FhirInstanceValidatorR5Test {
logResultsAndReturnAll(output);
assertThat(
output.getMessages().get(0).getMessage(),
containsString("The value provided ('notvalidcode') is not in the value set http://hl7.org/fhir/ValueSet/observation-status")
containsString("The value provided ('notvalidcode') is not in the value set 'ObservationStatus' (http://hl7.org/fhir/ValueSet/observation-status|4.6.0), and a code is required from this value set) (error message = Unknown code 'notvalidcode' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-status')")
);
}
@ -965,7 +972,7 @@ public class FhirInstanceValidatorR5Test {
assertEquals(1, all.size());
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
assertEquals(
"None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
"None of the codings provided are in the value set 'IdentifierType' (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
all.get(0).getMessage());
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());

View File

@ -1,845 +1,440 @@
{
"resourceType": "Bundle",
"type": "transaction",
"timestamp": "2018-03-09T15:21:51.2112Z",
"entry": [
{
"resource": {
"resourceType": "ServiceRequest",
"id": 1,
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"status": "active",
"intent": "proposal",
"priority": "routine",
"subject": {
"reference": "Patient/1"
},
"authoredOn": "2018-03-09T15:21:51Z",
"requester": {
"reference": "PractitionerRole/1"
},
"performer": {
"reference": "https://www.caredove.com/FHIR3/HealthcareService/8654"
},
"reasonCode": [
{
"text": "Reason for referral narrative goes here"
}
],
"supportingInfo": [
{
"reference": "DocumentReference/1"
}
],
"note": [
{
"text": "Allergies: Penicillin \nSocial History: History of family conflict \nlow social interaction \nFood Allergies: Peanuts"
}
]
},
"request": {
"method": "POST",
"url": "ServiceRequest"
}
},
{
"resource": {
"resourceType": "Patient",
"id": 1,
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"identifier": [
{
"type": {
"coding": [
{
"code": "JHN",
"system": "http://hl7.org/fhir/v2/0203"
}
],
"text": "Ontario PHN"
},
"value": "4455 044 033",
"system": "http://ehealthontario.ca/API/FHIR/NamingSystem/ca-on-patient-hcn",
"extension": [
{
"url": "https://www.caredove.com/FHIR3/StructureDefinition/caredove-healthcardversion",
"valueString": "H"
}
]
}
],
"name": [
{
"given": [
"John",
"Scott"
],
"family": "Smith"
}
],
"telecom": [
{
"system": "phone",
"value": "(555) 111-1111",
"use": "mobile",
"rank": 1
},
{
"system": "phone",
"value": "(555) 222-2222",
"rank": 2
},
{
"system": "email",
"value": "testpatient@caredove.com"
}
],
"gender": "male",
"birthDate": "1928-06-29",
"address": [
{
"use": "home",
"type": "physical",
"line": [
"Unit 2",
"50 Albert St."
],
"city": "Waterloo",
"state": "ON",
"postalCode": "K8N 1N1",
"country": "Can"
}
],
"maritalStatus": {
"coding": [
{
"code": "M",
"display": "Married"
}
],
"text": "Married"
},
"contact": [
{
"relationship": [
{
"text": "Alternate Contact"
}
],
"name": {
"given": [
"Shemergency",
"Scott"
],
"family": "McContact"
},
"telecom": [
{
"system": "phone",
"value": "(555) 111-1111",
"use": "mobile",
"rank": 1
},
{
"system": "phone",
"value": "(555) 222-2222",
"rank": 2
},
{
"system": "email",
"value": "testcontact@caredove.com"
}
],
"address": {
"use": "home",
"type": "physical",
"line": [
"Unit 2",
"50 Albert St."
],
"city": "Waterloo",
"state": "ON",
"postalCode": "32819",
"country": "Can"
},
"gender": "female"
}
],
"communication": [
{
"language": {
"coding": [
{
"code": "en",
"display": "English"
}
],
"text": "English"
},
"preferred": true
}
],
"generalPractitioner": [
{
"reference": "PractitionerRole/2"
}
]
},
"request": {
"method": "POST",
"url": "Patient"
}
},
{
"resource": {
"resourceType": "PractitionerRole",
"id": 1,
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"practitioner": {
"reference": "Practitioner/1"
},
"organization": {
"reference": "Organization/1"
},
"location": [
{
"reference": "Location/1"
}
],
"telecom": [
{
"system": "phone",
"value": "(555) 111-1111",
"use": "work"
},
{
"system": "email",
"value": "testsender@caredove.com",
"use": "work"
}
]
},
"request": {
"method": "POST",
"url": "PractitionerRole"
}
},
{
"resource": {
"resourceType": "Practitioner",
"id": 1,
"name": [
{
"given": [
"Requesty"
],
"family": "McSenderson"
}
]
},
"request": {
"method": "POST",
"url": "Practitioner"
}
},
{
"resource": {
"resourceType": "Organization",
"id": 1,
"name": "North Sender Clinic"
},
"request": {
"method": "POST",
"url": "Organization"
}
},
{
"resource": {
"resourceType": "Location",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"id": 1,
"name": "Downtown Sender Hub",
"address": {
"use": "work",
"type": "physical",
"line": [
"Suite 11",
"11 King st. West"
],
"city": "Kitchener",
"state": "ON",
"postalCode": "N2L 1T1",
"country": "Can"
}
},
"request": {
"method": "POST",
"url": "Location"
}
},
{
"resource": {
"resourceType": "PractitionerRole",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"id": 2,
"practitioner": {
"reference": "Practitioner/2"
},
"organization": {
"reference": "Organization/2"
},
"location": [
{
"reference": "Location/2"
}
],
"telecom": [
{
"system": "phone",
"value": "(555) 222-2222",
"use": "work"
},
{
"system": "email",
"value": "familydoc@caredove.com",
"use": "work"
}
]
},
"request": {
"method": "POST",
"url": "PractitionerRole"
}
},
{
"resource": {
"resourceType": "Practitioner",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"id": 2,
"name": [
{
"given": [
"Dr. Prim"
],
"family": "Caredoc"
}
]
},
"request": {
"method": "POST",
"url": "Practitioner"
}
},
{
"resource": {
"resourceType": "Organization",
"id": 2,
"name": "Star Family Health Team"
},
"request": {
"method": "POST",
"url": "Organization"
}
},
{
"resource": {
"resourceType": "DocumentReference",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"id": 1,
"status": "current",
"created": "2018-03-09T15:21:51.2112Z",
"description": "Filename or Document Title goes here",
"content": [
{
"attachment": "NEEDS WORK - ATTACHMENT DATA TYPE",
"format": "NEEDS WORK - FORMAT INFO"
}
]
},
"request": {
"method": "POST",
"url": "Practitioner"
}
},
{
"resource": {
"resourceType": "Location",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"id": 2,
"name": "West Side GP Office",
"address": {
"use": "work",
"type": "physical",
"line": [
"22 Weber st. East"
],
"city": "Kitchener",
"state": "ON",
"postalCode": "N2L 2T2",
"country": "Can"
}
},
"request": {
"method": "POST",
"url": "Location"
}
},
{
"resource": {
"resourceType": "Task",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"> </div>"
},
"id": 1,
"basedOn": {
"reference": "ServiceRequest/1"
},
"status": "requested",
"businessStatus ": {
"text": "Waiting for preliminary review"
},
"intent": "proposal",
"code": {
"text": "Process Request"
},
"description": "Process and close this referral request",
"authoredOn": "2018-03-09T15:21:51Z",
"lastModified": "2018-03-09T15:21:51Z"
},
"request": {
"method": "POST",
"url": "Task"
}
}
]
}

View File

@ -766,7 +766,7 @@
<properties>
<fhir_core_version>5.5.7</fhir_core_version>
<fhir_core_version>5.6.3</fhir_core_version>
<ucum_version>1.0.3</ucum_version>
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>