diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java index 8c3cf454300..4fb7129fbca 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java @@ -52,7 +52,7 @@ public class FhirValidator { private static volatile Boolean ourPhPresentOnClasspath; private final FhirContext myContext; private List 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; } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/3209-validate_contains_change.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/3209-validate_contains_change.yaml new file mode 100644 index 00000000000..51dc9ee782d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/3209-validate_contains_change.yaml @@ -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." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/changelog_2020.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/changelog_2020.md index 82996cdafe6..2ddff35415c 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/changelog_2020.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/changelog_2020.md @@ -1,3 +1,4 @@ + # Changelog: 2020 diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfigDstu3Plus.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfigDstu3Plus.java index 796ba682cb9..845c07d2d70 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfigDstu3Plus.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfigDstu3Plus.java @@ -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(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java index 82aaf6fef7b..ff5fbb2c981 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java @@ -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; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java index ff3678d051c..3481cb129b2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java @@ -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"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 33cbefd436f..75fb6bdc8ed 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -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 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); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index a3ec4b90623..14e7d3a4a5f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -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)")); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index 8164fd79a9a..5ee914b618f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -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()) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorR4Test.java index f5187b4f36d..4546ccf2d86 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorR4Test.java @@ -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); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java index 693e1809f14..8742be4cf9b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java @@ -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 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(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index 3a901ed9c1b..6d64df75098 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -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; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index c91a9382b10..47021e3ec8a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -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; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java index 7967ad727ee..15099ddb9be 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java @@ -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; } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java index 9ffce46129c..108c7f9db2f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java @@ -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; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java index ffacc9c294c..8c92f1c97ef 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java @@ -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; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java index 05d1cfe6d58..be47ec35e1e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java @@ -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 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); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java index 56707443461..a4505b9030c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java @@ -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; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorPolicyAdvisor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorPolicyAdvisor.java new file mode 100644 index 00000000000..6fe84311983 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorPolicyAdvisor.java @@ -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 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; + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java index 8256aae493e..246a0f32fe2 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java @@ -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; } diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java index 1f4723e395e..b1a01c58454 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/HapiWorkerContext.java @@ -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; diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/HapiWorkerContext.java index e74075368d0..b6bdc611da1 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/HapiWorkerContext.java @@ -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; diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java index ba66b4937d3..7c95f3f01e1 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/ctx/HapiWorkerContext.java @@ -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 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 diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirDefaultPolicyAdvisor.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirDefaultPolicyAdvisor.java new file mode 100644 index 00000000000..036f3553111 --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirDefaultPolicyAdvisor.java @@ -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 systems) { + return CodedContentValidationPolicy.CODE; + } +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirInstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirInstanceValidator.java index 384ff09111e..48e3a9a3138 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirInstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FhirInstanceValidator.java @@ -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 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; } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java index 8c0614cab5b..e3ddf634460 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/ValidatorWrapper.java @@ -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 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); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java index c402b9b8dfd..0919cf6f177 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java @@ -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 fetchResource(Class 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 { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeAdvisorDstu21.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeAdvisorDstu21.java new file mode 100644 index 00000000000..bca74807eed --- /dev/null +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeAdvisorDstu21.java @@ -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; + } +} diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterDstu21.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterDstu21.java index 4866ca246ae..5f7abb52de9 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterDstu21.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionTypeConverterDstu21.java @@ -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 diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java index c62c5ab16ef..87a7d13b948 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/FhirInstanceValidatorDstu3Test.java @@ -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 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 diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java index 951e2899e5a..0642989bf26 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java @@ -165,7 +165,7 @@ public class ResourceValidatorDstu3Test { ValidationResult output = val.validateWithResult(p); List 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()); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index f23a99ba21e..20f910cbe33 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -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 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 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 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')")); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java index d839f65486c..e87a0cf7296 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/FhirInstanceValidatorR5Test.java @@ -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()); diff --git a/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json b/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json index aab85907d50..da3dd5e717e 100644 --- a/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json +++ b/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json @@ -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": "
" + "resourceType": "Bundle", + "type": "transaction", + "timestamp": "2018-03-09T15:21:51.2112Z", + "entry": [ + { + "resource": { + "resourceType": "ServiceRequest", + "id": 1, + "text": { + "status": "generated", + "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" + } + ] }, - - "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": "
" + "request": { + "method": "POST", + "url": "ServiceRequest" + } + }, + { + "resource": { + "resourceType": "Patient", + "id": 1, + "text": { + "status": "generated", + "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" + } + ] }, - - "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": "
" + "request": { + "method": "POST", + "url": "Patient" + } + }, + { + "resource": { + "resourceType": "PractitionerRole", + "id": 1, + "text": { + "status": "generated", + "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" + } + ] }, - - "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": "
" + "request": { + "method": "POST", + "url": "PractitionerRole" + } + }, + { + "resource": { + "resourceType": "Practitioner", + "id": 1, + "name": [ + { + "given": [ + "Requesty" + ], + "family": "McSenderson" + } + ] }, - - "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": "
" + "request": { + "method": "POST", + "url": "Practitioner" + } + }, + { + "resource": { + "resourceType": "Organization", + "id": 1, + "name": "North Sender Clinic" }, - - "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": "
" + "request": { + "method": "POST", + "url": "Organization" + } + }, + { + "resource": { + "resourceType": "Location", + "text": { + "status": "generated", + "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" + } }, - - "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": "
" + "request": { + "method": "POST", + "url": "Location" + } + }, + { + "resource": { + "resourceType": "PractitionerRole", + "text": { + "status": "generated", + "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" + } + ] }, - - "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": "
" + "request": { + "method": "POST", + "url": "PractitionerRole" + } + }, + { + "resource": { + "resourceType": "Practitioner", + "text": { + "status": "generated", + "div": "
" + }, + "id": 2, + "name": [ + { + "given": [ + "Dr. Prim" + ], + "family": "Caredoc" + } + ] }, - - "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": "
" + "request": { + "method": "POST", + "url": "Practitioner" + } + }, + { + "resource": { + "resourceType": "Organization", + "id": 2, + "name": "Star Family Health Team" }, - - "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" - - } - - } - - ] - + "request": { + "method": "POST", + "url": "Organization" + } + }, + { + "resource": { + "resourceType": "DocumentReference", + "text": { + "status": "generated", + "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": "
" + }, + "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": "
" + }, + "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" + } + } + ] } diff --git a/pom.xml b/pom.xml index 085664230cd..c04773787ca 100644 --- a/pom.xml +++ b/pom.xml @@ -766,7 +766,7 @@ - 5.5.7 + 5.6.3 1.0.3 -Dfile.encoding=UTF-8 -Xmx2048m