From 0abbb1a4f4b6e019d3d865e3fcf4f04fce9bdb06 Mon Sep 17 00:00:00 2001 From: dotasek Date: Wed, 12 Jun 2024 12:24:26 -0500 Subject: [PATCH] Core library bump 6.3.11 (#5772) * Bump to core 6.2.6 + fix compilation errors * Fix signature error * Error ordering * WIP 1 Fixing failing tests * Fix FhirInstanceValidatorR4Test * Fix FhirInstanceValidatorR5Test * Fix FhirInstanceValidatorR4BTest * Fix FhirInstanceValidatorDstu3Test.testValidateBuiltInProfiles() * WIP Fix FhirInstanceValidatorDstu3Test * Fix isPrimitiveType * Add placeholder narratives to ServerCapabilityStatement * Fix QuestionnaireResponseValidatorR4Test * Newline cleanup * Fix QuestionnaireResponseValidatorDstu3Test * Fix QuestionnaireResponseValidatorR5Test * Increase expected error messages by two because VALIDATION_HL7_WG_NEEDED New validation requirement as of 2023-09-16 * Add placeholder fix for Balp narrative * Fix expected validation messages * Fix more expected validation messages * Don't generate a master IPS narrative * Fix IPS generation tests expecting old composition narrative * Update fhir core and clinical-reasoning * Remove commented code * Bump to core 6.2.16-SNAPSHOT * Add missing methods * Add missing methods 2 * Fix error codes * Fix error code * Fix failing tests for Unsupported method 2488 * Fix error text * Fix another expected error message * Apply spotless * Fix error strings * Add minimal implementation to fix failing test * Fix error message * Fix some validation tests (r4) * Fix more R5 tests * Update for changing API * Fix some R4B test failures * Fix android incompatibility * Fix more tests * Switch back to LF * Fix more tests 2 * Fix R4 IPS generation use of relative references, switch to random UUID * Fix missing codes and patient ID in IPS test * Fix missing codes in IPS R4 Test 2 * Fix display value * Fix display value * Reorg wrapper issue collection; fixes errors with Balp and others * Update for API changes * Fix code set typo causing test failure * Fix DiffProviderR4Test * Fix RepositoryValidatingInterceptorHttpR4Test fails due to stricter code validation * More code fixes * Don't expect an extra error. * Use more specific logic for details tx code * Catch expected error * Clean up * Account for HAPI isEnabledValidationForCodingsLogicalAnd cases * Apply spotless * Change outputs for en_US single code test * Add missing system from code to pass validation * Account for DSTU3 and R4 including different code systems for race * Add expected loinc codes to test dao * Add some codesystem support for test * Bump core version * WIP pass on a list of CodeValidationIssue from CodeValidationResults * Re-use v2 and v3 data from r4 in r4b * CodeSystems can be supported, but not have a resource * Remove hack around unitsofmeasure + return false LookupCodeResult * Clean up chatter. * Update test cases, remove FIXME * Stop returning before adding all issues to ValidationResults * Update for changes in core API * Add severity to code issues + return to unknown system support * Use new CollectionUtils * Fix more issue messages and orders + null in TermReadSvcImpl * After checking valueSet, also check codeSystem * On second thought, always check the codesystem * Improve validation message * Refactor * Add error consistent with core validator results * Decrement query counts for validation * Improve code validation messages + don't miss invalid display on cs * Fix expected messages (added code details) * More expected message fixes * Remove redundant text from diagnostics + match core exception handling * Explicitly send tx passthrough messages no longer managed in core * Apply spotless * Add issue to code validation in TermReadSvcImpl * Adjust indexes of expected errors now that TermReadSvc is reporting * Bump to released core version * Code cleanup: commented code * More commented code cleanup * Fix parameter names * Use Set.of and List.of + fix duplicate values in set * Revert animal sniffed breakage, make field private * Add comments to describe disabled test * Remove System.out chatter * Fix javadoc generation for r5 structures module * More comments and move disabled annotation It was working when it left the shop. * Move older changelog into 7.4.0 and create core update changelog * Switch changelog type to change * Bump HAPI version * Add validation utils for FATAL issue severity * Handle FATAL codeValidationIssue severity * Fix test (validation throws more warnings) * Bump to version 7.3.4-SNAPSHOT * Bump to SNAPSHOT version of core + add necessary method implementations * Add comments to explain R4 terminology resources in R4B * Add test for Observation vital signs profile validation * Try using default validatorPolicyAdvisor * Return an empty set for fetchCanonicalResourceVersions * Profile now gives more explicit error instead of valueSet * Revert commit * Add CodeValidationIssues and additional error info * Do not return first successful match from codings. Check all. + adjust for extra errors. * apply spotless * Fix failing test, add comments re: invocations * Fix test to expect more informative error * Fix stray compilation errors from merge. * Fix merge overwrite of validation messages and indices * Fix merge overwrite of new validation results * Use core release 6.3.11 * Update HAPI version to 7.3.5-SNAPSHOT * Fix changelog * Remove commented code --------- Co-authored-by: James Agnew Co-authored-by: volodymyr --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- ...rofileValidationSupportBundleStrategy.java | 6 +- .../context/support/IValidationSupport.java | 81 +++- .../ca/uhn/fhir/i18n/hapi-messages.properties | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- .../ca/uhn/fhir/cli/ValidateCommandTest.java | 4 +- ...1.xml => patient-uslab-example1-dstu3.xml} | 4 +- .../resources/patient-uslab-example1-r4.xml | 83 ++++ hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- .../canonical/VersionCanonicalizer.java | 3 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- .../7_4_0/5559-improve-ips-narrative-gen.yaml | 7 + ...9-update-org-hl7-fhir-core-dependency.yaml | 4 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../jpa/dao/JpaStorageResourceParser.java | 2 +- .../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 17 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- .../fhir/jpa/ips/narrative/composition.html | 6 +- .../ips/generator/IpsGenerationR4Test.java | 131 ++++++- .../generator/IpsGeneratorSvcImplTest.java | 6 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- .../extractor/SearchParamExtractorDstu3.java | 2 +- .../extractor/SearchParamExtractorR4.java | 37 +- .../extractor/SearchParamExtractorR4B.java | 37 +- .../extractor/SearchParamExtractorR5.java | 42 ++- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- .../FhirResourceDaoDstu2ValidateTest.java | 3 +- .../FhirResourceDaoValueSetDstu2Test.java | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- .../FhirResourceDaoDstu3ValidateTest.java | 2 +- .../dstu3/ResourceProviderDstu3Test.java | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- ...idationSupportFromValidationChainTest.java | 6 +- .../r4/FhirResourceDaoR4QueryCountTest.java | 8 +- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 151 +++++--- .../dao/r4/FhirResourceDaoR4ValueSetTest.java | 2 +- ...sitoryValidatingInterceptorHttpR4Test.java | 10 +- ...tionMessageSuppressingInterceptorTest.java | 9 +- .../jpa/provider/r4/DiffProviderR4Test.java | 8 +- .../r4/ResourceProviderR4CodeSystemTest.java | 2 +- .../provider/r4/ResourceProviderR4Test.java | 16 +- .../jpa/term/ValueSetExpansionR4Test.java | 4 +- .../r4/uscore/observation-pulseox.json | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- .../dao/r5/FhirResourceDaoR5ValueSetTest.java | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- .../ca/uhn/fhir/jpa/test/BaseJpaR4Test.java | 41 +- .../fhir/jpa/test/R4ValidationTestUtil.java | 8 + hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 8 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../ResponseHighlighterInterceptor.java | 44 ++- .../ServerCapabilityStatementProvider.java | 11 + .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 9 +- .../validation/ValidatorPolicyAdvisor.java | 56 ++- .../validation/ValidatorResourceFetcher.java | 11 +- .../balp/BalpAuditCaptureInterceptor.java | 5 + hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- .../dstu3/hapi/ctx/HapiWorkerContext.java | 7 +- .../dstu3/hapi/fluentpath/FhirPathDstu3.java | 8 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- .../fhir/r4/hapi/ctx/HapiWorkerContext.java | 34 ++ .../fhir/r4/hapi/fluentpath/FhirPathR4.java | 44 ++- .../ResponseHighlighterInterceptorTest.java | 12 +- hapi-fhir-structures-r4b/pom.xml | 2 +- .../fhir/r4b/hapi/fhirpath/FhirPathR4B.java | 44 ++- hapi-fhir-structures-r5/pom.xml | 9 +- .../fhir/r5/hapi/ctx/HapiWorkerContext.java | 144 ++++++- .../hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java | 49 ++- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 9 +- .../CommonCodeSystemsTerminologyService.java | 16 +- ...oryTerminologyServerValidationSupport.java | 174 ++++++--- ...ownCodeSystemWarningValidationSupport.java | 10 +- .../FHIRPathResourceGeneratorR4.java | 7 +- .../validator/FhirDefaultPolicyAdvisor.java | 42 ++- .../validator/FhirInstanceValidator.java | 43 ++- .../validator/ValidatorWrapper.java | 2 +- .../VersionSpecificWorkerContextWrapper.java | 353 ++++++++++++++++-- ...rverCapabilityStatementProviderR4Test.java | 4 +- ...mmonCodeSystemsTerminologyServiceTest.java | 2 +- ...nSpecificWorkerContextWrapperCoreTest.java | 277 ++++++++++++++ .../FhirInstanceValidatorDstu2Test.java | 77 +++- .../FhirInstanceValidatorDstu3Test.java | 145 +++++-- ...estionnaireResponseValidatorDstu3Test.java | 12 +- .../QuestionnaireValidatorDstu3Test.java | 4 +- .../ResourceValidatorDstu3Test.java | 8 +- .../fhir/dstu3/utils/FhirPathEngineTest.java | 1 + .../fhir/r4/utils/FhirPathEngineR4Test.java | 1 + .../FhirInstanceValidatorR4Test.java | 202 ++++++---- .../r4/validation/HapiWorkerContextTest.java | 4 +- .../NpmPackageValidationSupportTest.java | 5 +- .../QuestionnaireResponseValidatorR4Test.java | 14 +- .../FhirInstanceValidatorR4BTest.java | 105 ++++-- .../FhirInstanceValidatorR5Test.java | 99 +++-- .../QuestionnaireResponseValidatorR5Test.java | 10 +- .../src/test/resources/bug703.json | 2 +- .../dstu3/valueset-doc-typecodes.json | 67 ++++ .../resources/dstu3/valueset-v2-0131.json | 38 ++ ...multiple-comm-langs-en-US-and-en-DASH.json | 4 + ...le-comm-langs-en-and-en_US-UNDERSCORE.json | 4 + ...le-comm-langs-en_US-and-en-UNDERSCORE.json | 4 + ...ient-with-single-comm-lang-en-US-DASH.json | 4 + .../patient-with-single-comm-lang-en.json | 4 + ...ith-single-comm-lang-en_US-UNDERSCORE.json | 4 + ...iagnosticreport-example-gingival-mass.json | 2 +- .../r4/observation-with-body-temp-ucum.json | 8 + hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 4 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 162 files changed, 2557 insertions(+), 609 deletions(-) rename hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/{patient-uslab-example1.xml => patient-uslab-example1-dstu3.xml} (94%) create mode 100644 hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-r4.xml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5559-improve-ips-narrative-gen.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5939-update-org-hl7-fhir-core-dependency.yaml create mode 100644 hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperCoreTest.java create mode 100644 hapi-fhir-validation/src/test/resources/dstu3/valueset-doc-typecodes.json create mode 100644 hapi-fhir-validation/src/test/resources/dstu3/valueset-v2-0131.json diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 01bbf73d5ef..889393a4e6f 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 2a27429116b..4db9430e046 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 1869bb59bde..7cf423ed29d 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java index e35521ea2fb..f6319bcd3a4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java @@ -123,8 +123,10 @@ class DefaultProfileValidationSupportBundleStrategy implements IValidationSuppor break; case R4B: terminologyResources.add("/org/hl7/fhir/r4b/model/valueset/valuesets.xml"); - terminologyResources.add("/org/hl7/fhir/r4b/model/valueset/v2-tables.xml"); - terminologyResources.add("/org/hl7/fhir/r4b/model/valueset/v3-codesystems.xml"); + // For R4B we can re-use the same v2 and v3 files as R4, as these will not be updated and it will reduce + // duplication. + terminologyResources.add("/org/hl7/fhir/r4/model/valueset/v2-tables.xml"); + terminologyResources.add("/org/hl7/fhir/r4/model/valueset/v3-codesystems.xml"); structureDefinitionResources.add("/org/hl7/fhir/r4b/model/profile/profiles-resources.xml"); structureDefinitionResources.add("/org/hl7/fhir/r4b/model/profile/profiles-types.xml"); structureDefinitionResources.add("/org/hl7/fhir/r4b/model/profile/profiles-others.xml"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index 328cd53f445..bad153298c4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -459,6 +459,58 @@ public interface IValidationSupport { INFORMATION } + enum CodeValidationIssueCode { + NOT_FOUND, + CODE_INVALID, + INVALID, + OTHER + } + + enum CodeValidationIssueCoding { + VS_INVALID, + NOT_FOUND, + NOT_IN_VS, + + INVALID_CODE, + INVALID_DISPLAY, + OTHER + } + + class CodeValidationIssue { + + private final String myMessage; + private final IssueSeverity mySeverity; + private final CodeValidationIssueCode myCode; + private final CodeValidationIssueCoding myCoding; + + public CodeValidationIssue( + String theMessage, + IssueSeverity mySeverity, + CodeValidationIssueCode theCode, + CodeValidationIssueCoding theCoding) { + this.myMessage = theMessage; + this.mySeverity = mySeverity; + this.myCode = theCode; + this.myCoding = theCoding; + } + + public String getMessage() { + return myMessage; + } + + public IssueSeverity getSeverity() { + return mySeverity; + } + + public CodeValidationIssueCode getCode() { + return myCode; + } + + public CodeValidationIssueCoding getCoding() { + return myCoding; + } + } + class ConceptDesignation { private String myLanguage; @@ -634,6 +686,8 @@ public interface IValidationSupport { private String myDisplay; private String mySourceDetails; + private List myCodeValidationIssues; + public CodeValidationResult() { super(); } @@ -717,6 +771,23 @@ public interface IValidationSupport { return this; } + public List getCodeValidationIssues() { + if (myCodeValidationIssues == null) { + myCodeValidationIssues = new ArrayList<>(); + } + return myCodeValidationIssues; + } + + public CodeValidationResult setCodeValidationIssues(List theCodeValidationIssues) { + myCodeValidationIssues = new ArrayList<>(theCodeValidationIssues); + return this; + } + + public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) { + getCodeValidationIssues().add(theCodeValidationIssue); + return this; + } + public boolean isOk() { return isNotBlank(myCode); } @@ -777,14 +848,18 @@ public interface IValidationSupport { private final IBaseResource myValueSet; private final String myError; - public ValueSetExpansionOutcome(String theError) { + private boolean myErrorIsFromServer; + + public ValueSetExpansionOutcome(String theError, boolean theErrorIsFromServer) { myValueSet = null; myError = theError; + myErrorIsFromServer = theErrorIsFromServer; } public ValueSetExpansionOutcome(IBaseResource theValueSet) { myValueSet = theValueSet; myError = null; + myErrorIsFromServer = false; } public String getError() { @@ -794,6 +869,10 @@ public interface IValidationSupport { public IBaseResource getValueSet() { return myValueSet; } + + public boolean getErrorIsFromServer() { + return myErrorIsFromServer; + } } class LookupCodeResult { diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index 8cf10972757..ef31da44191 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -1,5 +1,5 @@ -org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport.displayMismatch=Concept Display "{0}" does not match expected "{1}" +org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport.displayMismatch=Concept Display "{0}" does not match expected "{1}" for ''{2}#{3}'' org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService.unknownCodeInSystem=Unknown code "{0}#{1}" org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService.invalidCodeInSystem=Code {0} is not valid for system: {1} diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 26cb8ac4b05..4b139de91ff 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 701da56cfac..1bbb8ca2932 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 4a08abd2b08..aa7f1d41e62 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ValidateCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ValidateCommandTest.java index 3accc9874e3..1cb09df5377 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ValidateCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/ValidateCommandTest.java @@ -38,7 +38,7 @@ public class ValidateCommandTest { @Test public void testValidateLocalProfileDstu3() { - String resourcePath = ValidateCommandTest.class.getResource("/patient-uslab-example1.xml").getFile(); + String resourcePath = ValidateCommandTest.class.getResource("/patient-uslab-example1-dstu3.xml").getFile(); App.main(new String[]{ "validate", @@ -49,7 +49,7 @@ public class ValidateCommandTest { @Test public void testValidateLocalProfileR4() { - String resourcePath = ValidateCommandTest.class.getResource("/patient-uslab-example1.xml").getFile(); + String resourcePath = ValidateCommandTest.class.getResource("/patient-uslab-example1-r4.xml").getFile(); ourLog.info(resourcePath); App.main(new String[]{ diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1.xml b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-dstu3.xml similarity index 94% rename from hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1.xml rename to hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-dstu3.xml index 10b3c327a24..d11b75deeaa 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-dstu3.xml @@ -40,6 +40,7 @@ + @@ -47,6 +48,7 @@ + @@ -78,4 +80,4 @@ - \ No newline at end of file + diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-r4.xml b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-r4.xml new file mode 100644 index 00000000000..feb346ad61e --- /dev/null +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/resources/patient-uslab-example1-r4.xml @@ -0,0 +1,83 @@ + + + + + +
+

+ Generated Narrative with Details +

+

+ id + : patient-uslab-example1 +

+

+ identifier + : 18547545 (USUAL) +

+

+ name + : Todd G. Lerr (OFFICIAL) +

+

+ gender + : male +

+

+ birthDate + : Jun 7, 2012 +

+

+ deceased + : false +

+

+ address + : 123 North 102nd Street Apt 4d Harrisburg PA 17102 USA (HOME) +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 2967959fea7..6c553cc15fa 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 63d690e7ed0..fd446d9d633 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 628fd2fc5b3..9c5af1c79fd 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 61d26d4fb6c..d4cb2a2fde7 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 65387ad98e8..67e049c6105 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java index 06c7f99d181..0a01892f056 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java @@ -250,7 +250,8 @@ public class VersionCanonicalizer { String packageUserData = (String) theResource.getUserData("package"); if (packageUserData != null) { retVal.setUserData("package", packageUserData); - retVal.setSourcePackage(new PackageInformation(packageUserData, new Date())); + retVal.setSourcePackage(new PackageInformation( + packageUserData, theResource.getStructureFhirVersionEnum().getFhirVersionString(), new Date())); } return retVal; } diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 6819519c4e9..5e2764d4b08 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 879aabfad42..769b9544235 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5559-improve-ips-narrative-gen.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5559-improve-ips-narrative-gen.yaml new file mode 100644 index 00000000000..f63f34e1124 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5559-improve-ips-narrative-gen.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5559 +title: "The IPS narrative generator previously created narratives for each `Composition.section.text` entry, + but also combined these section narratives into a single concatenated narrative for `Composition.text`. This + caused validation issues as the narratives then contained duplicate IDs, and also wasted space. This has + been removed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5939-update-org-hl7-fhir-core-dependency.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5939-update-org-hl7-fhir-core-dependency.yaml new file mode 100644 index 00000000000..5bdc369371d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5939-update-org-hl7-fhir-core-dependency.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5939 +title: "This brings the org.hl7.fhir.core dependency to version 6.3.11." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 642fd34b836..b3f11687fa0 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 4849131ece6..d090969ca36 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 2b70be28b6e..f066e574952 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 3f6dd2b58b5..3d2e00b876f 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java index 6bab499210c..09d46a557e4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java @@ -58,7 +58,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.util.IMetaTagSorter; import ca.uhn.fhir.util.MetaUtil; import jakarta.annotation.Nullable; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseCoding; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index c1d0b605ceb..e9f07b798a5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -300,6 +300,9 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { + if (isBlank(theSystem)) { + return false; + } TermCodeSystemVersionDetails cs = getCurrentCodeSystemVersion(theSystem); return cs != null; } @@ -1040,8 +1043,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } catch (InMemoryTerminologyServerValidationSupport.ExpansionCouldNotBeCompletedInternallyException e) { if (theExpansionOptions != null && !theExpansionOptions.isFailOnMissingCodeSystem() - && e.getFailureType() - == InMemoryTerminologyServerValidationSupport.FailureType.UNKNOWN_CODE_SYSTEM) { + // Code system is unknown, therefore NOT_FOUND + && e.getCodeValidationIssue().getCoding() == CodeValidationIssueCoding.NOT_FOUND) { return; } throw new InternalErrorException(Msg.code(888) + e); @@ -2151,6 +2154,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { theCode, theDisplay, expectedDisplay, + theSystem, systemVersion, myStorageSettings.getIssueSeverityForCodeDisplayMismatch()); } @@ -2181,10 +2185,16 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { private CodeValidationResult createFailureCodeValidationResult( String theSystem, String theCode, String theCodeSystemVersion, String theAppend) { + String theMessage = "Unable to validate code " + theSystem + "#" + theCode + theAppend; return new CodeValidationResult() .setSeverity(IssueSeverity.ERROR) .setCodeSystemVersion(theCodeSystemVersion) - .setMessage("Unable to validate code " + theSystem + "#" + theCode + theAppend); + .setMessage(theMessage) + .addCodeValidationIssue(new CodeValidationIssue( + theMessage, + IssueSeverity.ERROR, + CodeValidationIssueCode.CODE_INVALID, + CodeValidationIssueCoding.INVALID_CODE)); } private List findByValueSetResourcePidSystemAndCode( @@ -2800,6 +2810,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { theCode, theDisplay, code.getDisplay(), + code.getSystem(), code.getSystemVersion(), myStorageSettings.getIssueSeverityForCodeDisplayMismatch()); } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index db70068681a..e2fbe14b4ca 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 74543e232e2..c0e2f3aa64a 100644 --- a/hapi-fhir-jpaserver-hfql/pom.xml +++ b/hapi-fhir-jpaserver-hfql/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index d80f5d2446a..3f5a9c1ad0f 100644 --- a/hapi-fhir-jpaserver-ips/pom.xml +++ b/hapi-fhir-jpaserver-ips/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/src/main/resources/ca/uhn/fhir/jpa/ips/narrative/composition.html b/hapi-fhir-jpaserver-ips/src/main/resources/ca/uhn/fhir/jpa/ips/narrative/composition.html index 505a799a597..a3f18924015 100644 --- a/hapi-fhir-jpaserver-ips/src/main/resources/ca/uhn/fhir/jpa/ips/narrative/composition.html +++ b/hapi-fhir-jpaserver-ips/src/main/resources/ca/uhn/fhir/jpa/ips/narrative/composition.html @@ -3,9 +3,5 @@ This template generates a composite narrative for the Composition, incorporating all of the section narratives into a single narrative. */-->
-
-

-
-
-
+

International Patient Summary Document

diff --git a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java index d46094688d1..9ba3d82c09e 100644 --- a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java +++ b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.ips.api.IIpsGenerationStrategy; +import ca.uhn.fhir.jpa.ips.api.IpsContext; import ca.uhn.fhir.jpa.ips.jpa.DefaultJpaIpsGenerationStrategy; import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider; import ca.uhn.fhir.jpa.model.util.JpaConstants; @@ -21,12 +22,14 @@ import org.hl7.fhir.common.hapi.validation.support.NpmPackageValidationSupport; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Composition; import org.hl7.fhir.r4.model.Condition; import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Immunization; import org.hl7.fhir.r4.model.MedicationStatement; import org.hl7.fhir.r4.model.Parameters; @@ -43,8 +46,12 @@ import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -100,7 +107,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { validateDocument(output); assertEquals(117, output.getEntry().size()); String patientId = findFirstEntryResource(output, Patient.class, 1).getIdElement().toUnqualifiedVersionless().getValue(); - assertEquals("Patient/f15d2419-fbff-464a-826d-0afe8f095771", patientId); + assertThat(patientId).matches("urn:uuid:.*"); MedicationStatement medicationStatement = findFirstEntryResource(output, MedicationStatement.class, 2); assertEquals(patientId, medicationStatement.getSubject().getReference()); assertNull(medicationStatement.getInformationSource().getReference()); @@ -196,7 +203,7 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { validateDocument(output); assertEquals(7, output.getEntry().size()); String patientId = findFirstEntryResource(output, Patient.class, 1).getIdElement().toUnqualifiedVersionless().getValue(); - assertEquals("Patient/5342998", patientId); + assertThat(patientId).matches("urn:uuid:.*"); assertEquals(patientId, findEntryResource(output, Condition.class, 0, 2).getSubject().getReference()); assertEquals(patientId, findEntryResource(output, Condition.class, 1, 2).getSubject().getReference()); @@ -243,9 +250,8 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { ourLog.info("Output: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output)); Composition composition = findCompositionSectionByDisplay(output, "History of Immunization Narrative"); - // Should be newest first - assertThat(composition.getText().getDivAsString()).containsSubsequence( - "Vax 2015", "Vax 2010", "Vax 2005" + assertThat(composition.getText().getDivAsString()).isEqualTo( + "

International Patient Summary Document

" ); List resourceDates = output @@ -303,7 +309,12 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { @Bean public IIpsGenerationStrategy ipsGenerationStrategy() { - return new DefaultJpaIpsGenerationStrategy(); + return new DefaultJpaIpsGenerationStrategy() { + @Override + public IIdType massageResourceId(@Nullable IpsContext theIpsContext, @javax.annotation.Nonnull IBaseResource theResource) { + return IdType.newRandomUuid(); + } + }; } @Bean @@ -341,6 +352,73 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { * package. */ private class IpsTerminologySvc implements IValidationSupport { + + final Set loincValueSetCodes = Set.of( + "60591-5", + "75326-9", + "94306-8" + ); + + final Set snomedValueSetCodes = Set.of( + "14657009", + "255604002", + "38341003", + "1208807009" + ); + + final Set loincCodes = Set.of( + "10160-0", + "11369-6", + "11450-4", + "14682-9", + "14933-6", + "1988-5", + "20570-8", + "2157-6", + "26444-0", + "26449-9", + "26464-8", + "26474-7", + "26484-6", + "26515-7", + "2823-3", + "28539-5", + "2951-2", + "29953-7", + "30428-7", + "30449-3", + "30954-2", + "31348-6", + "31627-3", + "32677-7", + "48765-2", + "62238-1", + "718-7", + "8061-4", + "8076-2", + "8091-1", + "8092-9", + "8093-7", + "8094-5", + "94500-6" + ); + + final Set snomedCodes = Set.of( + // Tiny patient summary + "38341003", + "1208807009", + + // Large patient summary + "10312003", + "385055001", + "318913001", + "90560007", + "1240581000000104", + "16217701000119102", + "72098002", + "260415000" + ); + @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { return true; @@ -350,12 +428,40 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { @Override public CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { if ("http://loinc.org".equals(theCodeSystem)) { - if ("60591-5".equals(theCode)) { + if (loincValueSetCodes.contains(theCode)) { return new CodeValidationResult().setCode(theCode); } } if ("http://snomed.info/sct".equals(theCodeSystem)) { - if ("14657009".equals(theCode) || "255604002".equals(theCode)) { + if (snomedValueSetCodes.contains(theCode)) { + return new CodeValidationResult().setCode(theCode); + } + } + return null; + } + + @Override + public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { + return true; + } + + @Nullable + @Override + public CodeValidationResult validateCode( + ValidationSupportContext theValidationSupportContext, + ConceptValidationOptions theOptions, + String theCodeSystem, + String theCode, + String theDisplay, + String theValueSetUrl) { + if ("http://loinc.org".equals(theCodeSystem)) { + if (loincCodes.contains(theCode)) { + return new CodeValidationResult().setCode(theCode); + } + } + + if ("http://snomed.info/sct".equals(theCodeSystem)) { + if (snomedCodes.contains(theCode)) { return new CodeValidationResult().setCode(theCode); } } @@ -374,6 +480,15 @@ public class IpsGenerationR4Test extends BaseResourceProviderR4Test { cs.addConcept().setCode("no-known-allergies"); return cs; } + if ("http://hl7.org/fhir/sid/cvx".equals(theSystem)) { + CodeSystem cs = new CodeSystem(); + cs.setUrl("http://hl7.org/fhir/sid/cvx"); + cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + cs.addConcept().setCode("208"); + cs.addConcept().setCode("121"); + cs.addConcept().setCode("141"); + return cs; + } return null; } diff --git a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java index ef96ada152b..9a1460d83a3 100644 --- a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java +++ b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java @@ -171,9 +171,9 @@ public class IpsGeneratorSvcImplTest { // Composition itself should also have a narrative String compositionNarrative = composition.getText().getDivAsString(); ourLog.info("Composition narrative: {}", compositionNarrative); - assertThat(compositionNarrative).contains("Allergies and Intolerances"); - assertThat(compositionNarrative).doesNotContain("Pregnancy"); - + assertThat(compositionNarrative).isEqualTo( + "

International Patient Summary Document

" + ); } @Test diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index bd44b824257..82b28a3e2d8 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 1d1f881d8f6..a7e3e7c4ea6 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 2f62afaeacf..1e9bf513279 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java index baa914fc90e..ac9af04e812 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java @@ -26,9 +26,9 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.annotations.VisibleForTesting; import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu3.context.IWorkerContext; +import org.hl7.fhir.dstu3.fhirpath.FHIRPathEngine; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.model.Base; -import org.hl7.fhir.dstu3.utils.FHIRPathEngine; import org.hl7.fhir.instance.model.api.IBase; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java index de583877a07..ebddca35be0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java @@ -32,16 +32,16 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r4.context.IWorkerContext; +import org.hl7.fhir.r4.fhirpath.ExpressionNode; +import org.hl7.fhir.r4.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r4.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r4.fhirpath.TypeDetails; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4.model.Base; -import org.hl7.fhir.r4.model.ExpressionNode; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.ResourceType; -import org.hl7.fhir.r4.model.TypeDetails; import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.r4.utils.FHIRPathEngine; -import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.FunctionDetails; import java.util.Collections; import java.util.HashMap; @@ -106,13 +106,16 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements private final Map myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>()); @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Collections.emptyList(); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -122,24 +125,33 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object theAppContext, String theUrl, Base theRefContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object theAppContext, String theUrl, Base refContext) { Base retVal = (Base) BundleUtil.getReferenceInBundle(getContext(), theUrl, theAppContext); if (retVal != null) { return retVal; @@ -187,12 +199,13 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java index 1d9253ba369..f804e770cd1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java @@ -32,16 +32,16 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r4b.context.IWorkerContext; +import org.hl7.fhir.r4b.fhirpath.ExpressionNode; +import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r4b.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r4b.fhirpath.TypeDetails; import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4b.model.Base; -import org.hl7.fhir.r4b.model.ExpressionNode; import org.hl7.fhir.r4b.model.IdType; import org.hl7.fhir.r4b.model.Resource; import org.hl7.fhir.r4b.model.ResourceType; -import org.hl7.fhir.r4b.model.TypeDetails; import org.hl7.fhir.r4b.model.ValueSet; -import org.hl7.fhir.r4b.utils.FHIRPathEngine; -import org.hl7.fhir.r4b.utils.FHIRPathUtilityClasses.FunctionDetails; import java.util.Collections; import java.util.HashMap; @@ -106,13 +106,16 @@ public class SearchParamExtractorR4B extends BaseSearchParamExtractor implements private final Map myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>()); @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Collections.emptyList(); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -122,24 +125,33 @@ public class SearchParamExtractorR4B extends BaseSearchParamExtractor implements } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object theAppContext, String theUrl, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object theAppContext, String theUrl, Base refContext) { Base retVal = (Base) BundleUtil.getReferenceInBundle(getContext(), theUrl, theAppContext); if (retVal != null) { return retVal; @@ -187,12 +199,13 @@ public class SearchParamExtractorR4B extends BaseSearchParamExtractor implements } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java index 090f5f140f4..dd99d856543 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java @@ -31,16 +31,16 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.fhirpath.ExpressionNode; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r5.fhirpath.TypeDetails; import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.ResourceType; -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.FHIRPathUtilityClasses.FunctionDetails; import java.util.Collections; import java.util.HashMap; @@ -103,13 +103,16 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements private final Map myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>()); @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Collections.emptyList(); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -119,24 +122,33 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String theUrl, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String theUrl, Base refContext) { Base retVal = (Base) BundleUtil.getReferenceInBundle(getContext(), theUrl, appContext); if (retVal != null) { return retVal; @@ -184,13 +196,19 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object theO, String theS) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } + + @Override + public boolean paramIsType(String name, int index) { + return false; + } } } diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 721d6297f87..7335ca25b09 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index 07b5f928c4a..2b057a8310b 100644 --- a/hapi-fhir-jpaserver-test-dstu2/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java index 9b2cdb670f3..008478945a0 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java @@ -123,7 +123,6 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { Observation input = new Observation(); String profileUri = "http://example.com/StructureDefinition/" + methodName; ResourceMetadataKeyEnum.PROFILES.put(input, Collections.singletonList(new IdDt(profileUri))); - input.addIdentifier().setSystem("http://acme").setValue("12345"); input.getEncounter().setReference("http://foo.com/Encounter/9"); input.setStatus(ObservationStatusEnum.FINAL); @@ -137,7 +136,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test { assertHasErrors(oo); String ooString = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(ooString); - assertThat(ooString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"); + assertThat(ooString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it could not be found"); } @Test diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java index 233089dc754..43242b0114f 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoValueSetDstu2Test.java @@ -102,7 +102,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { CodeableConceptDt codeableConcept = null; IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isOk()); - assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage()); + assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for 'http://acme.org#11378-7' for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals(IValidationSupport.IssueSeverity.WARNING, result.getSeverity()); } diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 5b87d992043..f32226f6482 100644 --- a/hapi-fhir-jpaserver-test-dstu3/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java index ca3bf285c04..d8c0a4f99e0 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java @@ -325,7 +325,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test { assertHasErrors(oo); String outputString = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(outputString); - assertThat(outputString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"); + assertThat(outputString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it could not be found"); } @Test diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 432e0fb9eea..e245163576b 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -328,7 +328,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { try { CodeableConcept codeableConcept = new CodeableConcept(); Coding codingCode = codeableConcept.addCoding(); - codingCode.setSystem(DeviceStatus.ACTIVE.toCode()); + codingCode.setCode(DeviceStatus.ACTIVE.toCode()); codingCode.setSystem(DeviceStatus.ACTIVE.getSystem()); Task task = new Task(); diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index c245600877d..15221dccabe 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java index 0637f70bcad..ef9c28688e1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java @@ -75,7 +75,7 @@ public class JpaPersistedResourceValidationSupportFromValidationChainTest { final ValidationResult validationResult = validator.validateWithResult(bundleWithBadLibrary); - assertEquals(10, validationResult.getMessages().stream().filter(errorMessagePredicate()).count()); + assertEquals(12, validationResult.getMessages().stream().filter(errorMessagePredicate()).count()); } @Test @@ -92,7 +92,7 @@ public class JpaPersistedResourceValidationSupportFromValidationChainTest { final ValidationResult validationResult = validator.validateWithResult(bundleWithMeasureOnly ); - assertEquals(8, validationResult.getMessages().stream().filter(errorMessagePredicate()).count()); + assertEquals(10, validationResult.getMessages().stream().filter(errorMessagePredicate()).count()); } @Test @@ -106,7 +106,7 @@ public class JpaPersistedResourceValidationSupportFromValidationChainTest { final ValidationResult validationResult = validator.validateWithResult(bundleWithMeasureOnlyNoLibraryReference); - assertEquals(7, validationResult.getMessages().stream().filter(errorMessagePredicate()).count()); + assertEquals(9, validationResult.getMessages().stream().filter(errorMessagePredicate()).count()); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index 4ebb9e6e067..a278e8a7ebd 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -594,24 +594,24 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test fail(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); } myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(14, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); + assertEquals(8, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); - assertEquals(12, myCaptureQueriesListener.getCommitCount()); + assertEquals(6, myCaptureQueriesListener.getCommitCount()); // Validate again (should rely only on caches) myCaptureQueriesListener.clear(); myObservationDao.validate(obs, null, null, null, null, null, null); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(6, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); + assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); assertThat(myCaptureQueriesListener.getUpdateQueriesForCurrentThread()).isEmpty(); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertThat(myCaptureQueriesListener.getInsertQueriesForCurrentThread()).isEmpty(); myCaptureQueriesListener.logDeleteQueriesForCurrentThread(); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); - assertEquals(6, myCaptureQueriesListener.getCommitCount()); + assertEquals(0, myCaptureQueriesListener.getCommitCount()); } /** diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java index 6e89abe6171..0167dca32f2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java @@ -68,7 +68,6 @@ import static org.awaitility.Awaitility.await; import static org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService.CURRENCIES_CODESYSTEM_URL; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -133,7 +132,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue().size()).as(encoded).isEqualTo(1); - assertThat(oo.getIssue().get(0).getDiagnostics()).contains("provided (http://cs#code99) is not in the value set"); + assertThat(oo.getIssue().get(0).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); assertThat(oo.getIssue().get(0).getDiagnostics()).contains("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"); assertThat(oo.getIssueFirstRep().getSeverity()).as(encoded).isEqualTo(OperationOutcome.IssueSeverity.ERROR); @@ -165,7 +164,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue()).hasSize(1); - assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("provided (http://cs#code99) is not in the value set"); + assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); } @@ -202,7 +201,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertThat(oo.getIssue()).hasSize(2); assertThat(oo.getIssue().get(0).getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(0).getSeverity()); - assertThat(oo.getIssue().get(1).getDiagnostics()).contains("provided (http://cs#code99) is not in the value set 'ValueSet[http://vs]'"); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(1).getSeverity()); } @@ -241,7 +240,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue()).hasSize(1); - assertThat(oo.getIssue().get(0).getDiagnostics()).contains("provided (http://cs#code99) is not in the value set"); + assertThat(oo.getIssue().get(0).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); } @@ -335,19 +334,25 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { oo = validateAndReturnOutcome(obs, true); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(1); - assertThat(oo.getIssue().get(0).getDiagnostics()).contains("provided (http://cs#code1) is not in the value set"); - assertThat(oo.getIssue().get(0).getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code1"); - assertThat(oo.getIssue().get(0).getDiagnostics()).contains("HAPI-0702: Unable to expand ValueSet because CodeSystem could not be found: http://cs"); + assertThat(oo.getIssue()).hasSize(2); + OperationOutcome.OperationOutcomeIssueComponent unableToExpandError = oo.getIssue().get(0); + assertThat(unableToExpandError.getDiagnostics()).contains("Unable to expand ValueSet because CodeSystem could not be found: http://cs"); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); - assertEquals(27, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); - assertEquals(4, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue()); - assertEquals("Terminology_TX_NoValid_12", ((StringType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue()); - assertEquals(OperationOutcome.IssueType.PROCESSING, oo.getIssue().get(0).getCode()); - assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity()); - assertThat(oo.getIssue().get(0).getLocation()).hasSize(2); - assertEquals("Observation.value.ofType(Quantity)", oo.getIssue().get(0).getLocation().get(0).getValue()); - assertEquals("Line[27] Col[4]", oo.getIssue().get(0).getLocation().get(1).getValue()); + + OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(1); + assertThat(notInValueSetError.getDiagnostics()).contains("provided (http://cs#code1) was not found in the value set"); + assertThat(notInValueSetError.getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code1"); + assertThat(notInValueSetError.getDiagnostics()).contains("HAPI-0702: Unable to expand ValueSet because CodeSystem could not be found: http://cs"); + assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); + assertEquals(27, ((IntegerType) notInValueSetError.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); + assertEquals(4, ((IntegerType) notInValueSetError.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue()); + assertEquals("Terminology_TX_NoValid_12", ((StringType) notInValueSetError.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue()); + assertEquals(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); + assertEquals(OperationOutcome.IssueSeverity.ERROR, notInValueSetError.getSeverity()); + assertThat(notInValueSetError.getLocation()).hasSize(2); + assertEquals("Observation.value.ofType(Quantity)", notInValueSetError.getLocation().get(0).getValue()); + assertEquals("Line[27] Col[4]", notInValueSetError.getLocation().get(1).getValue()); + } @@ -507,7 +512,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertHasErrors(outcome); String outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); ourLog.info("Validation outcome: {}", outcomeStr); - assertThat(outcomeStr).contains("provided (http://unitsofmeasure.org#cm) is not in the value set"); + assertThat(outcomeStr).contains("provided (http://unitsofmeasure.org#cm) was not found in the value set"); // Before, the VS wasn't pre-expanded. Try again with it pre-expanded runInTransaction(() -> { @@ -535,7 +540,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertHasErrors(outcome); outcomeStr = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome); ourLog.info("Validation outcome: {}", outcomeStr); - assertThat(outcomeStr).contains("provided (http://unitsofmeasure.org#cm) is not in the value set"); + assertThat(outcomeStr).contains("provided (http://unitsofmeasure.org#cm) was not found in the value set"); } @@ -623,7 +628,7 @@ 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); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("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)"); + assertThat(oo.getIssue().get(1).getDiagnostics()).as(encode(oo)).isEqualTo("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)"); // Valid code with no system obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); @@ -639,7 +644,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { // 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"); + obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Vital Signs"); oo = validateAndReturnOutcome(obs); assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("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)"); @@ -648,7 +653,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3"); obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("FOO"); oo = validateAndReturnOutcome(obs); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-category'"); + assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO'"); // Make sure we're caching the validations as opposed to hitting the DB every time myCaptureQueriesListener.clear(); @@ -931,7 +936,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { OperationOutcome oo = validateAndReturnOutcome(vs); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("The code '123' is not valid in the system https://bb", oo.getIssue().get(0).getDiagnostics()); + assertEquals("The code '123' is not valid in the system https://bb (Validation failed)", oo.getIssue().get(0).getDiagnostics()); } @Test @@ -986,7 +991,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.getCode().getCodingFirstRep().setDisplay("Some Code"); outcome = (OperationOutcome) myObservationDao.validate(obs, null, null, null, ValidationModeEnum.CREATE, "http://example.com/structuredefinition", mySrd).getOperationOutcome(); ourLog.debug("Outcome: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); - assertEquals("Unknown code in fragment CodeSystem 'http://example.com/codesystem#foo-foo' for in-memory expansion of ValueSet 'http://example.com/valueset'", outcome.getIssueFirstRep().getDiagnostics()); + assertEquals("Unknown code in fragment CodeSystem 'http://example.com/codesystem#foo-foo'", outcome.getIssueFirstRep().getDiagnostics()); assertEquals(OperationOutcome.IssueSeverity.WARNING, outcome.getIssueFirstRep().getSeverity()); // Correct codesystem, Code in codesystem @@ -1077,7 +1082,7 @@ 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); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("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)"); + assertThat(oo.getIssue().get(2).getDiagnostics()).as(encode(oo)).isEqualTo("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)"); // Valid code with no system obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); @@ -1089,20 +1094,20 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3"); oo = validateAndReturnOutcome(obs); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("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)"); + assertThat(oo.getIssue().get(1).getDiagnostics()).as(encode(oo)).isEqualTo("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)"); // 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"); + obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Vital Signs"); oo = validateAndReturnOutcome(obs); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("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)"); + assertThat(oo.getIssue().get(1).getDiagnostics()).as(encode(oo)).isEqualTo("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)"); // Invalid code in built-in VS/CS obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3"); obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("FOO"); oo = validateAndReturnOutcome(obs); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO' for in-memory expansion of ValueSet 'http://hl7.org/fhir/ValueSet/observation-category'"); + assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("Unknown code 'http://terminology.hl7.org/CodeSystem/observation-category#FOO'"); } @@ -1214,8 +1219,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { OperationOutcome oo = validateAndReturnOutcome(obs); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("Error MY ERROR validating Coding: java.lang.NullPointerException: MY ERROR", oo.getIssueFirstRep().getDiagnostics()); - assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); + assertEquals("Error MY ERROR validating CodeableConcept", oo.getIssueFirstRep().getDiagnostics()); + assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssueFirstRep().getSeverity()); } @Test @@ -1366,7 +1371,9 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { TermCodeSystemVersion csv = new TermCodeSystemVersion(); csv.addConcept().setCode("bar").setDisplay("Bar Code"); - myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(codeSystem, csv, mySrd, Collections.emptyList(), Collections.emptyList()); + IIdType updatedCsId = myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(codeSystem, csv, mySrd, Collections.emptyList(), Collections.emptyList()); + + // Validate a resource containing this codesystem in a field with an extendable binding Patient patient = new Patient(); @@ -1386,10 +1393,11 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { // It would be ok for this to produce 0 issues, or just an information message too assertEquals(2, OperationOutcomeUtil.getIssueCount(myFhirContext, oo)); - assertThat(OperationOutcomeUtil.getFirstIssueDetails(myFhirContext, oo)).contains("None of the codings provided are in the value set 'IdentifierType'"); - assertThat(OperationOutcomeUtil.getFirstIssueDetails(myFhirContext, oo)).contains("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)"); - assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(1).getSeverity()); - assertEquals("Concept Display \"not bar code\" does not match expected \"Bar Code\" for 'http://foo#bar'", oo.getIssue().get(1).getDiagnostics()); + OperationOutcome.OperationOutcomeIssueComponent notInValueSetIssue = oo.getIssue().get(1); + assertThat(notInValueSetIssue.getDiagnostics()).contains("None of the codings provided are in the value set 'IdentifierType'"); + assertThat(notInValueSetIssue.getDiagnostics()).contains("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)"); + assertEquals(OperationOutcome.IssueSeverity.WARNING, notInValueSetIssue.getSeverity()); + assertEquals("Concept Display \"not bar code\" does not match expected \"Bar Code\" for 'http://foo#bar'", oo.getIssue().get(0).getDiagnostics()); } private OperationOutcome doTestValidateResourceContainingProfileDeclaration(String methodName, EncodingEnum enc) throws IOException { @@ -1465,7 +1473,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertHasErrors(oo); String outputString = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(outputString); - assertThat(outputString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"); + assertThat(outputString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it could not be found"); } @Test @@ -1498,7 +1506,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertHasErrors(oo); String outputString = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo); ourLog.info(outputString); - assertThat(outputString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"); + assertThat(outputString).contains("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it could not be found"); } @Test @@ -1572,10 +1580,14 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { @Test void testValidateCommonCodes_Ucum_ErrorMessageIsPreserved() { + + String loincCode = "1234"; + addLoincCodeToCodeSystemDao(loincCode); + Observation input = new Observation(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(ObservationStatus.AMENDED); - input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); + input.getCode().addCoding().setSystem("http://loinc.org").setCode(loincCode).setDisplay("FOO"); input.setValue(new Quantity( null, 123, @@ -1592,7 +1604,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertEquals(15, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); assertEquals(4, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue()); assertEquals("Terminology_PassThrough_TX_Message", ((StringType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue()); - assertEquals("Error processing unit 'MG/DL': The unit 'DL' is unknown' at position 3 for 'http://unitsofmeasure.org#MG/DL'", oo.getIssue().get(0).getDiagnostics()); + assertEquals("Error processing unit 'MG/DL': The unit 'DL' is unknown' at position 3 (for 'http://unitsofmeasure.org#MG/DL')", oo.getIssue().get(0).getDiagnostics()); assertEquals(OperationOutcome.IssueType.PROCESSING, oo.getIssue().get(0).getCode()); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity()); assertThat(oo.getIssue().get(0).getLocation()).hasSize(2); @@ -1602,10 +1614,14 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { @Test void testValidateCommonCodes_Currency_ErrorMessageIsPreserved() { + String loincCode = "1234"; + + addLoincCodeToCodeSystemDao(loincCode); + Observation input = new Observation(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(ObservationStatus.AMENDED); - input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); + input.getCode().addCoding().setSystem("http://loinc.org").setCode(loincCode).setDisplay("FOO"); input.setValue(new Quantity( null, 123, @@ -1622,7 +1638,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertEquals(15, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); assertEquals(4, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue()); assertEquals("Terminology_PassThrough_TX_Message", ((StringType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue()); - assertEquals("Unknown code 'urn:iso:std:iso:4217#blah' for 'urn:iso:std:iso:4217#blah'", oo.getIssue().get(0).getDiagnostics()); + assertEquals("Unknown code 'urn:iso:std:iso:4217#blah'", oo.getIssue().get(0).getDiagnostics()); assertEquals(OperationOutcome.IssueType.PROCESSING, oo.getIssue().get(0).getCode()); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity()); assertThat(oo.getIssue().get(0).getLocation()).hasSize(2); @@ -2064,6 +2080,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { codeElement.addType().setCode("CodeableConcept"); codeElement.getBinding().setStrength(Enumerations.BindingStrength.REQUIRED); codeElement.getBinding().setValueSet("http://vs"); + String encodedStructureDefinition = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(sd); + myStructureDefinitionDao.create(sd, new SystemRequestDetails()); CodeSystem cs = new CodeSystem(); @@ -2073,12 +2091,17 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { cs.addConcept() .setCode("8302-2") .setDisplay("Body Height"); + String encodedCodeSystem = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(cs); + myCodeSystemDao.create(cs, new SystemRequestDetails()); ValueSet vs = new ValueSet(); vs.setUrl("http://vs"); vs.setStatus(Enumerations.PublicationStatus.ACTIVE); vs.getCompose().addInclude().setSystem("http://cs"); + String encodedValueSet = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(vs); + + myValueSetDao.create(vs, new SystemRequestDetails()); if (thePreCalculateExpansion) { @@ -2099,8 +2122,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { obs.setSubject(new Reference("Patient/123")); obs.setValue(new Quantity(null, 123, "http://unitsofmeasure.org", "[in_i]", "in")); - String encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs); - MethodOutcome outcome = myObservationDao.validate(obs, null, encoded, EncodingEnum.JSON, ValidationModeEnum.CREATE, null, new SystemRequestDetails()); + String encodedResource = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs); + MethodOutcome outcome = myObservationDao.validate(obs, null, encodedResource, EncodingEnum.JSON, ValidationModeEnum.CREATE, null, new SystemRequestDetails()); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); ourLog.info("Outcome: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo).replace("\"resourceType\"", "\"resType\"")); @@ -2111,7 +2134,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { assertThat(oo.getIssue()).hasSize(2); badDisplayIssue = oo.getIssue().get(1); - OperationOutcome.OperationOutcomeIssueComponent noGoodCodings = oo.getIssue().get(0); + OperationOutcome.OperationOutcomeIssueComponent noGoodCodings = oo.getIssue().get(1); assertEquals("error", noGoodCodings.getSeverity().toCode()); assertEquals("None of the codings provided are in the value set 'ValueSet[http://vs]' (http://vs), and a coding from this value set is required) (codes = http://cs#8302-2)", noGoodCodings.getDiagnostics()); @@ -2160,18 +2183,42 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test { @Test public void testKnownCodeSystemUnknownValueSetUri() { - CodeSystem cs = new CodeSystem(); - cs.setUrl(ITermLoaderSvc.LOINC_URI); - cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); - cs.addConcept().setCode("10013-1"); - cs.setId(LOINC_LOW); - myCodeSystemDao.update(cs); + String loincCode = "10013-1"; - IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(new UriType("http://fooVs"), null, new StringType("10013-1"), new StringType(ITermLoaderSvc.LOINC_URI), null, null, null, mySrd); + addLoincCodeToCodeSystemDao(loincCode); + + IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(new UriType("http://fooVs"), null, new StringType(loincCode), new StringType(ITermLoaderSvc.LOINC_URI), null, null, null, mySrd); assertFalse(result.isOk()); assertEquals("Validator is unable to provide validation for 10013-1#http://loinc.org - Unknown or unusable ValueSet[http://fooVs]", result.getMessage()); } + private void addLoincCodeToCodeSystemDao(String loincCode) { + CodeSystem cs = new CodeSystem(); + cs.setUrl(ITermLoaderSvc.LOINC_URI); + cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); + cs.addConcept().setCode(loincCode); + cs.setId(LOINC_LOW); + myCodeSystemDao.update(cs); + } + + @Test + public void testValidateObservationWithVitalSignsLoincCode() { + Observation obs = new Observation(); + obs.setStatus(ObservationStatus.FINAL); + obs.setCode( + new CodeableConcept().addCoding( + new Coding().setSystem("http://loinc.org").setCode("8302-2").setDisplay("Body height") + )); + obs.getSubject().setReference("Patient/A"); + obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); + obs.getText().getDiv().setValue("
hello
"); + + obs.setEffective(new DateTimeType("2020-01-01")); + + obs.setValue(new Quantity().setUnit("cm").setValue(51)); + OperationOutcome oo = validateAndReturnOutcome(obs); + assertHasNoErrors(oo); + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java index 9e9cf7ee8ba..7b5e063681c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java @@ -343,7 +343,7 @@ public class FhirResourceDaoR4ValueSetTest extends BaseJpaR4Test { IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isOk()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); - assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage()); + assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for 'http://acme.org#11378-7' for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage()); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorHttpR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorHttpR4Test.java index 4ec631cc9a9..0155d2a571e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorHttpR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptorHttpR4Test.java @@ -49,6 +49,7 @@ public class RepositoryValidatingInterceptorHttpR4Test extends BaseJpaR4Test { @Test public void testValidationIsSkippedOnAutoCreatedPlaceholderReferencesIfConfiguredToDoSo() { + createLocalCsAndVs(); List rules = newRuleBuilder() .forResourcesOfType("Observation") .requireValidationToDeclaredProfiles() @@ -56,7 +57,7 @@ public class RepositoryValidatingInterceptorHttpR4Test extends BaseJpaR4Test { myValInterceptor.setRules(rules); Observation obs = new Observation(); - obs.getCode().addCoding().setSystem("http://foo").setCode("123").setDisplay("help im a bug"); + obs.getCode().addCoding().setSystem("http://example.com/my_code_system").setCode("A").setDisplay("Code A"); obs.setStatus(Observation.ObservationStatus.AMENDED); MethodOutcome outcome = myRestfulServerExtension @@ -68,11 +69,12 @@ public class RepositoryValidatingInterceptorHttpR4Test extends BaseJpaR4Test { String operationOutcomeEncoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome()); ourLog.info("Outcome: {}", operationOutcomeEncoded); - assertThat(operationOutcomeEncoded).contains("All observations should have a subject"); + assertThat(operationOutcomeEncoded).contains("In general, all observations should have a subject"); } @Test public void testValidationOutcomeAddedToRequestResponse() { + createLocalCsAndVs(); List rules = newRuleBuilder() .forResourcesOfType("Observation") .requireValidationToDeclaredProfiles() @@ -81,7 +83,7 @@ public class RepositoryValidatingInterceptorHttpR4Test extends BaseJpaR4Test { myValInterceptor.setRules(rules); Observation obs = new Observation(); - obs.getCode().addCoding().setSystem("http://foo").setCode("123").setDisplay("help im a bug"); + obs.getCode().addCoding().setSystem("http://example.com/my_code_system").setCode("A").setDisplay("Code A"); obs.setStatus(Observation.ObservationStatus.AMENDED); MethodOutcome outcome = myRestfulServerExtension @@ -93,7 +95,7 @@ public class RepositoryValidatingInterceptorHttpR4Test extends BaseJpaR4Test { String operationOutcomeEncoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome()); ourLog.info("Outcome: {}", operationOutcomeEncoded); - assertThat(operationOutcomeEncoded).contains("All observations should have a subject"); + assertThat(operationOutcomeEncoded).contains("In general, all observations should have a subject"); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java index 8625881de60..c663df22986 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/ValidationMessageSuppressingInterceptorTest.java @@ -64,7 +64,7 @@ public class ValidationMessageSuppressingInterceptorTest extends BaseResourcePro assertHasWarnings(oo); String encode = encode(oo); ourLog.info(encode); - assertThat(encode).contains("All observations should have a performer"); + assertThat(encode).contains("In general, all observations should have a performer"); } @Test @@ -92,13 +92,16 @@ public class ValidationMessageSuppressingInterceptorTest extends BaseResourcePro } catch (UnprocessableEntityException e) { String encode = encode(e.getOperationOutcome()); ourLog.info(encode); - assertThat(encode).contains("Unknown code 'http://loinc.org#59408-5'"); + assertThat(encode).contains("Slice 'Observation.code.coding:PulseOx': a matching slice is required, but not found"); } } // With suppression ValidationMessageSuppressingInterceptor interceptor = new ValidationMessageSuppressingInterceptor(); - interceptor.addMessageSuppressionPatterns("Unknown code 'http://loinc.org#59408-5'"); + interceptor.addMessageSuppressionPatterns("Unable to validate code http://loinc.org#not-a-real-code - Code is not found in CodeSystem: http://loinc.org", + "Slice 'Observation.code.coding:PulseOx': a matching slice is required, but not found (from http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry|3.1.1)", + "This element does not match any known slice defined in the profile http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry|3.1.1"); + myInterceptorRegistry.registerInterceptor(interceptor); { Observation inputObs = loadResource(myFhirContext, Observation.class, "/r4/uscore/observation-pulseox.json"); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/DiffProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/DiffProviderR4Test.java index d724071e0e1..6c159860206 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/DiffProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/DiffProviderR4Test.java @@ -47,8 +47,8 @@ public class DiffProviderR4Test extends BaseResourceProviderR4Test { assertEquals("replace", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 0, "operation", "type")); assertEquals("Patient.text.div", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 0, "operation", "path")); - assertEquals("
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 0, "operation", "previousValue")); - assertEquals("
SMITH
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 0, "operation", "value")); + assertEquals("
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 0, "operation", "previousValue")); + assertEquals("
SMITH
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 0, "operation", "value")); assertEquals("insert", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 1, "operation", "type")); assertEquals("Patient.name", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 1, "operation", "path")); @@ -86,8 +86,8 @@ public class DiffProviderR4Test extends BaseResourceProviderR4Test { assertEquals("replace", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 2, "operation", "type")); assertEquals("Patient.text.div", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 2, "operation", "path")); - assertEquals("
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 2, "operation", "previousValue")); - assertEquals("
SMITH
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 2, "operation", "value")); + assertEquals("
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 2, "operation", "previousValue")); + assertEquals("
SMITH
", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 2, "operation", "value")); assertEquals("insert", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 3, "operation", "type")); assertEquals("Patient.name", FhirPatchApplyR4Test.extractPartValuePrimitive(diff, 3, "operation", "path")); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index a01c79cc0eb..1c4ead58ee2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -573,7 +573,7 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test ourLog.info(resp); assertTrue(((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue()); - assertEquals("Concept Display \"Old Systolic blood pressure.inspiration - expiration\" does not match expected \"Systolic blood pressure.inspiration - expiration\"", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); + assertEquals("Concept Display \"Old Systolic blood pressure.inspiration - expiration\" does not match expected \"Systolic blood pressure.inspiration - expiration\" for 'http://acme.org#8452-5'", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString()); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 7feada87a6d..f33bbcd2c28 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -1,11 +1,5 @@ package ca.uhn.fhir.jpa.provider.r4; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; - import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.Msg; @@ -203,8 +197,16 @@ import static ca.uhn.fhir.rest.param.BaseParamWithPrefix.MSG_PREFIX_INVALID_FORM import static ca.uhn.fhir.util.TestUtil.sleepAtLeast; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + import static org.mockito.Mockito.when; @SuppressWarnings("Duplicates") @@ -2108,7 +2110,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { String respString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8); ourLog.debug(respString); assertEquals(200, resp.getStatusLine().getStatusCode()); - assertThat(respString).contains("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it is unknown"); + assertThat(respString).contains("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found"); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index e7f1a617716..634d309a699 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -1642,7 +1642,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test implements IValueSet assertTrue(outcome.isOk()); assertEquals("28571000087109", outcome.getCode()); assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay()); - assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\" for in-memory expansion of ValueSet: http://ehealthontario.ca/fhir/ValueSet/vaccinecode", outcome.getMessage()); + assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\" for 'http://snomed.info/sct#28571000087109' for in-memory expansion of ValueSet: http://ehealthontario.ca/fhir/ValueSet/vaccinecode", outcome.getMessage()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://ehealthontario.ca/fhir/ValueSet/vaccinecode", outcome.getSourceDetails()); assertEquals("0.17", outcome.getCodeSystemVersion()); @@ -1689,7 +1689,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test implements IValueSet assertEquals("28571000087109", outcome.getCode()); assertEquals("MODERNA COVID-19 mRNA-1273", outcome.getDisplay()); assertEquals("0.17", outcome.getCodeSystemVersion()); - assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\"", outcome.getMessage()); + assertEquals("Concept Display \"BLAH\" does not match expected \"MODERNA COVID-19 mRNA-1273\" for 'http://snomed.info/sct#28571000087109'", outcome.getMessage()); // Validate code - good code, good display codeSystemUrl = "http://snomed.info/sct"; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/r4/uscore/observation-pulseox.json b/hapi-fhir-jpaserver-test-r4/src/test/resources/r4/uscore/observation-pulseox.json index 5728c9d01bc..b4519f9156d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/resources/r4/uscore/observation-pulseox.json +++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/r4/uscore/observation-pulseox.json @@ -38,7 +38,7 @@ }, { "system": "http://loinc.org", - "code": "59408-5", + "code": "not-a-real-code", "display": "Oxygen saturation in Arterial blood by Pulse oximetry" }, { diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index c5deacf31f7..86ee6a2954d 100644 --- a/hapi-fhir-jpaserver-test-r4b/pom.xml +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 866c2b7cccd..04f09e776fe 100644 --- a/hapi-fhir-jpaserver-test-r5/pom.xml +++ b/hapi-fhir-jpaserver-test-r5/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java index 86ab3dc5ce7..8d96a32e00d 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5ValueSetTest.java @@ -116,7 +116,7 @@ public class FhirResourceDaoR5ValueSetTest extends BaseJpaR5Test { IValidationSupport.CodeValidationResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd); assertTrue(result.isOk()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); - assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage()); + assertEquals("Concept Display \"Systolic blood pressure at First encounterXXXX\" does not match expected \"Systolic blood pressure at First encounter\" for 'http://acme.org#11378-7' for in-memory expansion of ValueSet: http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", result.getMessage()); } @Test diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 25b4ed90772..c130cb74fbd 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java index 9882f8393e6..a584ebc575d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java @@ -187,6 +187,7 @@ import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.utils.validation.IMessagingServices; 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; @@ -194,6 +195,7 @@ 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.hl7.fhir.utilities.validation.ValidationMessage; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; @@ -211,6 +213,7 @@ import org.springframework.transaction.PlatformTransactionManager; import java.io.IOException; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -1022,12 +1025,44 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil } @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; + public EnumSet policyForResource(IResourceValidator validator, Object appContext, + org.hl7.fhir.r5.model.StructureDefinition type, String path) { + return EnumSet.allOf(ResourceValidationAction.class); } @Override - public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, String containerType, String containerId, Element.SpecialElement containingResourceType, String path, String url) { + public EnumSet policyForElement(IResourceValidator validator, Object appContext, + org.hl7.fhir.r5.model.StructureDefinition structure, ElementDefinition element, String path) { + return EnumSet.allOf(ElementValidationAction.class); + } + @Override + public EnumSet policyForCodedContent(IResourceValidator validator, + Object appContext, + String stackPath, + ElementDefinition definition, + org.hl7.fhir.r5.model.StructureDefinition structure, + BindingKind kind, + AdditionalBindingPurpose purpose, + org.hl7.fhir.r5.model.ValueSet valueSet, + List systems) { + return EnumSet.allOf(CodedContentValidationAction.class); + } + + @Override + public List getImpliedProfilesForResource(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, org.hl7.fhir.r5.model.StructureDefinition structure, Element resource, boolean valid, IMessagingServices msgServices, List messages) { + return List.of(); + } + + @Override + public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, + Object appContext, + org.hl7.fhir.r5.model.StructureDefinition structure, + ElementDefinition element, + String containerType, + String containerId, + Element.SpecialElement containingResourceType, + String path, + String url) { return ContainedReferenceValidationPolicy.CHECK_VALID; } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/R4ValidationTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/R4ValidationTestUtil.java index fc11048a6fc..7312223b6d9 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/R4ValidationTestUtil.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/R4ValidationTestUtil.java @@ -30,6 +30,10 @@ public final class R4ValidationTestUtil { private R4ValidationTestUtil() { } + public static void assertHasFatals(OperationOutcome theOperationOutcome) { + assertThat(hasValidationIssuesWithSeverity(theOperationOutcome, OperationOutcome.IssueSeverity.FATAL)).as("Expected validation errors, found none").isTrue(); + } + public static void assertHasErrors(OperationOutcome theOperationOutcome) { assertThat(hasValidationIssuesWithSeverity(theOperationOutcome, OperationOutcome.IssueSeverity.ERROR)).as("Expected validation errors, found none").isTrue(); } @@ -55,4 +59,8 @@ public final class R4ValidationTestUtil { public static void assertErrorDiagnosticContainsString(OperationOutcome theOo, String theExpectedDiagnosticSubstring) { assertThat(theOo.getIssue().stream().anyMatch(t -> t.getSeverity() == OperationOutcome.IssueSeverity.ERROR && t.getDiagnostics().contains(theExpectedDiagnosticSubstring))).as("Expected a validation error with diagnostic containing '" + theExpectedDiagnosticSubstring + "', found none").isTrue(); } + + public static void assertFatalDiagnosticContainsString(OperationOutcome theOo, String theExpectedDiagnosticSubstring) { + assertThat(theOo.getIssue().stream().anyMatch(t -> t.getSeverity() == OperationOutcome.IssueSeverity.FATAL && t.getDiagnostics().contains(theExpectedDiagnosticSubstring))).as("Expected a validation error with diagnostic containing '" + theExpectedDiagnosticSubstring+ "', found none").isTrue(); + } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index fb444ed51aa..3c086a63664 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 8e57d090771..e163c7a4b6d 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index f302995d83c..1350a8b6d46 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -78,6 +78,12 @@ info.debatty java-string-similarity 1.2.1 + + + net.jcip + jcip-annotations + + org.springframework diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 0f2930522fc..ab45d0148cf 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index a1788d227c4..5272e175284 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java index c3407162c16..0b2375d3cf5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java @@ -53,11 +53,13 @@ import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.text.StringEscapeUtils; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; import java.io.IOException; @@ -858,7 +860,7 @@ public class ResponseHighlighterInterceptor { // Try to extract the narrative from the resource. First, just see if there // is a narrative in the normal spot. - XhtmlNode xhtmlNode = extractNarrativeFromDomainResource(theResource, ctx); + XhtmlNode xhtmlNode = extractNarrativeFromElement(theResource, ctx); // If the resource is a document, see if the Composition has a narrative if (xhtmlNode == null && "Bundle".equals(ctx.getResourceType(theResource))) { @@ -866,7 +868,7 @@ public class ResponseHighlighterInterceptor { IBaseResource firstResource = ctx.newTerser().getSingleValueOrNull(theResource, "entry.resource", IBaseResource.class); if (firstResource != null && "Composition".equals(ctx.getResourceType(firstResource))) { - xhtmlNode = extractNarrativeFromDomainResource(firstResource, ctx); + xhtmlNode = extractNarrativeFromComposition(firstResource, ctx); } } } @@ -897,6 +899,34 @@ public class ResponseHighlighterInterceptor { return null; } + private XhtmlNode extractNarrativeFromComposition(IBaseResource theComposition, FhirContext theCtx) { + XhtmlNode retVal = new XhtmlNode(NodeType.Element, "div"); + + XhtmlNode xhtmlNode = extractNarrativeFromElement(theComposition, theCtx); + if (xhtmlNode != null) { + retVal.add(xhtmlNode); + } + + List sections = theCtx.newTerser().getValues(theComposition, "section"); + for (IBase section : sections) { + String title = theCtx.newTerser().getSinglePrimitiveValueOrNull(section, "title"); + if (isNotBlank(title)) { + XhtmlNode sectionNarrative = extractNarrativeFromElement(section, theCtx); + if (sectionNarrative != null && sectionNarrative.hasChildren()) { + XhtmlNode titleNode = new XhtmlNode(NodeType.Element, "h1"); + titleNode.addText(title); + retVal.add(titleNode); + retVal.add(sectionNarrative); + } + } + } + + if (retVal.isEmpty()) { + return null; + } + return retVal; + } + private void writeLength(HttpServletResponse theServletResponse, int theLength) throws IOException { double kb = ((double) theLength) / FileUtils.ONE_KB; if (kb <= 1000) { @@ -1014,11 +1044,15 @@ public class ResponseHighlighterInterceptor { myShowNarrative = theShowNarrative; } + /** + * Extracts the narrative from an element (typically a FHIR resource) that holds + * a "text" element + */ @Nullable - private static XhtmlNode extractNarrativeFromDomainResource(@Nonnull IBaseResource theResource, FhirContext ctx) { - if (ctx.getResourceDefinition(theResource).getChildByName("text") != null) { + private static XhtmlNode extractNarrativeFromElement(@Nonnull IBase theElement, FhirContext ctx) { + if (ctx.getElementDefinition(theElement.getClass()).getChildByName("text") != null) { return ctx.newTerser() - .getSingleValue(theResource, "text.div", XhtmlNode.class) + .getSingleValue(theElement, "text.div", XhtmlNode.class) .orElse(null); } return null; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java index 7d14a79b30e..b42e56cd74c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java @@ -206,6 +206,9 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv terser.addElement(retVal, "id", UUID.randomUUID().toString()); terser.addElement(retVal, "name", "RestServer"); + IBase text = terser.addElement(retVal, "text"); + terser.addElement(text, "status", "generated"); + terser.addElement(text, "div", "
HAPI-FHIR Server
"); terser.addElement(retVal, "publisher", myPublisher); terser.addElement(retVal, "date", conformanceDate(configuration)); terser.addElement( @@ -704,6 +707,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv myContext.getResourceDefinition("OperationDefinition").newInstance(); FhirTerser terser = myContext.newTerser(); + IBase text = terser.addElement(op, "text"); + terser.addElement(text, "status", "generated"); + terser.addElement(text, "div", "
Operation Named Search
"); + terser.addElement(op, "status", "active"); terser.addElement(op, "kind", "query"); terser.addElement(op, "affectsState", "false"); @@ -765,6 +772,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv myContext.getResourceDefinition("OperationDefinition").newInstance(); FhirTerser terser = myContext.newTerser(); + IBase text = terser.addElement(op, "text"); + terser.addElement(text, "status", "generated"); + terser.addElement(text, "div", "
Operation
"); + terser.addElement(op, "status", "active"); terser.addElement(op, "kind", "operation"); diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index b0a716b64f1..d5120b2184f 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml index 00d71e8c5ac..69cf42cccf3 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index d587457dcb0..f4f68b41318 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml index 3cd1e2cec90..291c3392219 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml @@ -7,7 +7,7 @@ hapi-fhir ca.uhn.hapi.fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 9adff03bb69..edcc70ccee3 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 0ecee1c1020..07000f14888 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 9c3d26cd3a6..672ad18349a 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index 014a4cec8c7..73c42ccc46b 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 0c5d86a7480..0861c6b1e14 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 8e101617ec4..e4751243985 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 43c03e0d63c..ca7b50aea4f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index a41d726d92f..56a15b62095 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 5f59f9f0a61..c3784baf3c2 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index a1c024ce368..59bd55dabc3 100644 --- a/hapi-fhir-storage-batch2-jobs/pom.xml +++ b/hapi-fhir-storage-batch2-jobs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 47e4178f984..87824ecf0a6 100644 --- a/hapi-fhir-storage-batch2-test-utilities/pom.xml +++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 03782d27c60..1a621440a23 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index b08a677b3fd..c724117920b 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index a776ea675bc..3772a8fbdb6 100644 --- a/hapi-fhir-storage-mdm/pom.xml +++ b/hapi-fhir-storage-mdm/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index 1f0b1da0f64..8d2524eaaef 100644 --- a/hapi-fhir-storage-test-utilities/pom.xml +++ b/hapi-fhir-storage-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 36683401bf2..0bd777bbf3d 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -166,8 +166,13 @@ ${project.version} test + + ca.uhn.hapi.fhir + org.hl7.fhir.r5 + ${fhir_core_version} + - + 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 index af7151bf793..166f3a50383 100644 --- 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 @@ -24,16 +24,19 @@ 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.IMessagingServices; 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.hl7.fhir.utilities.validation.ValidationMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import java.util.Arrays; +import java.util.EnumSet; import java.util.List; public class ValidatorPolicyAdvisor implements IValidationPolicyAdvisor { @@ -58,22 +61,41 @@ public class ValidatorPolicyAdvisor implements IValidationPolicyAdvisor { } @Override - public CodedContentValidationPolicy policyForCodedContent( - IResourceValidator iResourceValidator, - Object o, - String s, - ElementDefinition elementDefinition, - StructureDefinition structureDefinition, - BindingKind bindingKind, + public EnumSet policyForResource( + IResourceValidator validator, Object appContext, StructureDefinition type, String path) { + return EnumSet.allOf(ResourceValidationAction.class); + } + + @Override + public EnumSet policyForElement( + IResourceValidator validator, + Object appContext, + StructureDefinition structure, + ElementDefinition element, + String path) { + return EnumSet.allOf(ElementValidationAction.class); + } + + @Override + public EnumSet policyForCodedContent( + IResourceValidator validator, + Object appContext, + String stackPath, + ElementDefinition definition, + StructureDefinition structure, + BindingKind kind, + AdditionalBindingPurpose purpose, ValueSet valueSet, - List list) { - return CodedContentValidationPolicy.CODE; + List systems) { + return EnumSet.allOf(CodedContentValidationAction.class); } @Override public ContainedReferenceValidationPolicy policyForContained( IResourceValidator validator, Object appContext, + StructureDefinition structure, + ElementDefinition element, String containerType, String containerId, Element.SpecialElement containingResourceType, @@ -81,4 +103,18 @@ public class ValidatorPolicyAdvisor implements IValidationPolicyAdvisor { String url) { return ContainedReferenceValidationPolicy.CHECK_VALID; } + + @Override + public List getImpliedProfilesForResource( + IResourceValidator validator, + Object appContext, + String stackPath, + ElementDefinition definition, + StructureDefinition structure, + Element resource, + boolean valid, + IMessagingServices msgServices, + List messages) { + return Arrays.asList(); + } } 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 2666695bd67..14813f8b394 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 @@ -43,8 +43,11 @@ import org.hl7.fhir.utilities.CanonicalPair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URISyntaxException; +import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Set; public class ValidatorResourceFetcher implements IValidatorResourceFetcher { @@ -131,7 +134,8 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { } @Override - public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) { + public CanonicalResource fetchCanonicalResource(IResourceValidator validator, Object appContext, String url) + throws URISyntaxException { return null; } @@ -139,4 +143,9 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { public boolean fetchesCanonicalResource(IResourceValidator iResourceValidator, String s) { return false; } + + @Override + public Set fetchCanonicalResourceVersions(IResourceValidator validator, Object appContext, String url) { + return Collections.emptySet(); + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java index 83617855c6b..338387780a9 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java @@ -33,6 +33,7 @@ import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.AuditEvent; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; import java.nio.charset.StandardCharsets; import java.util.Date; @@ -306,6 +307,10 @@ public class BalpAuditCaptureInterceptor { AuditEvent auditEvent = new AuditEvent(); auditEvent.getMeta().addProfile(theProfile.getProfileUrl()); + auditEvent + .getText() + .setDiv(new XhtmlNode().setValue("
Audit Event
")) + .setStatus(org.hl7.fhir.r4.model.Narrative.NarrativeStatus.GENERATED); auditEvent .getType() .setSystem(BalpConstants.CS_AUDIT_EVENT_TYPE) diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index a8fc86af634..f9c2ab88c0a 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index d890cde03c0..f445f893582 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index c66b90392d7..6e0c56685ea 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml 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 9b2f1096929..7b6f58cf7b7 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 @@ -31,6 +31,7 @@ import org.hl7.fhir.dstu3.terminologies.ValueSetExpander; import org.hl7.fhir.dstu3.utils.INarrativeGenerator; import org.hl7.fhir.dstu3.utils.validation.IResourceValidator; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.i18n.I18nBase; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationOptions; @@ -313,7 +314,8 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext @Override public ValidationResult validateCode(String theSystem, String theCode, String theDisplay) { - ValidationOptions options = new ValidationOptions(); + ValidationOptions options = new ValidationOptions(FhirPublication.fromCode( + myValidationSupport.getFhirContext().getVersion().toString())); IValidationSupport.CodeValidationResult result = myValidationSupport.validateCode( new ValidationSupportContext(myValidationSupport), convertConceptValidationOptions(options), @@ -351,7 +353,8 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) { IValidationSupport.CodeValidationResult outcome; - ValidationOptions options = new ValidationOptions(); + ValidationOptions options = new ValidationOptions(FhirPublication.fromCode( + myValidationSupport.getFhirContext().getVersion().toString())); if (isNotBlank(theVs.getUrl())) { outcome = myValidationSupport.validateCode( new ValidationSupportContext(myValidationSupport), diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java index e7dfd0d1c9b..a1e84c1bc26 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java @@ -7,13 +7,13 @@ import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.fhirpath.IFhirPathEvaluationContext; import ca.uhn.fhir.i18n.Msg; import jakarta.annotation.Nonnull; +import org.hl7.fhir.dstu3.fhirpath.ExpressionNode; +import org.hl7.fhir.dstu3.fhirpath.FHIRPathEngine; +import org.hl7.fhir.dstu3.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.dstu3.fhirpath.TypeDetails; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.model.Base; -import org.hl7.fhir.dstu3.model.ExpressionNode; import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.TypeDetails; -import org.hl7.fhir.dstu3.utils.FHIRPathEngine; -import org.hl7.fhir.dstu3.utils.FHIRPathUtilityClasses.FunctionDetails; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 381482bf69e..8b588c3d835 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 9f9b20bd1bd..d18b6799bf3 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml 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 ed09a4bf5ec..436338c8443 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 @@ -48,6 +48,29 @@ import java.util.Set; import static org.apache.commons.lang3.StringUtils.isNotBlank; public final class HapiWorkerContext extends I18nBase implements IWorkerContext { + private static final List PRIMITIVE_TYPES = Arrays.asList( + "boolean", + "integer", + "integer64", + "string", + "decimal", + "uri", + "base64Binary", + "instant", + "date", + "dateTime", + "time", + "code", + "oid", + "id", + "markdown", + "unsignedInt", + "positiveInt", + "uuid", + "xhtml", + "url", + "canonical"); + private final FhirContext myCtx; private final Cache myFetchedResourceCache; private IValidationSupport myValidationSupport; @@ -410,6 +433,12 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } } + @Override + public T fetchResource( + Class theClass, String theUri, String theVersion) { + return fetchResource(theClass, theUri + "|" + theVersion); + } + @Override public T fetchResourceWithException(Class theClass, String theUri) throws FHIRException { @@ -454,4 +483,9 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } return retVal; } + + @Override + public boolean isPrimitiveType(String theType) { + return PRIMITIVE_TYPES.contains(theType); + } } diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java index 5c492e158e3..2c04412d4b8 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java @@ -10,14 +10,14 @@ import jakarta.annotation.Nonnull; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.r4.fhirpath.ExpressionNode; +import org.hl7.fhir.r4.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r4.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r4.fhirpath.TypeDetails; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4.model.Base; -import org.hl7.fhir.r4.model.ExpressionNode; import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.TypeDetails; import org.hl7.fhir.r4.model.ValueSet; -import org.hl7.fhir.r4.utils.FHIRPathEngine; -import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.FunctionDetails; import java.util.List; import java.util.Optional; @@ -94,13 +94,20 @@ public class FhirPathR4 implements IFhirPath { myEngine.setHostServices(new FHIRPathEngine.IEvaluationContext() { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, + Object appContext, + String name, + boolean beforeContext, + boolean explicitConstant) throws PathEngineException { return null; } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -110,34 +117,45 @@ public class FhirPathR4 implements IFhirPath { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String theUrl, Base theRefContext) throws FHIRException { - return (Base) theEvaluationContext.resolveReference(new IdType(theUrl), theRefContext); + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) + throws FHIRException { + return (Base) theEvaluationContext.resolveReference(new IdType(url), refContext); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } }); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptorTest.java index 8a41c6fcb2c..d60cb020257 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptorTest.java @@ -46,6 +46,7 @@ import org.hl7.fhir.r4.model.Composition; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Narrative; import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Parameters; @@ -268,10 +269,19 @@ public class ResponseHighlighterInterceptorTest { bundle.setType(Bundle.BundleType.DOCUMENT); Composition composition = new Composition(); composition.getText().setDivAsString("
HELLO
"); + + // Add sections with title and narrative (should be includee) + composition.addSection().setTitle("Section 1").getText().setDivAsString("
HELLO 2
"); + composition.addSection().setTitle("Section 2").getText().setDivAsString("
HELLO 3
"); + + // Add sections with no title or no narrative (should not be included) + composition.addSection().setTitle("Section 3").getText().setDivAsString(""); + composition.addSection().setTitle("").getText().setDivAsString("
HELLO 5
"); + bundle.addEntry().setResource(composition); String outcome = ourInterceptor.extractNarrativeHtml(newRequest(), bundle); - assertEquals("
HELLO
", outcome); + assertEquals("
HELLO

Section 1

HELLO 2

Section 2

HELLO 3
", outcome); } @Test diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 9024c5019dc..b0c3b0b2cf2 100644 --- a/hapi-fhir-structures-r4b/pom.xml +++ b/hapi-fhir-structures-r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java index 8d7d3c5ff9d..fd5570b6f33 100644 --- a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/fhirpath/FhirPathR4B.java @@ -10,14 +10,14 @@ import jakarta.annotation.Nonnull; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.r4b.fhirpath.ExpressionNode; +import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r4b.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r4b.fhirpath.TypeDetails; import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4b.model.Base; -import org.hl7.fhir.r4b.model.ExpressionNode; import org.hl7.fhir.r4b.model.IdType; -import org.hl7.fhir.r4b.model.TypeDetails; import org.hl7.fhir.r4b.model.ValueSet; -import org.hl7.fhir.r4b.utils.FHIRPathEngine; -import org.hl7.fhir.r4b.utils.FHIRPathUtilityClasses.FunctionDetails; import java.util.List; import java.util.Optional; @@ -91,13 +91,20 @@ public class FhirPathR4B implements IFhirPath { myEngine.setHostServices(new FHIRPathEngine.IEvaluationContext() { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, + Object appContext, + String name, + boolean beforeContext, + boolean explicitConstant) throws PathEngineException { return null; } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -107,34 +114,45 @@ public class FhirPathR4B implements IFhirPath { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String theUrl, Base refContext) throws FHIRException { - return (Base) theEvaluationContext.resolveReference(new IdType(theUrl), refContext); + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) + throws FHIRException { + return (Base) theEvaluationContext.resolveReference(new IdType(url), refContext); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } }); diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 23620fae94f..0aaa93cfa80 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -152,6 +152,13 @@ true
+ + commons-net + commons-net + 3.9.0 + true + + ca.uhn.hapi.fhir 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 8346eec71c3..411d4b27596 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 @@ -13,6 +13,7 @@ import org.apache.commons.lang3.Validate; import org.fhir.ucum.UcumService; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; +import org.hl7.fhir.r5.context.IContextResourceLoader; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContextManager; import org.hl7.fhir.r5.model.CodeSystem; @@ -30,10 +31,12 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; +import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest; +import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; +import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.TimeTracker; -import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.i18n.I18nBase; import org.hl7.fhir.utilities.npm.BasePackageCacheManager; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -90,6 +93,16 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } } + @Override + public CodeSystem fetchCodeSystem(String system, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2456)); + } + + @Override + public CodeSystem fetchCodeSystem(String system, String version, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2457)); + } + @Override public CodeSystem fetchSupplementedCodeSystem(String theS) { return null; @@ -100,6 +113,16 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext return null; } + @Override + public CodeSystem fetchSupplementedCodeSystem(String system, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2458)); + } + + @Override + public CodeSystem fetchSupplementedCodeSystem(String system, String version, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2459)); + } + @Override public List getResourceNames() { List result = new ArrayList<>(); @@ -110,6 +133,11 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext return result; } + @Override + public List getResourceNames(FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2460)); + } + @Override public IResourceValidator newValidator() { throw new UnsupportedOperationException(Msg.code(206)); @@ -130,6 +158,14 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } } + @Override + public boolean supportsSystem(String system, FhirPublication fhirVersion) throws TerminologyServiceException { + if (!fhirVersion.equals(FhirPublication.R5)) { + throw new UnsupportedOperationException(Msg.code(2461)); + } + return supportsSystem(system); + } + @Override public ValidationResult validateCode(ValidationOptions theOptions, CodeableConcept theCode, ValueSet theVs) { for (Coding next : theCode.getCoding()) { @@ -247,6 +283,10 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } @Override + public void setExpansionParameters(Parameters expParameters) { + setExpansionProfile(expParameters); + } + public void setExpansionProfile(Parameters theExpParameters) { myExpansionProfile = theExpParameters; } @@ -264,7 +304,8 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext input.getCompose().addInclude(theInc); IValidationSupport.ValueSetExpansionOutcome output = myValidationSupport.expandValueSet(new ValidationSupportContext(myValidationSupport), null, input); - return new ValueSetExpansionOutcome((ValueSet) output.getValueSet(), output.getError(), null); + return new ValueSetExpansionOutcome( + (ValueSet) output.getValueSet(), output.getError(), null, output.getErrorIsFromServer()); } @Override @@ -278,12 +319,12 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } @Override - public ILoggingService getLogger() { + public org.hl7.fhir.r5.context.ILoggingService getLogger() { throw new UnsupportedOperationException(Msg.code(213)); } @Override - public void setLogger(ILoggingService theLogger) { + public void setLogger(org.hl7.fhir.r5.context.ILoggingService theLogger) { throw new UnsupportedOperationException(Msg.code(214)); } @@ -312,21 +353,36 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext throw new UnsupportedOperationException(Msg.code(218)); } - @Override - public TranslationServices translator() { - throw new UnsupportedOperationException(Msg.code(219)); - } - @Override public StructureDefinition fetchTypeDefinition(String typeName) { return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); } + @Override + public boolean isPrimitiveType(String s) { + throw new UnsupportedOperationException(Msg.code(2462)); + } + + @Override + public boolean isDataType(String s) { + throw new UnsupportedOperationException(Msg.code(2463)); + } + + @Override + public StructureDefinition fetchTypeDefinition(String typeName, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2464)); + } + @Override public List fetchTypeDefinitions(String n) { throw new UnsupportedOperationException(Msg.code(234)); } + @Override + public List fetchTypeDefinitions(String n, FhirPublication fhirPublication) { + throw new UnsupportedOperationException(Msg.code(2465)); + } + @Override public T fetchResourceRaw(Class class_, String uri) { return fetchResource(class_, uri); @@ -343,6 +399,10 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext } } + public T fetchResource(Class class_, String uri, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2466)); + } + @Override public T fetchResourceWithException(Class theClass, String theUri) throws FHIRException { @@ -364,21 +424,47 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext return fetchResource(theClass, theUri + "|" + theVersion); } + @Override + public T fetchResource( + Class class_, String uri, String version, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2467)); + } + @Override public T fetchResource(Class class_, String uri, Resource canonicalForSource) { return fetchResource(class_, uri); } + @Override + public List fetchResourcesByType(Class class_, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2468)); + } + @Override public org.hl7.fhir.r5.model.Resource fetchResourceById(String theType, String theUri) { throw new UnsupportedOperationException(Msg.code(226)); } + @Override + public Resource fetchResourceById(String type, String uri, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2469)); + } + @Override public boolean hasResource(Class theClass_, String theUri) { throw new UnsupportedOperationException(Msg.code(227)); } + @Override + public boolean hasResource(Class class_, String uri, Resource sourceOfReference) { + throw new UnsupportedOperationException(Msg.code(2470)); + } + + @Override + public boolean hasResource(Class class_, String uri, FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2471)); + } + @Override public void cacheResource(org.hl7.fhir.r5.model.Resource theRes) throws FHIRException { throw new UnsupportedOperationException(Msg.code(228)); @@ -397,6 +483,11 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext return myCtx.getResourceTypes(); } + @Override + public Set getResourceNamesAsSet(FhirPublication fhirVersion) { + throw new UnsupportedOperationException(Msg.code(2472)); + } + @Override public ValueSetExpansionOutcome expandVS( Resource src, ElementDefinitionBindingComponent theBinding, boolean theCacheOk, boolean theHierarchical) @@ -493,6 +584,11 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext throw new UnsupportedOperationException(Msg.code(2113) + "Can't fetch all resources of type: " + theClass); } + @Override + public List fetchResourcesByUrl(Class class_, String url) { + throw new UnsupportedOperationException(Msg.code(2508) + "Can't fetch all resources of url: " + url); + } + @Override public IWorkerContext setPackageTracker(IWorkerContextManager.IPackageLoadingTracker theIPackageLoadingTracker) { throw new UnsupportedOperationException(Msg.code(220)); @@ -518,4 +614,34 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext public void setForPublication(boolean b) { throw new UnsupportedOperationException(Msg.code(2350)); } + + @Override + public OIDSummary urlsForOid(String oid, String resourceType) { + throw new UnsupportedOperationException(Msg.code(2473)); + } + + @Override + public T findTxResource(Class class_, String canonical, Resource sourceOfReference) { + throw new UnsupportedOperationException(Msg.code(2491)); + } + + @Override + public T findTxResource(Class class_, String canonical) { + throw new UnsupportedOperationException(Msg.code(2492)); + } + + @Override + public T findTxResource(Class class_, String canonical, String version) { + throw new UnsupportedOperationException(Msg.code(2493)); + } + + @Override + public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) { + throw new UnsupportedOperationException(Msg.code(2488)); + } + + @Override + public boolean isServerSideSystem(String url) { + return false; + } } diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java index 6321e9a9bb2..d0d33f403f5 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java @@ -10,14 +10,14 @@ import jakarta.annotation.Nonnull; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.r5.fhirpath.ExpressionNode; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses; +import org.hl7.fhir.r5.fhirpath.TypeDetails; import org.hl7.fhir.r5.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.IdType; -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.FHIRPathUtilityClasses; import java.util.List; import java.util.Optional; @@ -92,13 +92,20 @@ public class FhirPathR5 implements IFhirPath { myEngine.setHostServices(new FHIRPathEngine.IEvaluationContext() { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, + Object appContext, + String name, + boolean beforeContext, + boolean explicitConstant) throws PathEngineException { return null; } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -108,36 +115,52 @@ public class FhirPathR5 implements IFhirPath { } @Override - public FHIRPathUtilityClasses.FunctionDetails resolveFunction(String functionName) { + public FHIRPathUtilityClasses.FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String theUrl, Base refContext) throws FHIRException { - return (Base) theEvaluationContext.resolveReference(new IdType(theUrl), refContext); + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) + throws FHIRException { + return (Base) theEvaluationContext.resolveReference(new IdType(url), refContext); } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } + + @Override + public boolean paramIsType(String name, int index) { + return false; + } }); } diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 03ddbc9d6f2..80316205b3a 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 52cf37ebb7a..370ab16d056 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 6cf83c13ab3..5c0b679a587 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index a58edf904de..de641d14d9c 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 2ea342169f0..201adcd3776 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index 070d77a0702..84752c6b672 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml index e3a156d7114..fd30132d476 100644 --- a/hapi-fhir-validation-resources-r4b/pom.xml +++ b/hapi-fhir-validation-resources-r4b/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 5a1c5fd9164..361e6370b42 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 2c3c9cb8097..37faac7cd65 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -309,6 +309,13 @@ test + + org.hl7.fhir.testcases + fhir-test-cases + 1.5.5-SNAPSHOT + test + + diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index 7ecbec36fc0..1b12e20ec62 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -187,7 +187,14 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { } protected CodeValidationResult getValidateCodeResultError(final String theMessage) { - return new CodeValidationResult().setSeverity(IssueSeverity.ERROR).setMessage(theMessage); + return new CodeValidationResult() + .setSeverity(IssueSeverity.ERROR) + .setMessage(theMessage) + .setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue( + theMessage, + IssueSeverity.ERROR, + CodeValidationIssueCode.INVALID, + CodeValidationIssueCoding.INVALID_CODE))); } private CodeValidationResult validateCodeUsingCodeMap( @@ -209,7 +216,8 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { if (result.isFound()) { return getValidateCodeResultOk(theCode, result.getCodeDisplay()); } else if (result.getErrorMessage() != null) { - return getValidateCodeResultError(result.getErrorMessage()); + String errorMessageWithSystemInfo = result.getErrorMessage() + " (for '" + theSystem + "#" + theCode + "')"; + return getValidateCodeResultError(errorMessageWithSystemInfo); } return null; } @@ -464,7 +472,9 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { - + if (theSystem == null) { + return false; + } switch (theSystem) { case COUNTRIES_CODESYSTEM_URL: case UCUM_CODESYSTEM_URL: diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 3c4cca0a865..67e553e3d3b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -134,7 +134,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu expansionR5 = expandValueSetToCanonical( theValidationSupportContext, theValueSetToExpand, theWantSystemAndVersion, theWantCode); } catch (ExpansionCouldNotBeCompletedInternallyException e) { - return new ValueSetExpansionOutcome(e.getMessage()); + return new ValueSetExpansionOutcome(e.getMessage(), false); } if (expansionR5 == null) { return null; @@ -267,6 +267,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } codeValidationResult.setMessage(msg); + codeValidationResult.addCodeValidationIssue(e.getCodeValidationIssue()); return codeValidationResult; } @@ -407,6 +408,8 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu String theValueSetUrl) { assert theExpansion != null; + final CodeValidationResult codeValidationResult; + boolean caseSensitive = true; IBaseResource codeSystemToValidateResource = null; if (!theOptions.isInferSystem() && isNotBlank(theCodeSystemUrlAndVersionToValidate)) { @@ -534,7 +537,83 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu codeSystemUrlToValidate = theCodeSystemUrlAndVersionToValidate; } } - for (FhirVersionIndependentConcept nextExpansionCode : codes) { + CodeValidationResult valueSetResult = findCodeInExpansion( + theCodeToValidate, + theDisplayToValidate, + theValueSetUrl, + codeSystemUrlToValidate, + codeSystemVersionToValidate, + codeSystemResourceName, + codeSystemResourceVersion, + codes, + theOptions, + caseSensitive); + if (valueSetResult != null) { + codeValidationResult = valueSetResult; + } else { + ValidationMessage.IssueSeverity severity; + String message; + CodeValidationIssueCode issueCode = CodeValidationIssueCode.CODE_INVALID; + CodeValidationIssueCoding issueCoding = CodeValidationIssueCoding.INVALID_CODE; + if ("fragment".equals(codeSystemResourceContentMode)) { + severity = ValidationMessage.IssueSeverity.WARNING; + message = "Unknown code in fragment CodeSystem '" + + getFormattedCodeSystemAndCodeForMessage( + theCodeSystemUrlAndVersionToValidate, theCodeToValidate) + + "'"; + } else { + severity = ValidationMessage.IssueSeverity.ERROR; + message = "Unknown code '" + + getFormattedCodeSystemAndCodeForMessage( + theCodeSystemUrlAndVersionToValidate, theCodeToValidate) + + "'"; + } + if (isNotBlank(theValueSetUrl)) { + message += " for in-memory expansion of ValueSet '" + theValueSetUrl + "'"; + issueCoding = CodeValidationIssueCoding.NOT_IN_VS; + } + + codeValidationResult = new CodeValidationResult() + .setSeverityCode(severity.toCode()) + .setMessage(message) + .addCodeValidationIssue(new CodeValidationIssue( + message, getIssueSeverityFromCodeValidationIssue(severity), issueCode, issueCoding)); + } + + return codeValidationResult; + } + + private static String getFormattedCodeSystemAndCodeForMessage( + String theCodeSystemUrlAndVersionToValidate, String theCodeToValidate) { + return (isNotBlank(theCodeSystemUrlAndVersionToValidate) ? theCodeSystemUrlAndVersionToValidate + "#" : "") + + theCodeToValidate; + } + + private IValidationSupport.IssueSeverity getIssueSeverityFromCodeValidationIssue( + ValidationMessage.IssueSeverity theSeverity) { + switch (theSeverity) { + case ERROR: + return IValidationSupport.IssueSeverity.ERROR; + case WARNING: + return IValidationSupport.IssueSeverity.WARNING; + case INFORMATION: + return IValidationSupport.IssueSeverity.INFORMATION; + } + return null; + } + + private CodeValidationResult findCodeInExpansion( + String theCodeToValidate, + String theDisplayToValidate, + String theValueSetUrl, + String codeSystemUrlToValidate, + String codeSystemVersionToValidate, + String codeSystemResourceName, + String codeSystemResourceVersion, + List expansionCodes, + ConceptValidationOptions theOptions, + boolean caseSensitive) { + for (FhirVersionIndependentConcept nextExpansionCode : expansionCodes) { boolean codeMatches; if (caseSensitive) { @@ -574,6 +653,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu theCodeToValidate, theDisplayToValidate, nextExpansionCode.getDisplay(), + codeSystemUrlToValidate, csVersion, messageAppend, getIssueSeverityForCodeDisplayMismatch()); @@ -585,29 +665,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } } } - - ValidationMessage.IssueSeverity severity; - String message; - if ("fragment".equals(codeSystemResourceContentMode)) { - severity = ValidationMessage.IssueSeverity.WARNING; - message = "Unknown code in fragment CodeSystem '" - + (isNotBlank(theCodeSystemUrlAndVersionToValidate) - ? theCodeSystemUrlAndVersionToValidate + "#" - : "") - + theCodeToValidate + "'"; - } else { - severity = ValidationMessage.IssueSeverity.ERROR; - message = "Unknown code '" - + (isNotBlank(theCodeSystemUrlAndVersionToValidate) - ? theCodeSystemUrlAndVersionToValidate + "#" - : "") - + theCodeToValidate + "'"; - } - if (isNotBlank(theValueSetUrl)) { - message += " for in-memory expansion of ValueSet '" + theValueSetUrl + "'"; - } - - return new CodeValidationResult().setSeverityCode(severity.toCode()).setMessage(message); + return null; } @Override @@ -915,7 +973,6 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu boolean ableToHandleCode = false; String failureMessage = null; - FailureType failureType = FailureType.OTHER; boolean isIncludeCodeSystemIgnored = includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT; @@ -1025,16 +1082,22 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } if (!ableToHandleCode) { - if (includeOrExcludeSystemResource == null && failureMessage == null) { - failureMessage = getFailureMessageForMissingOrUnusableCodeSystem( - includeOrExcludeSystemResource, loadedCodeSystemUrl); + if (failureMessage == null) { + if (includeOrExcludeSystemResource == null) { + failureMessage = getFailureMessageForMissingOrUnusableCodeSystem( + includeOrExcludeSystemResource, loadedCodeSystemUrl); + } else { + failureMessage = "Unable to expand value set"; + } } - if (includeOrExcludeSystemResource == null) { - failureType = FailureType.UNKNOWN_CODE_SYSTEM; - } - - throw new ExpansionCouldNotBeCompletedInternallyException(Msg.code(702) + failureMessage, failureType); + throw new ExpansionCouldNotBeCompletedInternallyException( + Msg.code(702) + failureMessage, + new CodeValidationIssue( + failureMessage, + IssueSeverity.ERROR, + CodeValidationIssueCode.NOT_FOUND, + CodeValidationIssueCoding.NOT_FOUND)); } if (includeOrExcludeSystemResource != null @@ -1054,9 +1117,14 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu org.hl7.fhir.r5.model.ValueSet subExpansion = expandValueSetR5(theValidationSupportContext, vs, theWantSystemUrlAndVersion, theWantCode); if (subExpansion == null) { + String theMessage = "Failed to expand ValueSet: " + nextValueSetInclude.getValueAsString(); throw new ExpansionCouldNotBeCompletedInternallyException( - Msg.code(703) + "Failed to expand ValueSet: " + nextValueSetInclude.getValueAsString(), - FailureType.OTHER); + Msg.code(703) + theMessage, + new CodeValidationIssue( + theMessage, + IssueSeverity.ERROR, + CodeValidationIssueCode.OTHER, + CodeValidationIssueCoding.OTHER)); } for (org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent next : subExpansion.getExpansion().getContains()) { @@ -1260,6 +1328,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu String theCode, String theDisplay, String theExpectedDisplay, + String theCodeSystem, String theCodeSystemVersion, IssueSeverity theIssueSeverityForCodeDisplayMismatch) { return createResultForDisplayMismatch( @@ -1267,6 +1336,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu theCode, theDisplay, theExpectedDisplay, + theCodeSystem, theCodeSystemVersion, "", theIssueSeverityForCodeDisplayMismatch); @@ -1277,6 +1347,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu String theCode, String theDisplay, String theExpectedDisplay, + String theCodeSystem, String theCodeSystemVersion, String theMessageAppend, IssueSeverity theIssueSeverityForCodeDisplayMismatch) { @@ -1293,15 +1364,26 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu InMemoryTerminologyServerValidationSupport.class, "displayMismatch", theDisplay, - theExpectedDisplay) + theExpectedDisplay, + theCodeSystem, + theCode) + theMessageAppend; } - return new CodeValidationResult() + CodeValidationResult codeValidationResult = new CodeValidationResult() .setSeverity(issueSeverity) .setMessage(message) .setCode(theCode) .setCodeSystemVersion(theCodeSystemVersion) .setDisplay(theExpectedDisplay); + if (issueSeverity != null) { + codeValidationResult.setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue( + message, + theIssueSeverityForCodeDisplayMismatch, + CodeValidationIssueCode.INVALID, + CodeValidationIssueCoding.INVALID_DISPLAY))); + } + + return codeValidationResult; } private static void flattenAndConvertCodesDstu2( @@ -1364,23 +1446,19 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu } } - public enum FailureType { - UNKNOWN_CODE_SYSTEM, - OTHER - } - public static class ExpansionCouldNotBeCompletedInternallyException extends Exception { private static final long serialVersionUID = -2226561628771483085L; - private final FailureType myFailureType; + private final CodeValidationIssue myCodeValidationIssue; - public ExpansionCouldNotBeCompletedInternallyException(String theMessage, FailureType theFailureType) { + public ExpansionCouldNotBeCompletedInternallyException( + String theMessage, CodeValidationIssue theCodeValidationIssue) { super(theMessage); - myFailureType = theFailureType; + myCodeValidationIssue = theCodeValidationIssue; } - public FailureType getFailureType() { - return myFailureType; + public CodeValidationIssue getCodeValidationIssue() { + return myCodeValidationIssue; } } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java index ba339a6e036..1898292c451 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java @@ -76,7 +76,9 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup CodeValidationResult result = new CodeValidationResult(); // will be warning or info (error/fatal filtered out above) result.setSeverity(myNonExistentCodeSystemSeverity); - result.setMessage("CodeSystem is unknown and can't be validated: " + theCodeSystem); + String theMessage = "CodeSystem is unknown and can't be validated: " + theCodeSystem + " for '" + theCodeSystem + + "#" + theCode + "'"; + result.setMessage(theMessage); // For information level, we just strip out the severity+message entirely // so that nothing appears in the validation result @@ -84,6 +86,12 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup result.setCode(theCode); result.setSeverity(null); result.setMessage(null); + } else { + result.addCodeValidationIssue(new CodeValidationIssue( + theMessage, + myNonExistentCodeSystemSeverity, + CodeValidationIssueCode.NOT_FOUND, + CodeValidationIssueCoding.NOT_FOUND)); } return result; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java index c092dd751f9..a630599d234 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/FHIRPathResourceGeneratorR4.java @@ -8,10 +8,10 @@ import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.fhirpath.ExpressionNode; +import org.hl7.fhir.r4.fhirpath.FHIRPathEngine; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; -import org.hl7.fhir.r4.model.ExpressionNode; import org.hl7.fhir.r4.model.Resource; -import org.hl7.fhir.r4.utils.FHIRPathEngine; import java.util.ArrayList; import java.util.HashMap; @@ -272,8 +272,6 @@ public class FHIRPathResourceGeneratorR4 { break; case MatchesFull: case Aggregate: - case Alias: - case AliasAs: case All: case AllFalse: case AllTrue: @@ -294,6 +292,7 @@ public class FHIRPathResourceGeneratorR4 { case ConvertsToTime: case Count: case Custom: + case DefineVariable: case Descendants: case Distinct: case Empty: 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 index 295a14b449a..c9d01882e4a 100644 --- 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 @@ -4,13 +4,16 @@ 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.IMessagingServices; 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.hl7.fhir.utilities.validation.ValidationMessage; +import java.util.Arrays; +import java.util.EnumSet; import java.util.List; /** @@ -25,10 +28,28 @@ public class FhirDefaultPolicyAdvisor implements IValidationPolicyAdvisor { return ReferenceValidationPolicy.IGNORE; } + @Override + public EnumSet policyForResource( + IResourceValidator validator, Object appContext, StructureDefinition type, String path) { + return EnumSet.allOf(ResourceValidationAction.class); + } + + @Override + public EnumSet policyForElement( + IResourceValidator validator, + Object appContext, + StructureDefinition structure, + ElementDefinition element, + String path) { + return EnumSet.allOf(ElementValidationAction.class); + } + @Override public ContainedReferenceValidationPolicy policyForContained( IResourceValidator validator, Object appContext, + StructureDefinition structure, + ElementDefinition element, String containerType, String containerId, Element.SpecialElement containingResourceType, @@ -38,15 +59,30 @@ public class FhirDefaultPolicyAdvisor implements IValidationPolicyAdvisor { } @Override - public CodedContentValidationPolicy policyForCodedContent( + public EnumSet policyForCodedContent( IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, BindingKind kind, + AdditionalBindingPurpose purpose, ValueSet valueSet, List systems) { - return CodedContentValidationPolicy.CODE; + return EnumSet.allOf(CodedContentValidationAction.class); + } + + @Override + public List getImpliedProfilesForResource( + IResourceValidator validator, + Object appContext, + String stackPath, + ElementDefinition definition, + StructureDefinition structure, + Element resource, + boolean valid, + IMessagingServices msgServices, + List messages) { + return Arrays.asList(); } } 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 3c46a5ab904..70af4473f18 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 @@ -10,11 +10,11 @@ import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r5.fhirpath.TypeDetails; 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.FHIRPathUtilityClasses.FunctionDetails; 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; @@ -38,7 +38,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta private boolean assumeValidRestReferences; private List myExtensionDomains = Collections.emptyList(); private IValidatorResourceFetcher validatorResourceFetcher; - private IValidationPolicyAdvisor validatorPolicyAdvisor; + private IValidationPolicyAdvisor validatorPolicyAdvisor = new FhirDefaultPolicyAdvisor(); /** * Constructor @@ -288,13 +288,16 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta public static class NullEvaluationContext implements FHIRPathEngine.IEvaluationContext { @Override - public List resolveConstant(Object appContext, String name, boolean beforeContext) + public List resolveConstant( + FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { return Collections.emptyList(); } @Override - public TypeDetails resolveConstantType(Object appContext, String name) throws PathEngineException { + public TypeDetails resolveConstantType( + FHIRPathEngine engine, Object appContext, String name, boolean explicitConstant) + throws PathEngineException { return null; } @@ -304,35 +307,51 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta } @Override - public FunctionDetails resolveFunction(String functionName) { + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { return null; } @Override - public TypeDetails checkFunction(Object appContext, String functionName, List parameters) + public TypeDetails checkFunction( + FHIRPathEngine engine, + Object appContext, + String functionName, + TypeDetails focus, + List parameters) throws PathEngineException { return null; } @Override public List executeFunction( - Object appContext, List focus, String functionName, List> parameters) { + FHIRPathEngine engine, + Object appContext, + List focus, + String functionName, + List> parameters) { return null; } @Override - public Base resolveReference(Object appContext, String url, Base refContext) throws FHIRException { + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) + throws FHIRException { return null; } @Override - public boolean conformsToProfile(Object appContext, Base item, String url) throws FHIRException { + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { return false; } @Override - public ValueSet resolveValueSet(Object appContext, String url) { + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { return null; } + + @Override + public boolean paramIsType(String name, int index) { + return false; + } } } 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 c946e2157a7..c5ed37b5519 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 @@ -14,8 +14,8 @@ import org.apache.commons.io.input.ReaderInputStream; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Manager; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; 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 0dc166c0c8e..40e0d8bb706 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 @@ -19,22 +19,28 @@ import org.fhir.ucum.UcumService; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.context.IContextResourceLoader; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContextManager; import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.NamingSystem; +import org.hl7.fhir.r5.model.OperationOutcome; import org.hl7.fhir.r5.model.PackageInformation; +import org.hl7.fhir.r5.model.Parameters; 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.profilemodel.PEBuilder; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; +import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest; import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass; +import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier; +import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.TimeTracker; -import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.i18n.I18nBase; import org.hl7.fhir.utilities.npm.BasePackageCacheManager; import org.hl7.fhir.utilities.npm.NpmPackage; @@ -45,11 +51,7 @@ import org.slf4j.LoggerFactory; import java.io.FileNotFoundException; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -214,6 +216,10 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo } @Override + public void setExpansionParameters(Parameters expParameters) { + setExpansionProfile(expParameters); + } + public void setExpansionProfile(org.hl7.fhir.r5.model.Parameters expParameters) { myExpansionProfile = expParameters; } @@ -278,7 +284,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo theResult.getCodeSystemVersion(), conceptDefinitionComponent, display, - null); + getIssuesForCodeValidation(theResult.getCodeValidationIssues())); } if (retVal == null) { @@ -288,6 +294,76 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo return retVal; } + private List getIssuesForCodeValidation( + List codeValidationIssues) { + List issues = new ArrayList<>(); + + for (IValidationSupport.CodeValidationIssue codeValidationIssue : codeValidationIssues) { + + CodeableConcept codeableConcept = new CodeableConcept().setText(codeValidationIssue.getMessage()); + codeableConcept.addCoding( + "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", + getIssueCodingFromCodeValidationIssue(codeValidationIssue), + null); + + OperationOutcome.OperationOutcomeIssueComponent issue = + new OperationOutcome.OperationOutcomeIssueComponent() + .setSeverity(getIssueSeverityFromCodeValidationIssue(codeValidationIssue)) + .setCode(getIssueTypeFromCodeValidationIssue(codeValidationIssue)) + .setDetails(codeableConcept); + issue.getDetails().setText(codeValidationIssue.getMessage()); + issue.addExtension() + .setUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id") + .setValue(new org.hl7.fhir.r5.model.StringType("Terminology_PassThrough_TX_Message")); + issues.add(issue); + } + return issues; + } + + private String getIssueCodingFromCodeValidationIssue(IValidationSupport.CodeValidationIssue codeValidationIssue) { + switch (codeValidationIssue.getCoding()) { + case VS_INVALID: + return "vs-invalid"; + case NOT_FOUND: + return "not-found"; + case NOT_IN_VS: + return "not-in-vs"; + case INVALID_CODE: + return "invalid-code"; + case INVALID_DISPLAY: + return "invalid-display"; + } + return null; + } + + private OperationOutcome.IssueType getIssueTypeFromCodeValidationIssue( + IValidationSupport.CodeValidationIssue codeValidationIssue) { + switch (codeValidationIssue.getCode()) { + case NOT_FOUND: + return OperationOutcome.IssueType.NOTFOUND; + case CODE_INVALID: + return OperationOutcome.IssueType.CODEINVALID; + case INVALID: + return OperationOutcome.IssueType.INVALID; + } + return null; + } + + private OperationOutcome.IssueSeverity getIssueSeverityFromCodeValidationIssue( + IValidationSupport.CodeValidationIssue codeValidationIssue) { + switch (codeValidationIssue.getSeverity()) { + case FATAL: + return OperationOutcome.IssueSeverity.FATAL; + case ERROR: + return OperationOutcome.IssueSeverity.ERROR; + case WARNING: + return OperationOutcome.IssueSeverity.WARNING; + case INFORMATION: + return OperationOutcome.IssueSeverity.INFORMATION; + } + return null; + } + @Override public ValueSetExpansionOutcome expandVS( org.hl7.fhir.r5.model.ValueSet source, boolean cacheOk, boolean Hierarchical) { @@ -313,7 +389,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo String error = expanded.getError(); TerminologyServiceErrorClass result = null; - return new ValueSetExpansionOutcome(convertedResult, error, result); + return new ValueSetExpansionOutcome(convertedResult, error, result, expanded.getErrorIsFromServer()); } @Override @@ -374,6 +450,16 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo } } + @Override + public CodeSystem fetchCodeSystem(String system, FhirPublication fhirVersion) { + return null; + } + + @Override + public CodeSystem fetchCodeSystem(String system, String version, FhirPublication fhirVersion) { + return null; + } + @Override public CodeSystem fetchSupplementedCodeSystem(String system) { return null; @@ -384,6 +470,16 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo return null; } + @Override + public CodeSystem fetchSupplementedCodeSystem(String system, FhirPublication fhirVersion) { + return null; + } + + @Override + public CodeSystem fetchSupplementedCodeSystem(String system, String version, FhirPublication fhirVersion) { + return null; + } + @Override public T fetchResourceRaw(Class class_, String uri) { return fetchResource(class_, uri); @@ -408,6 +504,11 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo throw new UnsupportedOperationException(Msg.code(666)); } + @Override + public Resource fetchResourceById(String type, String uri, FhirPublication fhirVersion) { + return null; + } + @Override public T fetchResourceWithException(Class class_, String uri) throws FHIRException { T retVal = fetchResource(class_, uri); @@ -423,11 +524,27 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo return fetchResource(class_, uri + "|" + version); } + @Override + public T fetchResource(Class class_, String uri, FhirPublication fhirVersion) { + return null; + } + + @Override + public T fetchResource( + Class class_, String uri, String version, FhirPublication fhirVersion) { + return null; + } + @Override public T fetchResource(Class class_, String uri, Resource canonicalForSource) { return fetchResource(class_, uri); } + @Override + public List fetchResourcesByType(Class class_, FhirPublication fhirVersion) { + return null; + } + @Override public T fetchResourceWithException(Class class_, String uri, Resource sourceOfReference) throws FHIRException { @@ -442,6 +559,11 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo .getResourceTypes()); } + @Override + public List getResourceNames(FhirPublication fhirVersion) { + return null; + } + @Override public Set getResourceNamesAsSet() { return myValidationSupportContext @@ -451,17 +573,46 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo } @Override - public StructureDefinition fetchTypeDefinition(String typeName) { - return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + typeName); + public Set getResourceNamesAsSet(FhirPublication theFhirVersion) { + return null; } @Override - public List fetchTypeDefinitions(String typeName) { + public StructureDefinition fetchTypeDefinition(String theTypeName) { + return fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + theTypeName); + } + + @Override + public StructureDefinition fetchTypeDefinition(String theTypeName, FhirPublication theFhirVersion) { + return null; + } + + @Override + public List fetchTypeDefinitions(String theTypeName) { List allStructures = new ArrayList<>(allStructures()); - allStructures.removeIf(sd -> !sd.hasType() || !sd.getType().equals(typeName)); + allStructures.removeIf(sd -> !sd.hasType() || !sd.getType().equals(theTypeName)); return allStructures; } + @Override + public List fetchTypeDefinitions(String theTypeName, FhirPublication theFhirVersion) { + return null; + } + + @Override + public boolean isPrimitiveType(String theType) { + List allStructures = new ArrayList<>(allStructures()); + return allStructures.stream() + .filter(structureDefinition -> + structureDefinition.getKind() == StructureDefinition.StructureDefinitionKind.PRIMITIVETYPE) + .anyMatch(structureDefinition -> theType.equals(structureDefinition.getName())); + } + + @Override + public boolean isDataType(String theType) { + return !isPrimitiveType(theType); + } + @Override public UcumService getUcumService() { throw new UnsupportedOperationException(Msg.code(676)); @@ -484,7 +635,22 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo @Override public boolean hasResource(Class class_, String uri) { - throw new UnsupportedOperationException(Msg.code(680)); + if (isBlank(uri)) { + return false; + } + + ResourceKey key = new ResourceKey(class_.getSimpleName(), uri); + return myFetchResourceCache.get(key) != null; + } + + @Override + public boolean hasResource(Class class_, String uri, Resource sourceOfReference) { + return false; + } + + @Override + public boolean hasResource(Class class_, String uri, FhirPublication fhirVersion) { + return false; } @Override @@ -508,12 +674,12 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo } @Override - public ILoggingService getLogger() { + public org.hl7.fhir.r5.context.ILoggingService getLogger() { return null; } @Override - public void setLogger(ILoggingService logger) { + public void setLogger(org.hl7.fhir.r5.context.ILoggingService logger) { throw new UnsupportedOperationException(Msg.code(687)); } @@ -525,8 +691,8 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo } @Override - public TranslationServices translator() { - throw new UnsupportedOperationException(Msg.code(688)); + public boolean supportsSystem(String system, FhirPublication fhirVersion) throws TerminologyServiceException { + return supportsSystem(system); } @Override @@ -637,47 +803,112 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo String theDisplay) { IValidationSupport.CodeValidationResult result; if (theValueSet != null) { - result = myValidationSupportContext - .getRootValidationSupport() - .validateCodeInValueSet( - myValidationSupportContext, - theValidationOptions, - theSystem, - theCode, - theDisplay, - theValueSet); + result = validateCodeInValueSet(theValueSet, theValidationOptions, theSystem, theCode, theDisplay); } else { - result = myValidationSupportContext - .getRootValidationSupport() - .validateCode( - myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, null); + result = validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay); } return convertValidationResult(theSystem, result); } + private IValidationSupport.CodeValidationResult validateCodeInValueSet( + IBaseResource theValueSet, + ConceptValidationOptions theValidationOptions, + String theSystem, + String theCode, + String theDisplay) { + IValidationSupport.CodeValidationResult result = myValidationSupportContext + .getRootValidationSupport() + .validateCodeInValueSet( + myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, theValueSet); + if (result != null) { + /* We got a value set result, which could be successful, or could contain errors/warnings. The code + might also be invalid in the code system, so we will check that as well and add those issues + to our result. + */ + IValidationSupport.CodeValidationResult codeSystemResult = + validateCodeInCodeSystem(theValidationOptions, theSystem, theCode, theDisplay); + final boolean valueSetResultContainsInvalidDisplay = result.getCodeValidationIssues().stream() + .anyMatch(codeValidationIssue -> codeValidationIssue.getCoding() + == IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY); + if (codeSystemResult != null) { + for (IValidationSupport.CodeValidationIssue codeValidationIssue : + codeSystemResult.getCodeValidationIssues()) { + /* Value set validation should already have checked the display name. If we get INVALID_DISPLAY + issues from code system validation, they will only repeat what was already caught. + */ + if (codeValidationIssue.getCoding() != IValidationSupport.CodeValidationIssueCoding.INVALID_DISPLAY + || !valueSetResultContainsInvalidDisplay) { + result.addCodeValidationIssue(codeValidationIssue); + } + } + } + } + return result; + } + + private IValidationSupport.CodeValidationResult validateCodeInCodeSystem( + ConceptValidationOptions theValidationOptions, String theSystem, String theCode, String theDisplay) { + return myValidationSupportContext + .getRootValidationSupport() + .validateCode(myValidationSupportContext, theValidationOptions, theSystem, theCode, theDisplay, null); + } + @Override public ValidationResult validateCode( ValidationOptions theOptions, org.hl7.fhir.r5.model.CodeableConcept code, org.hl7.fhir.r5.model.ValueSet theVs) { + List validationResultsOk = new ArrayList<>(); + List issues = new ArrayList<>(); for (Coding next : code.getCoding()) { + if (!next.hasSystem()) { + String message = + "Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided"; + OperationOutcome.OperationOutcomeIssueComponent issue = + new OperationOutcome.OperationOutcomeIssueComponent() + .setSeverity(OperationOutcome.IssueSeverity.WARNING) + .setCode(OperationOutcome.IssueType.NOTFOUND) + .setDiagnostics(message) + .setDetails(new CodeableConcept().setText(message)); + + issues.add(issue); + } ValidationResult retVal = validateCode(theOptions, next, theVs); if (retVal.isOk()) { - if (myValidationSupportContext.isEnabledValidationForCodingsLogicalAnd()) { - validationResultsOk.add(retVal); - } else { - return retVal; + validationResultsOk.add(retVal); + } else { + for (OperationOutcome.OperationOutcomeIssueComponent issue : retVal.getIssues()) { + issues.add(issue); } } } - if (code.getCoding().size() > 0 - && validationResultsOk.size() == code.getCoding().size()) { - return validationResultsOk.get(0); + if (code.getCoding().size() > 0) { + if (!myValidationSupportContext.isEnabledValidationForCodingsLogicalAnd()) { + if (validationResultsOk.size() == code.getCoding().size()) { + return validationResultsOk.get(0); + } + } else { + if (validationResultsOk.size() > 0) { + return validationResultsOk.get(0); + } + } } - return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null, null); + return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null, issues); + } + + private static OperationOutcome.OperationOutcomeIssueComponent getOperationOutcomeTxIssueComponent( + String message, OperationOutcome.IssueType issueCode, String txIssueTypeCode) { + OperationOutcome.OperationOutcomeIssueComponent issue = new OperationOutcome.OperationOutcomeIssueComponent() + .setSeverity(OperationOutcome.IssueSeverity.ERROR) + .setDiagnostics(message); + issue.getDetails().setText(message); + + issue.setCode(issueCode); + issue.getDetails().addCoding("http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", txIssueTypeCode, null); + return issue; } public void invalidateCaches() { @@ -692,6 +923,11 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo throw new UnsupportedOperationException(Msg.code(650) + "Unable to fetch resources of type: " + theClass); } + @Override + public List fetchResourcesByUrl(Class class_, String url) { + throw new UnsupportedOperationException(Msg.code(2509) + "Can't fetch all resources of url: " + url); + } + @Override public boolean isForPublication() { return false; @@ -702,6 +938,37 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo throw new UnsupportedOperationException(Msg.code(2351)); } + @Override + public OIDSummary urlsForOid(String oid, String resourceType) { + return null; + } + + @Override + public T findTxResource(Class class_, String canonical, Resource sourceOfReference) { + if (canonical == null) { + return null; + } + return fetchResource(class_, canonical, sourceOfReference); + } + + @Override + public T findTxResource(Class class_, String canonical) { + if (canonical == null) { + return null; + } + + return fetchResource(class_, canonical); + } + + @Override + public T findTxResource(Class class_, String canonical, String version) { + if (canonical == null) { + return null; + } + + return fetchResource(class_, canonical, version); + } + public static ConceptValidationOptions convertConceptValidationOptions(ValidationOptions theOptions) { ConceptValidationOptions retVal = new ConceptValidationOptions(); if (theOptions.isGuessSystem()) { @@ -763,4 +1030,14 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo return myHashCode; } } + + @Override + public Boolean subsumes(ValidationOptions optionsArg, Coding parent, Coding child) { + throw new UnsupportedOperationException(Msg.code(2489)); + } + + @Override + public boolean isServerSideSystem(String url) { + return false; + } } diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java index 21c119a2bac..3755eda492a 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java @@ -81,6 +81,7 @@ import org.hl7.fhir.r4.model.OperationDefinition.OperationKind; import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse; import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -133,7 +134,8 @@ public class ServerCapabilityStatementProviderR4Test extends BaseValidationTestW @BeforeEach public void before() { myValidator = myCtx.newValidator(); - myValidator.registerValidatorModule(new FhirInstanceValidator(myCtx)); + FhirInstanceValidator fhirInstanceValidator = new FhirInstanceValidator(myCtx); + myValidator.registerValidatorModule(fhirInstanceValidator); } private HttpServletRequest createHttpServletRequest() { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java index 7cb8736c3f7..b77826787be 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java @@ -109,7 +109,7 @@ public class CommonCodeSystemsTerminologyServiceTest extends BaseValidationTestW final String code = "FOO"; final ValueSet vs = new ValueSet().setUrl(UCUM_VALUESET_URL); CodeValidationResult result = mySvc.validateCodeInValueSet(newSupport(), newOptions(), UCUM_CODESYSTEM_URL, code, null, vs); - validateCodeResultError(result, "Error processing unit '" + code +"': The unit '" + code + "' is unknown' at position 0"); + validateCodeResultError(result, "Error processing unit '" + code +"': The unit '" + code + "' is unknown' at position 0 (for 'http://unitsofmeasure.org#"+code+"')"); } @ParameterizedTest diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperCoreTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperCoreTest.java new file mode 100644 index 00000000000..50831b6e6ee --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapperCoreTest.java @@ -0,0 +1,277 @@ +package org.hl7.fhir.common.hapi.validation.validator; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.test.utilities.LoggingExtension; +import ca.uhn.fhir.validation.FhirValidator; +import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import com.google.common.base.Charsets; + +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; +import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; +import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; +import org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport; +import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.Constants; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.test.utils.TestingUtilities; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.json.model.JsonObject; +import org.hl7.fhir.validation.special.TxServiceTestHelper; +import org.hl7.fhir.validation.special.TxTestData; +import org.hl7.fhir.validation.special.TxTestSetup; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.formats.XmlParser; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; + +import static org.junit.jupiter.api.Assertions.assertNull; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +/** + This test class is included as useful debugging code. + + It is not currently intended to pass. + + It will remain disabled so as not to impede the build, but can be enabled in order to debug issues relating to the + validation of codes and code systems. + + This test class was derived from an original test in org.hl7.fhir.core: + + https://github.com/hapifhir/org.hl7.fhir.core/blob/6.3.7/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java + + The original series of tests is loaded from the fhir-test-cases repository: + + https://github.com/FHIR/fhir-test-cases/tree/1.5.7/tx + + In the original test cases were executed using the core implementation of IWorkerContext, which is passed to this + utility method: + + https://github.com/hapifhir/org.hl7.fhir.core/blob/d81fc5d82d0297d1a6f4b38bd9b81e52f859eb4f/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxServiceTestHelper.java#L25 + + This executes a diff of the actual and expected results. In the core implementation of IWorkerContext, an actual server + is used for terminology expansion and validation, and actual test results are meant to equal the expected ones. + + In HAPI, we use a completely different implementation of IWorkerContext, with error messages that differ from the core + implementation. InMemoryTerminologyServerValidationSupport for example, should not produce the same error code as a + FHIR terminology server. Thus, these tests will produce different error messages, and will almost always fail. However, + they can be used to debug the validation process, as well as to compare our validation results to the core. + + You can produce diffs in a local directory by setting the TX_SERVICE_TEST_DIFF_TARGET environment variable. + + The code below is left in the last 'working' state. It may contain dead code and other artifacts of being derived from + the core library. It may have to be updated to suit a particular debugging task. The documentation above is meant to + provide some guidance on how to derive similar debugging code from the org.hl7.fhir.core test cases. + */ +@Disabled +public class VersionSpecificWorkerContextWrapperCoreTest { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(VersionSpecificWorkerContextWrapperCoreTest.class); + + private final Map myValueSets = new HashMap<>(); + + private final Map myCodeSystems = new HashMap<>(); + private static FhirContext ourCtx = FhirContext.forR5(); + private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(ourCtx); + @RegisterExtension + public LoggingExtension myLoggingExtension = new LoggingExtension(); + private FhirInstanceValidator myInstanceVal; + private Map mySupportedCodeSystemsForExpansion; + private FhirValidator myVal; + private CachingValidationSupport myValidationSupport; + + + private static final String VALIDATE_CODE_OPERATION = "validate-code"; + + private static final String VALIDATE_CODESYSTEM_OPERATION = "cs-validate-code"; + + private static final String EXPAND_OPERATION = "expand"; + + private VersionSpecificWorkerContextWrapper wrapper; + + VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(FhirContext.forR5Cached()); + + + @BeforeEach + public void before() { + myVal = ourCtx.newValidator(); + myVal.setValidateAgainstStandardSchema(false); + myVal.setValidateAgainstStandardSchematron(false); + + IValidationSupport mockSupport = mock(IValidationSupport.class, withSettings().strictness(Strictness.LENIENT)); + when(mockSupport.getFhirContext()).thenReturn(ourCtx); + + + when(mockSupport.fetchResource(eq(ValueSet.class), nullable(String.class))) + .thenAnswer(new Answer() { + @Override + public ValueSet answer(InvocationOnMock theInvocation) { + String url = (String) theInvocation.getArguments()[1]; + + System.out.println("Looking for valueSet: " + url + "... " + (myValueSets.containsKey(url) ? "found" : "not found")); + return myValueSets.get(url); + } + } + ); + + when(mockSupport.fetchValueSet(nullable(String.class))) + .thenAnswer(new Answer() { + @Override + public ValueSet answer(InvocationOnMock theInvocation) { + String url = (String) theInvocation.getArguments()[0]; + + System.out.println("Looking for valueSet: " + url + "... " + (myValueSets.containsKey(url) ? "found" : "not found")); + return myValueSets.get(url); + } + } + ); + + when(mockSupport.fetchCodeSystem(nullable(String.class))) + .thenAnswer(new Answer() { + @Override + public IBaseResource answer(InvocationOnMock theInvocation) { + String url = (String) theInvocation.getArguments()[0]; + + System.out.println("Looking for codeSystem: " + url); + return myCodeSystems.get(url); + } + } + ); + + UnknownCodeSystemWarningValidationSupport unknownCodeSystemWarningValidationSupport = new UnknownCodeSystemWarningValidationSupport(ourCtx); + unknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.WARNING); + myValidationSupport = new CachingValidationSupport( + new ValidationSupportChain( + mockSupport, + myDefaultValidationSupport, + new InMemoryTerminologyServerValidationSupport(ourCtx), + new CommonCodeSystemsTerminologyService(ourCtx), + unknownCodeSystemWarningValidationSupport) + ); + myInstanceVal = new FhirInstanceValidator(myValidationSupport); + + wrapper = new VersionSpecificWorkerContextWrapper(new ValidationSupportContext(myValidationSupport), versionCanonicalizer); + wrapper.setExpansionParameters(new Parameters()); + } + + public static Stream argumentSource() throws IOException { + TxTestData data = TxTestData.loadTestDataFromDefaultClassPath(); + + return data.getTestData().stream() + .filter( entry -> { + TxTestSetup testSetup = (TxTestSetup) entry[1]; + return testSetup.getTest().asString("operation").equals("validate-code") + || testSetup.getTest().asString("operation").equals("cs-validate-code"); + } + ) + .map( + entry -> + Arguments.of(entry[0], data, entry[1])); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("argumentSource") + public void fhirTestCasesCodeValidationTest(String name, TxTestData testData, TxTestSetup setup) throws IOException { + System.out.println("Name: " + name); + + for (String s : setup.getSuite().forceArray("setup").asStrings()) { + + Resource res = loadResource(s); + if ("ValueSet".equals(res.getResourceType().toString())) { + ValueSet valueSet = (ValueSet)res; + myValueSets.put(valueSet.getUrl(), valueSet); + } else if ("CodeSystem".equals(res.getResourceType().toString())) { + CodeSystem codeSystem = (CodeSystem) res; + myCodeSystems.put(codeSystem.getUrl(), codeSystem); + } else { + System.out.println("Can't load setup resource: " + s); + } + } + Resource req = loadResource(setup.getTest().asString("request")); + String fn = setup.getTest().has("response:tx.fhir.org") ? setup.getTest().asString("response:tx.fhir.org") : setup.getTest().asString("response"); + String resp = TestingUtilities.loadTestResource("tx", fn); + String fp = Utilities.path("[tmp]", "tx", fn); + JsonObject ext = testData.getExternals() == null ? null : testData.getExternals().getJsonObject(fn); + File fo = new File(fp); + if (fo.exists()) { + fo.delete(); + } + if (setup.getTest().asString("operation").equals("validate-code")) { + String diff = TxServiceTestHelper.getDiffForValidation(setup.getTest().str("name"), wrapper, setup.getTest().asString("name"), req, resp, setup.getTest().asString("Content-Language"), fp, ext, false); + assertNull(diff, diff); + } else if (setup.getTest().asString("operation").equals("cs-validate-code")) { + String diff = TxServiceTestHelper.getDiffForValidation(setup.getTest().str("name"), wrapper, setup.getTest().asString("name"), req, resp, setup.getTest().asString("Content-Language"), fp, ext, true); + assertNull(diff, diff); + } + } + + public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { + final String version = "5.0.0"; + String contents = TestingUtilities.loadTestResource("tx", filename); + try (InputStream inputStream = IOUtils.toInputStream(contents, Charsets.UTF_8)) { + if (filename.contains(".json")) { + if (Constants.VERSION.equals(version) || "5.0".equals(version)) + return new JsonParser().parse(inputStream); + else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(version) || "3.0".equals(version)) + return VersionConvertorFactory_30_50.convertResource(new org.hl7.fhir.dstu3.formats.JsonParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(version) || "1.4".equals(version)) + return VersionConvertorFactory_14_50.convertResource(new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(version) || "1.0".equals(version)) + return VersionConvertorFactory_10_50.convertResource(new org.hl7.fhir.dstu2.formats.JsonParser().parse(inputStream)); + else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(version) || "4.0".equals(version)) + return VersionConvertorFactory_40_50.convertResource(new org.hl7.fhir.r4.formats.JsonParser().parse(inputStream)); + else + throw new FHIRException("unknown version " + version); + } else { + if (Constants.VERSION.equals(version) || "5.0".equals(version)) + return new XmlParser().parse(inputStream); + else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(version) || "3.0".equals(version)) + return VersionConvertorFactory_30_50.convertResource(new org.hl7.fhir.dstu3.formats.XmlParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(version) || "1.4".equals(version)) + return VersionConvertorFactory_14_50.convertResource(new org.hl7.fhir.dstu2016may.formats.XmlParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(version) || "1.0".equals(version)) + return VersionConvertorFactory_10_50.convertResource(new org.hl7.fhir.dstu2.formats.XmlParser().parse(inputStream)); + else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(version) || "4.0".equals(version)) + return VersionConvertorFactory_40_50.convertResource(new org.hl7.fhir.r4.formats.XmlParser().parse(inputStream)); + else + throw new FHIRException("unknown version " + version); + } + } + } +} diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java index cabfb9398f9..da73c0be800 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2/hapi/validation/FhirInstanceValidatorDstu2Test.java @@ -1,8 +1,10 @@ package org.hl7.fhir.dstu2.hapi.validation; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks; import ca.uhn.fhir.model.dstu2.composite.PeriodDt; import ca.uhn.fhir.model.dstu2.resource.Parameters; @@ -26,16 +28,32 @@ import org.hl7.fhir.dstu2.model.Observation.ObservationStatus; import org.hl7.fhir.dstu2.model.QuestionnaireResponse; import org.hl7.fhir.dstu2.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.hl7.fhir.dstu2.model.StringType; +import org.hl7.fhir.dstu3.model.CodeSystem; +import org.hl7.fhir.utilities.validation.ValidationMessage; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; public class FhirInstanceValidatorDstu2Test extends BaseValidationTestWithInlineMocks { @@ -44,11 +62,62 @@ public class FhirInstanceValidatorDstu2Test extends BaseValidationTestWithInline private static FhirInstanceValidator ourValidator; private static FhirContext ourCtxHl7OrgDstu2 = FhirContext.forDstu2Hl7Org(); - @BeforeAll - public static void beforeClass() { + private ArrayList myValidConcepts; + private final Set myValidSystems = new HashSet<>(); + + private void addValidConcept(String theSystem, String theCode) { + myValidSystems.add(theSystem); + myValidConcepts.add(theSystem + "___" + theCode); + } + + @BeforeEach + public void beforeEach() { + myValidConcepts = new ArrayList<>(); + DefaultProfileValidationSupport defaultProfileValidationSupport = new DefaultProfileValidationSupport(ourCtxDstu2); - IValidationSupport validationSupport = new ValidationSupportChain(defaultProfileValidationSupport, new InMemoryTerminologyServerValidationSupport(ourCtxDstu2)); + IValidationSupport mockSupport = mock(IValidationSupport.class, withSettings().strictness(Strictness.LENIENT)); + when(mockSupport.getFhirContext()).thenReturn(ourCtxDstu2); + when(mockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() { + @Override + public Boolean answer(InvocationOnMock theInvocation) { + String url = (String) theInvocation.getArguments()[1]; + boolean retVal = myValidSystems.contains(url); + ourLog.debug("isCodeSystemSupported({}) : {}", new Object[]{url, retVal}); + + return retVal; + } + }); + + when(mockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() { + @Override + public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) { + ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class); + String system = theInvocation.getArgument(2, String.class); + String code = theInvocation.getArgument(3, String.class); + String display = theInvocation.getArgument(4, String.class); + String valueSetUrl = theInvocation.getArgument(5, String.class); + IValidationSupport.CodeValidationResult retVal; + if (myValidConcepts.contains(system + "___" + code)) { + retVal = new IValidationSupport.CodeValidationResult().setCode(code); + } else if (myValidSystems.contains(system)) { + return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"); + } else { + retVal = null; + } + ourLog.debug("validateCode({}, {}, {}, {}) : {}", system, code, display, valueSetUrl, retVal); + return retVal; + } + }); + + IValidationSupport validationSupport = new ValidationSupportChain( + mockSupport, + defaultProfileValidationSupport, + new InMemoryTerminologyServerValidationSupport(ourCtxDstu2)); + + + ourValidator = new FhirInstanceValidator(validationSupport); + } /** @@ -69,6 +138,8 @@ public class FhirInstanceValidatorDstu2Test extends BaseValidationTestWithInline @Test public void testObservation() { + addValidConcept("http://loinc.org", "12345"); + Observation o = new Observation(); o.addIdentifier().setSystem("http://acme.org").setValue("1234"); o.setStatus(ObservationStatus.FINAL); 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 d01e51fafd1..c809f8b200c 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 @@ -22,6 +22,7 @@ import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationS import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; +import org.hl7.fhir.dstu3.fhirpath.FHIRPathEngine; import org.hl7.fhir.dstu3.model.Base; import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.Bundle; @@ -53,8 +54,8 @@ import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionKind; 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.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; @@ -71,6 +72,8 @@ import org.mockito.stubbing.Answer; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -166,14 +169,6 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline return retVal; } }); -// when(mockSupport.isValueSetSupported(any(), nullable(String.class))).thenAnswer(new Answer() { -// @Override -// public Boolean answer(InvocationOnMock theInvocation) { -// String url = (String) theInvocation.getArguments()[1]; -// boolean retVal = myValueSets.containsKey(url); -// return retVal; -// } -// }); when(mockSupport.fetchValueSet(any())).thenAnswer(t->{ String url = t.getArgument(0, String.class); return myValueSets.get(url); @@ -223,7 +218,10 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline if (myValidConcepts.contains(system + "___" + code)) { retVal = new IValidationSupport.CodeValidationResult().setCode(code); } else if (myValidSystems.contains(system)) { - return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"); + final String message = "Unknown code (for '" + system + "#" + code + "')"; + + return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); + } else if (myCodeSystems.containsKey(system)) { CodeSystem cs = myCodeSystems.get(system); Optional found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst(); @@ -345,9 +343,9 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult result = val.validateWithResult(p); assertFalse(result.isSuccessful()); List all = logResultsAndReturnAll(result); - assertThat(all).hasSize(1); + assertThat(all).hasSize(2); assertEquals(ResultSeverityEnum.ERROR, all.get(0).getSeverity()); - assertEquals("Unknown code 'urn:iso:std:iso:3166#QQ' for 'urn:iso:std:iso:3166#QQ'", all.get(0).getMessage()); + assertEquals("Unknown code 'urn:iso:std:iso:3166#QQ'", all.get(0).getMessage()); } } @@ -401,6 +399,8 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline */ @Test public void testDstu3UsesLatestDefinitions() throws IOException { + addValidConcept("http://www.nlm.nih.gov/research/umls/rxnorm", "316663"); + addValidConcept("http://snomed.info/sct", "14760008"); String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/bug703.json"), Charsets.UTF_8); ValidationResult results = myVal.validateWithResult(input); @@ -510,6 +510,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline @Test public void testGoal() { + addValidConcept("http://foo", "some other goal"); Goal goal = new Goal(); goal.setSubject(new Reference("Patient/123")); goal.setDescription(new CodeableConcept().addCoding(new Coding("http://foo", "some other goal", ""))); @@ -674,10 +675,29 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline } else if (t.getMessage().contains("sdf-15") && t.getMessage().contains("The name 'kind' is not valid for any of the possible types")) { // Find constraint sdf-15 fails with stricter core validation. return false; - } else if (t.getMessage().contains("side is inherently a collection") && t.getMessage().endsWith("may fail or return false if there is more than one item in the content being evaluated")) { + } + else if (t.getMessage().contains("bdl-7") && t.getMessage().contains("Error evaluating FHIRPath expression: The parameter type http://hl7.org/fhir/StructureDefinition/uri is not legal for where parameter 1. expecting SINGLETON[http://hl7.org/fhirpath/System.Boolean]")) { + // Find constraint sdf-15 fails with stricter core validation. + return false; + } + else if (t.getMessage().contains("side is inherently a collection") && t.getMessage().endsWith("may fail or return false if there is more than one item in the content being evaluated")) { // Some DSTU3 FHIRPath expressions now produce warnings if a singleton is compared to a collection that potentially has > 1 elements return false; - } else { + } else if (t.getMessage().contains("When HL7 is publishing a resource, the owning committee must be stated using the http://hl7.org/fhir/StructureDefinition/structuredefinition-wg extension")) { + // DSTU3 resources predate this strict requirement + return false; + } else if (t.getMessage().equals("The nominated WG 'rcrim' is unknown")) { + //The rcrim workgroup is now brr http://www.hl7.org/Special/committees/rcrim/index.cfm + return false; + } else if (t.getSeverity() == ResultSeverityEnum.WARNING + && ( t.getMessageId().equals("VALIDATION_HL7_PUBLISHER_MISMATCH") + || t.getMessageId().equals("VALIDATION_HL7_PUBLISHER_MISMATCH2") + || t.getMessageId().equals("VALIDATION_HL7_WG_URL") + )) { + // Workgroups have been updated and have slightly different naming conventions and URLs. + return false; + } + else { return true; } }) @@ -752,9 +772,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult results = myVal.validateWithResult(is); List outcome = logResultsAndReturnNonInformationalOnes(results); assertThat(outcome).hasSize(1); - assertEquals("Unknown code 'http://dicom.nema.org/resources/ontology/DCM#BAR' for 'http://dicom.nema.org/resources/ontology/DCM#BAR'", outcome.get(0).getMessage()); -// assertEquals("The Coding provided is not in the value set http://hl7.org/fhir/ValueSet/dicom-cid29, and a code should come from this value set unless it has no suitable code. (error message = Unknown code[BAR] in system[http://dicom.nema.org/resources/ontology/DCM])", outcome.get(1).getMessage()); - + assertThat(outcome.get(0).getMessage()).startsWith("The Coding provided (http://dicom.nema.org/resources/ontology/DCM#BAR) was not found in the value set 'Acquisition Modality Codes' (http://hl7.org/fhir/ValueSet/dicom-cid29|20121129)"); } /** @@ -776,6 +794,55 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline public void testValidateDocument() throws Exception { String vsContents = ClasspathUtil.loadResource("/sample-document.xml"); + ValueSet valuesetDocTypeCodes = loadResource("/dstu3/valueset-doc-typecodes.json", ValueSet.class); + myValueSets.put(valuesetDocTypeCodes.getUrl(), valuesetDocTypeCodes); + myValueSets.put("ValueSet/" + valuesetDocTypeCodes.getIdElement().getIdPart(), valuesetDocTypeCodes); + + ValueSet valuesetV2_0131 = loadResource("/dstu3/valueset-v2-0131.json", ValueSet.class); + myValueSets.put(valuesetV2_0131.getUrl(), valuesetV2_0131); + myValueSets.put("ValueSet/" + valuesetV2_0131.getIdElement().getIdPart(), valuesetV2_0131); + + org.hl7.fhir.dstu3.model.CodeSystem mockOfRoleCode = new org.hl7.fhir.dstu3.model.CodeSystem(); + mockOfRoleCode.setUrl("http://hl7.org/fhir/v3/RoleCode"); + mockOfRoleCode.setContent(org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode.COMPLETE); + mockOfRoleCode.addConcept().setCode("PRN"); + mockOfRoleCode.addConcept().setCode("GPARNT"); + myCodeSystems.put("http://hl7.org/fhir/v3/RoleCode", mockOfRoleCode); + + addValidConcept("http://hl7.org/fhir/v3/RoleCode", "GPARNT"); + addValidConcept("http://hl7.org/fhir/v3/RoleCode", "PRN"); + + + org.hl7.fhir.dstu3.model.CodeSystem mockOfLoinc = new org.hl7.fhir.dstu3.model.CodeSystem(); + mockOfLoinc.setUrl("http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"); + mockOfLoinc.setContent(org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode.COMPLETE); + + String[] validLoincCodes = { + "29299-5", + "18776-5", + "69730-0", + "8716-3", + "10160-0", + "29549-3", + "11450-4", + "48765-2", + "30954-2", + "47519-4", + "11369-6", + "29762-2", + "46240-8", + "42348-3", + "42348-3", + "34133-9" + }; + + for (String validLoincCode: validLoincCodes) { + addValidConcept("http://loinc.org", validLoincCode); + mockOfLoinc.addConcept().setCode(validLoincCode); + } + + myCodeSystems.put("http://loinc.org", mockOfLoinc); + ValidationResult output = myVal.validateWithResult(vsContents); logResultsAndReturnNonInformationalOnes(output); assertTrue(output.isSuccessful()); @@ -789,7 +856,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline Patient resource = loadResource("/dstu3/nl/nl-core-patient-01.json", Patient.class); ValidationResult results = myVal.validateWithResult(resource); List outcome = logResultsAndReturnNonInformationalOnes(results); - assertThat(outcome.toString()).contains("The Coding provided (urn:oid:2.16.840.1.113883.2.4.4.16.34#6030) is not in the value set 'LandGBACodelijst'"); + assertThat(outcome.toString()).contains("The Coding provided (urn:oid:2.16.840.1.113883.2.4.4.16.34#6030) was not found in the value set 'LandGBACodelijst'"); } private void loadNL() throws IOException { @@ -954,7 +1021,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult output = myVal.validateWithResult(encoded); assertThat(output.getMessages().size()).as(output.toString()).isEqualTo(1); - assertEquals("The extension http://hl7.org/fhir/v3/ethnicity is unknown, and not allowed here", output.getMessages().get(0).getMessage()); + assertEquals("The extension http://hl7.org/fhir/v3/ethnicity could not be found so is not allowed here", output.getMessages().get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, output.getMessages().get(0).getSeverity()); } @@ -1022,7 +1089,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult output = myVal.validateWithResult(input); List res = logResultsAndReturnNonInformationalOnes(output); assertThat(res.size()).as(output.toString()).isEqualTo(1); - assertEquals("A code with no system has no defined meaning. A system should be provided", output.getMessages().get(0).getMessage()); + assertEquals("Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided", output.getMessages().get(0).getMessage()); } /** @@ -1111,7 +1178,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline List errors = logResultsAndReturnAll(output); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#12345')", errors.get(0).getMessage()); } @Test @@ -1139,7 +1206,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline public void testValidateResourceContainingProfileDeclarationDoesntResolve() { addValidConcept("http://loinc.org", "12345"); - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getMeta().addProfile("http://foo/structuredefinition/myprofile"); input.getCode().addCoding().setSystem("http://loinc.org").setCode("12345"); @@ -1148,7 +1215,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline myInstanceVal.setValidationSupport(myValidationSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString()).contains("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it is unknown"); + assertThat(errors.toString()).contains("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found"); } @Test @@ -1165,9 +1232,17 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline } + private Observation createObservationWithDefaultSubjectPerfomerEffective() { + Observation observation = new Observation(); + observation.setSubject(new Reference("Patient/123")); + observation.addPerformer(new Reference("Practitioner/124")); + observation.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + return observation; + } + @Test public void testValidateResourceWithDefaultValueset() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.setStatus(ObservationStatus.FINAL); input.getCode().setText("No code here!"); @@ -1191,12 +1266,14 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult output = myVal.validateWithResult(input); logResultsAndReturnAll(output); - assertThat(output.getMessages().get(0).getMessage()).contains("The value provided ('notvalidcode') is not in the value set 'ObservationStatus'"); + assertThat(output.getMessages().get(0).getMessage()).contains("The value provided ('notvalidcode') was not found in the value set 'ObservationStatus'"); } @Test public void testValidateResourceWithExampleBindingCodeValidationFailing() { - Observation input = new Observation(); + addValidConcept("http://loinc.org", "12345"); + + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1222,13 +1299,13 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.size()).as(errors.toString()).isGreaterThan(0); - assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://acme.org#9988877')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); myInstanceVal.setValidationSupport(myValidationSupport); addValidConcept("http://loinc.org", "12345"); @@ -1243,7 +1320,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoincWithExpansion() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); ValueSetExpansionComponent expansionComponent = new ValueSetExpansionComponent(); expansionComponent.addContains().setSystem("http://loinc.org").setCode("12345").setDisplay("Some display code"); @@ -1258,12 +1335,12 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertThat(errors).hasSize(1); - assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#1234')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingNonLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); myInstanceVal.setValidationSupport(myValidationSupport); addValidConcept("http://acme.org", "12345"); @@ -1320,6 +1397,9 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline IValidationPolicyAdvisor policyAdvisor = mock(IValidationPolicyAdvisor.class); IValidatorResourceFetcher fetcher = mock(IValidatorResourceFetcher.class); + when(policyAdvisor.policyForElement(any(), any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.ElementValidationAction.class)); + when(policyAdvisor.policyForCodedContent(any(),any(),any(),any(),any(),any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.CodedContentValidationAction.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); @@ -1334,8 +1414,9 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline @Test public void testValueWithWhitespace() throws IOException { - String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8); + addValidConcept("http://loinc.org", "34133-1"); + String input = IOUtils.toString(FhirInstanceValidatorDstu3Test.class.getResourceAsStream("/dstu3-rick-test.json"), Charsets.UTF_8); ValidationResult results = myVal.validateWithResult(input); List outcome = logResultsAndReturnNonInformationalOnes(results); assertThat(outcome).hasSize(2); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index faafad092d4..7804b9df10f 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -271,7 +271,7 @@ public class QuestionnaireResponseValidatorDstu3Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString()).contains("Unknown code: http://codesystems.com/system / code1 for 'http://codesystems.com/system#code1'"); + assertThat(errors.toString()).contains("Unknown code: http://codesystems.com/system / code1 (for 'http://codesystems.com/system#code1')"); assertThat(errors.toString()).contains("QuestionnaireResponse.item[0].answer[0]"); // Unhandled system @@ -1080,7 +1080,7 @@ public class QuestionnaireResponseValidatorDstu3Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString()).contains("Unknown code for 'http://codesystems.com/system#code1'"); + assertThat(errors.toString()).contains("Unknown code (for 'http://codesystems.com/system#code1')"); assertThat(errors.toString()).contains("QuestionnaireResponse.item[0].answer[0]"); // Partial code @@ -1092,9 +1092,9 @@ public class QuestionnaireResponseValidatorDstu3Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); assertThat(errors.getMessages()).hasSize(2); - assertThat(errors.getMessages().get(0).getMessage()).contains("A code with no system has no defined meaning. A system should be provided"); + assertThat(errors.getMessages().get(0).getMessage()).contains("A code with no system has no defined meaning, and it cannot be validated. A system should be provided"); assertThat(errors.getMessages().get(0).getLocationString()).contains("QuestionnaireResponse.item[0].answer[0]"); - assertThat(errors.getMessages().get(1).getMessage()).contains("The code provided code1 in the system null) is not in the options value set (ValueSet[http://somevalueset]) in the questionnaire: Validation failed"); + assertThat(errors.getMessages().get(1).getMessage()).contains("The code 'code1' in the system 'null' is not in the options value set (ValueSet[http://somevalueset]) specified by the questionnaire. Terminology Error: Validation failed"); assertThat(errors.getMessages().get(1).getLocationString()).contains("QuestionnaireResponse.item[0].answer[0]"); qa = new QuestionnaireResponse(); @@ -1105,9 +1105,9 @@ public class QuestionnaireResponseValidatorDstu3Test { errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); assertThat(errors.getMessages()).hasSize(2); - assertThat(errors.getMessages().get(0).getMessage()).contains("A code with no system has no defined meaning. A system should be provided"); + assertThat(errors.getMessages().get(0).getMessage()).contains("A code with no system has no defined meaning, and it cannot be validated. A system should be provided"); assertThat(errors.getMessages().get(0).getLocationString()).contains("QuestionnaireResponse.item[0].answer[0]"); - assertThat(errors.getMessages().get(1).getMessage()).contains("The code provided code1 in the system null) is not in the options value set (ValueSet[http://somevalueset]) in the questionnaire: Validation failed"); + assertThat(errors.getMessages().get(1).getMessage()).contains("The code 'code1' in the system 'null' is not in the options value set (ValueSet[http://somevalueset]) specified by the questionnaire. Terminology Error: Validation failed"); assertThat(errors.getMessages().get(1).getLocationString()).contains("QuestionnaireResponse.item[0].answer[0]"); qa = new QuestionnaireResponse(); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java index af1b624de2f..deb1a507ff1 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireValidatorDstu3Test.java @@ -116,8 +116,8 @@ public class QuestionnaireValidatorDstu3Test extends BaseValidationTestWithInlin ValidationResult errors = myVal.validateWithResult(q); ourLog.info(errors.toString()); - assertEquals(true, errors.isSuccessful()); - assertThat(errors.getMessages().get(0).getMessage()).contains("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 = null#text-box)"); + assertThat(errors.isSuccessful()).isTrue(); + assertThat(errors.getMessages().get(1).getMessage()).contains("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 = null#text-box)"); } } 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 b4b2a8d0a91..a2bc7cc9869 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 @@ -176,8 +176,12 @@ public class ResourceValidatorDstu3Test extends BaseValidationTestWithInlineMock ValidationResult output = val.validateWithResult(p); List all = logResultsAndReturnNonInformationalOnes(output); - assertThat(output.getMessages().get(0).getMessage()).contains("None of the codings provided are in the value set 'Marital Status Codes'"); - assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity()); + + assertThat(output.getMessages().get(0).getMessage()).contains("Unknown code 'http://hl7.org/fhir/v3/MaritalStatus#FOO'"); + assertEquals(ResultSeverityEnum.ERROR, output.getMessages().get(0).getSeverity()); + + assertThat(output.getMessages().get(1).getMessage()).contains("None of the codings provided are in the value set 'Marital Status Codes'"); + assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(1).getSeverity()); } @Test diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java index 6ca121b07ee..1a82ca57426 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/utils/FhirPathEngineTest.java @@ -13,6 +13,7 @@ import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.dstu3.model.Specimen; import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.fhirpath.FHIRPathEngine; import org.hl7.fhir.exceptions.FHIRException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/utils/FhirPathEngineR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/utils/FhirPathEngineR4Test.java index 1ea65e5d8f2..15bbc3a8bf3 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/utils/FhirPathEngineR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/utils/FhirPathEngineR4Test.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.dstu3.utils.FhirPathEngineTest; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.fhirpath.FHIRPathEngine; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4.model.Base; import org.hl7.fhir.r4.model.BooleanType; 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 457617b26f4..d1c2f409947 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 @@ -67,7 +67,7 @@ import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; 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.r4.fhirpath.FHIRPathEngine; import org.hl7.fhir.r5.elementmodel.JsonParser; import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; @@ -87,6 +87,8 @@ import org.mockito.stubbing.Answer; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -158,6 +160,8 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc myValidConcepts = new ArrayList<>(); + addMockedValueSets(); + when(myMockSupport.expandValueSet(any(), nullable(ValueSetExpansionOptions.class), any(IBaseResource.class))).thenAnswer(t -> { ValueSet arg = (ValueSet) t.getArgument(2, IBaseResource.class); ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getCompose().getIncludeFirstRep().getSystem()); @@ -171,15 +175,6 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc valueset.setExpansion(retVal); return new ValueSetExpander.ValueSetExpansionOutcome(valueset); }); - when(myMockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() { - @Override - public Boolean answer(InvocationOnMock theInvocation) { - String argument = theInvocation.getArgument(1, String.class); - boolean retVal = myValidSystems.contains(argument); - ourLog.debug("isCodeSystemSupported({}) : {}", argument, retVal); - return retVal; - } - }); when(myMockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer() { @Override public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable { @@ -197,26 +192,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc return retVal; } }); - when(myMockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() { - @Override - public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) { - ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class); - String system = theInvocation.getArgument(2, String.class); - String code = theInvocation.getArgument(3, String.class); - String display = theInvocation.getArgument(4, String.class); - String valueSetUrl = theInvocation.getArgument(5, String.class); - IValidationSupport.CodeValidationResult retVal; - if (myValidConcepts.contains(system + "___" + code)) { - retVal = new IValidationSupport.CodeValidationResult().setCode(code); - } else if (myValidSystems.contains(system)) { - return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"); - } else { - retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); - } - ourLog.debug("validateCode({}, {}, {}, {}) : {}", system, code, display, valueSetUrl, retVal); - return retVal; - } - }); + when(myMockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer() { @Override public CodeSystem answer(InvocationOnMock theInvocation) { @@ -278,6 +254,41 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc } + private void addMockedValueSets() { + + when(myMockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() { + @Override + public Boolean answer(InvocationOnMock theInvocation) { + String argument = theInvocation.getArgument(1, String.class); + boolean retVal = myValidSystems.contains(argument); + ourLog.debug("isCodeSystemSupported({}) : {}", argument, retVal); + return retVal; + } + }); + + when(myMockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() { + @Override + public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) { + ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class); + String system = theInvocation.getArgument(2, String.class); + String code = theInvocation.getArgument(3, String.class); + String display = theInvocation.getArgument(4, String.class); + String valueSetUrl = theInvocation.getArgument(5, String.class); + IValidationSupport.CodeValidationResult retVal; + if (myValidConcepts.contains(system + "___" + code)) { + retVal = new IValidationSupport.CodeValidationResult().setCode(code); + } else if (myValidSystems.contains(system)) { + final String message = "Unknown code (for '" + system + "#" + code + "')"; + return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); + } else { + retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); + } + ourLog.debug("validateCode({}, {}, {}, {}) : {}", system, code, display, valueSetUrl, retVal); + return retVal; + } + }); + } + private Object defaultString(Integer theLocationLine) { return theLocationLine != null ? theLocationLine.toString() : ""; } @@ -509,7 +520,12 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc */ @Test public void testValidateDoesntEnforceBestPracticesByDefault() { + addValidConcept("http://loinc.org", "1234"); + Observation input = new Observation(); + input.addPerformer(new Reference("Practitioner/124")); + input.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(ObservationStatus.AMENDED); input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); @@ -526,7 +542,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc result = val.validateWithResult(input); all = logResultsAndReturnAll(result); assertTrue(result.isSuccessful()); - assertThat(all).isEmpty(); + assertThat(all).hasSize(1); + assertEquals("Best Practice Recommendation: In general, all observations should have a subject", all.get(0).getMessage()); + assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity()); + // With BPs enabled val = ourCtx.newValidator(); @@ -537,7 +556,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc result = val.validateWithResult(input); all = logResultsAndReturnAll(result); assertFalse(result.isSuccessful()); - assertEquals("All observations should have a subject", all.get(0).getMessage()); + assertEquals("Best Practice Recommendation: In general, all observations should have a subject", all.get(0).getMessage()); } @@ -691,12 +710,13 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc @Test public void testLargeBase64() throws IOException { + addValidConcept("http://loinc.org", "1-8"); + String input = IOUtils.toString(FhirInstanceValidatorR4Test.class.getResourceAsStream("/r4/diagnosticreport-example-gingival-mass.json"), Constants.CHARSET_UTF8); ValidationResult output = myFhirValidator.validateWithResult(input); - List errors = logResultsAndReturnAll(output); - assertThat(errors).hasSize(2); - assertThat(errors.get(0).getMessage()).contains("None of the codings provided are in the value set 'LOINC Diagnostic Report Codes'"); - assertEquals("Base64 encoded values SHOULD not contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway", errors.get(1).getMessage()); + List messages = logResultsAndReturnAll(output); + assertThat(messages).hasSize(1); + assertEquals("Base64 encoded values SHOULD not contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway", messages.get(0).getMessage()); } @Test @@ -1009,7 +1029,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc ValidationResult output = myFhirValidator.validateWithResult(encoded); assertThat(output.getMessages().size()).as(output.toString()).isEqualTo(1); - assertEquals("The extension http://hl7.org/fhir/v3/ethnicity is unknown, and not allowed here", output.getMessages().get(0).getMessage()); + assertEquals("The extension http://hl7.org/fhir/v3/ethnicity could not be found so is not allowed here", output.getMessages().get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, output.getMessages().get(0).getSeverity()); } @@ -1097,7 +1117,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc ValidationResult output = myFhirValidator.validateWithResult(input); List res = logResultsAndReturnNonInformationalOnes(output); assertThat(res.size()).as(output.toString()).isEqualTo(1); - assertEquals("A code with no system has no defined meaning. A system should be provided", output.getMessages().get(0).getMessage()); + assertEquals("Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided", output.getMessages().get(0).getMessage()); } /** @@ -1161,12 +1181,22 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc } + private Observation createObservationWithDefaultSubjectPerfomerEffective() { + Observation observation = new Observation(); + observation.setSubject(new Reference("Patient/123")); + observation.addPerformer(new Reference("Practitioner/124")); + observation.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + return observation; + } + + @Test public void testValidateResourceContainingLoincCode() { addValidConcept("http://loinc.org", "1234567"); - Observation input = new Observation(); - // input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation"); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + input.addIdentifier().setSystem("http://acme").setValue("12345"); input.getEncounter().setReference("http://foo.com/Encounter/9"); @@ -1178,7 +1208,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc List errors = logResultsAndReturnAll(output); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#12345')", errors.get(0).getMessage()); } @Test @@ -1207,7 +1237,8 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc public void testValidateResourceContainingProfileDeclarationDoesntResolve() { addValidConcept("http://loinc.org", "12345"); - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.getMeta().addProfile("http://foo/structuredefinition/myprofile"); @@ -1219,7 +1250,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc List errors = logResultsAndReturnNonInformationalOnes(output); assertThat(errors).hasSize(1); - assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it is unknown", errors.get(0).getMessage()); + assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found", errors.get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); } @@ -1240,7 +1271,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithDefaultValueset() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(ObservationStatus.FINAL); @@ -1267,7 +1298,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc ""; ValidationResult output = myFhirValidator.validateWithResult(input); logResultsAndReturnAll(output); - assertEquals("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()); + assertEquals("The value provided ('notvalidcode') was not found 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()); } @Test @@ -1300,7 +1331,9 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithExampleBindingCodeValidationFailing() { - Observation input = new Observation(); + addValidConcept("http://loinc.org", "12345"); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1329,6 +1362,9 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc IValidationPolicyAdvisor policy = mock(IValidationPolicyAdvisor.class, withSettings().verboseLogging()); when(policy.policyForReference(any(), any(), any(), any())).thenReturn(ReferenceValidationPolicy.CHECK_VALID); + when(policy.policyForElement(any(), any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.ElementValidationAction.class)); + when(policy.policyForCodedContent(any(),any(),any(),any(),any(),any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.CodedContentValidationAction.class)); + myInstanceVal.setValidatorPolicyAdvisor(policy); IValidatorResourceFetcher fetcher = mock(IValidatorResourceFetcher.class, withSettings().verboseLogging()); @@ -1352,7 +1388,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithExampleBindingCodeValidationFailingNonLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1364,13 +1400,14 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc ValidationResult output = myFhirValidator.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.size()).as(errors.toString()).isGreaterThan(0); - assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://acme.org#9988877')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1386,7 +1423,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoincWithExpansion() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); ValueSetExpansionComponent expansionComponent = new ValueSetExpansionComponent(); @@ -1402,12 +1439,12 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc ValidationResult output = myFhirValidator.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertThat(errors).hasSize(1); - assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#1234')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingNonLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1452,8 +1489,8 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc output = myFhirValidator.validateWithResult(input); all = logResultsAndReturnNonInformationalOnes(output); assertThat(all).hasSize(2); - assertThat(all.get(0).getMessage()).contains("The unit 'Heck' is unknown' at position 0 for 'http://unitsofmeasure.org#Heck'"); - assertThat(all.get(1).getMessage()).contains("The value provided ('Heck') is not in the value set 'Body Temperature Units'"); + assertThat(all.get(0).getMessage()).contains("Error processing unit 'Heck': The unit 'Heck' is unknown' at position 0 (for 'http://unitsofmeasure.org#Heck')"); + assertThat(all.get(1).getMessage()).contains("The value provided ('Heck') was not found in the value set 'Body Temperature Units'"); } @@ -1500,13 +1537,15 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc 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); + when(policyAdvisor.policyForContained(any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(ContainedReferenceValidationPolicy.CHECK_TYPE); + when(policyAdvisor.policyForElement(any(), any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.ElementValidationAction.class)); + when(policyAdvisor.policyForCodedContent(any(),any(),any(),any(),any(),any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.CodedContentValidationAction.class)); myInstanceVal.setValidatorResourceFetcher(resourceFetcher); myInstanceVal.setValidatorPolicyAdvisor(policyAdvisor); myFhirValidator.validateWithResult(encoded); verify(resourceFetcher, times(12)).resolveURL(any(), any(), anyString(), anyString(), anyString(), anyBoolean()); - verify(policyAdvisor, times(12)).policyForContained(any(), any(), any(), any(), any(), any(), any()); + verify(policyAdvisor, times(12)).policyForContained(any(), any(), any(), any(), any(), any(), any(), any(), any()); } @Test @@ -1526,6 +1565,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc public void testValidateCurrency() { String input = "{\n" + " \"resourceType\": \"Invoice\",\n" + + "\"text\": {\n" + + " \"status\": \"generated\",\n" + + " \"div\": \"
AA
\"\n" + + " }," + " \"status\": \"draft\",\n" + " \"date\": \"2020-01-08\",\n" + " \"totalGross\": {\n" + @@ -1544,6 +1587,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc public void testValidateCurrency_Wrong() { String input = "{\n" + " \"resourceType\": \"Invoice\",\n" + + "\"text\": {\n" + + " \"status\": \"generated\",\n" + + " \"div\": \"
AA
\"\n" + + " }," + " \"status\": \"draft\",\n" + " \"date\": \"2020-01-08\",\n" + " \"totalGross\": {\n" + @@ -1553,9 +1600,9 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc "}"; ValidationResult output = myFhirValidator.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.size()).as(errors.toString()).isEqualTo(1); - assertThat(errors.get(0).getMessage()).contains("The value provided ('BLAH') is not in the value set 'CurrencyCode' (http://hl7.org/fhir/ValueSet/currencies|4.0.1)"); - assertThat(errors.get(0).getMessage()).contains("error message = Unknown code \"urn:iso:std:iso:4217#BLAH\""); + assertThat(errors.size()).as(errors.toString()).isEqualTo(2); + assertThat(errors.get(1).getMessage()).contains("The value provided ('BLAH') was not found in the value set 'CurrencyCode' (http://hl7.org/fhir/ValueSet/currencies|4.0.1)"); + assertThat(errors.get(1).getMessage()).contains("error message = Unknown code \"urn:iso:std:iso:4217#BLAH\""); } @@ -1564,6 +1611,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc public void testValidateReferenceTargetType_Correct() { AllergyIntolerance allergy = new AllergyIntolerance(); + allergy.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); allergy.getClinicalStatus().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical").setCode("active").setDisplay("Active"); allergy.getVerificationStatus().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/allergyintolerance-verification").setCode("confirmed").setDisplay("Confirmed"); allergy.setPatient(new Reference("Patient/123")); @@ -1642,9 +1690,12 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc @Test void testValidateCommonCodes_Ucum_ErrorMessageIsPreserved() { - buildValidationSupportWithLogicalAndSupport(false); + addValidConcept("http://loinc.org", "1234"); - Observation input = new Observation(); + buildValidationSupportWithLogicalAndSupport(false); + addMockedValueSets(); + + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(ObservationStatus.AMENDED); input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); @@ -1663,29 +1714,32 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc OperationOutcome oo = (OperationOutcome) result.toOperationOutcome(); ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("Error processing unit 'MG/DL': The unit 'DL' is unknown' at position 3 for 'http://unitsofmeasure.org#MG/DL'", result.getMessages().get(0).getMessage()); + assertEquals("Error processing unit 'MG/DL': The unit 'DL' is unknown' at position 3 (for 'http://unitsofmeasure.org#MG/DL')", result.getMessages().get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, result.getMessages().get(0).getSeverity()); - assertEquals(15, result.getMessages().get(0).getLocationLine()); + assertEquals(22, result.getMessages().get(0).getLocationLine()); assertEquals(4, result.getMessages().get(0).getLocationCol()); assertEquals("Observation.value.ofType(Quantity)", result.getMessages().get(0).getLocationString()); assertEquals("Terminology_PassThrough_TX_Message", result.getMessages().get(0).getMessageId()); - assertEquals(15, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); + assertEquals(22, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); assertEquals(4, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue()); assertEquals("Terminology_PassThrough_TX_Message", ((StringType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue()); - assertEquals("Error processing unit 'MG/DL': The unit 'DL' is unknown' at position 3 for 'http://unitsofmeasure.org#MG/DL'", oo.getIssue().get(0).getDiagnostics()); + assertEquals("Error processing unit 'MG/DL': The unit 'DL' is unknown' at position 3 (for 'http://unitsofmeasure.org#MG/DL')", oo.getIssue().get(0).getDiagnostics()); assertEquals(OperationOutcome.IssueType.PROCESSING, oo.getIssue().get(0).getCode()); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity()); assertThat(oo.getIssue().get(0).getLocation()).hasSize(2); assertEquals("Observation.value.ofType(Quantity)", oo.getIssue().get(0).getLocation().get(0).getValue()); - assertEquals("Line[15] Col[4]", oo.getIssue().get(0).getLocation().get(1).getValue()); + assertEquals("Line[22] Col[4]", oo.getIssue().get(0).getLocation().get(1).getValue()); } @Test void testValidateCommonCodes_Currency_ErrorMessageIsPreserved() { - buildValidationSupportWithLogicalAndSupport(false); + addValidConcept("http://loinc.org", "1234"); - Observation input = new Observation(); + buildValidationSupportWithLogicalAndSupport(false); + addMockedValueSets(); + + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(ObservationStatus.AMENDED); input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); @@ -1704,22 +1758,22 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc OperationOutcome oo = (OperationOutcome) result.toOperationOutcome(); ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertEquals("Unknown code 'urn:iso:std:iso:4217#blah' for 'urn:iso:std:iso:4217#blah'", result.getMessages().get(0).getMessage()); + assertEquals("Unknown code 'urn:iso:std:iso:4217#blah'", result.getMessages().get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, result.getMessages().get(0).getSeverity()); - assertEquals(15, result.getMessages().get(0).getLocationLine()); + assertEquals(22, result.getMessages().get(0).getLocationLine()); assertEquals(4, result.getMessages().get(0).getLocationCol()); assertEquals("Observation.value.ofType(Quantity)", result.getMessages().get(0).getLocationString()); assertEquals("Terminology_PassThrough_TX_Message", result.getMessages().get(0).getMessageId()); - assertEquals(15, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); + assertEquals(22, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-line").getValue()).getValue()); assertEquals(4, ((IntegerType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-col").getValue()).getValue()); assertEquals("Terminology_PassThrough_TX_Message", ((StringType) oo.getIssue().get(0).getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id").getValue()).getValue()); - assertEquals("Unknown code 'urn:iso:std:iso:4217#blah' for 'urn:iso:std:iso:4217#blah'", oo.getIssue().get(0).getDiagnostics()); + assertEquals("Unknown code 'urn:iso:std:iso:4217#blah'", oo.getIssue().get(0).getDiagnostics()); assertEquals(OperationOutcome.IssueType.PROCESSING, oo.getIssue().get(0).getCode()); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssue().get(0).getSeverity()); assertThat(oo.getIssue().get(0).getLocation()).hasSize(2); assertEquals("Observation.value.ofType(Quantity)", oo.getIssue().get(0).getLocation().get(0).getValue()); - assertEquals("Line[15] Col[4]", oo.getIssue().get(0).getLocation().get(1).getValue()); + assertEquals("Line[22] Col[4]", oo.getIssue().get(0).getLocation().get(1).getValue()); } @@ -1740,7 +1794,7 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc final ValidationResult output = myFhirValidator.validateWithResult(encoded); final List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors).isEmpty(); + assertFalse(output.isSuccessful()); } @Test diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/HapiWorkerContextTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/HapiWorkerContextTest.java index bff9ad2548a..06d70804530 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/HapiWorkerContextTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/HapiWorkerContextTest.java @@ -13,6 +13,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.context.IWorkerContext; import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.r4.model.ValueSet; +import org.hl7.fhir.utilities.FhirPublication; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.junit.jupiter.api.Test; import org.springframework.core.io.Resource; @@ -58,7 +59,8 @@ public class HapiWorkerContextTest extends BaseValidationTestWithInlineMocks { // Built-in Codes vs.setUrl("http://hl7.org/fhir/ValueSet/fm-status"); - ValidationOptions options = new ValidationOptions().withGuessSystem(); + ValidationOptions options = new ValidationOptions(FhirPublication.fromCode( + workerCtx.getVersion().toString())).withGuessSystem(); outcome = workerCtx.validateCode(options, "active", vs); assertThat(outcome.isOk()).as(outcome.getMessage()).isEqualTo(true); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/NpmPackageValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/NpmPackageValidationSupportTest.java index 74502c718af..0b1b4c34119 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/NpmPackageValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/NpmPackageValidationSupportTest.java @@ -24,6 +24,7 @@ import java.util.Map; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class NpmPackageValidationSupportTest extends BaseValidationTestWithInlineMocks { @@ -108,7 +109,9 @@ public class NpmPackageValidationSupportTest extends BaseValidationTestWithInlin String bundle = loadResource("/r4/mhd_minimal_provide_document_bundle.json"); ValidationResult validationResult = validator.validateWithResult(bundle); - assertEquals(1, validationResult.getMessages().size()); + assertEquals(3, validationResult.getMessages().size()); + + assertTrue(validationResult.isSuccessful()); String outcomeSerialized = myFhirContext.newJsonParser() .setPrettyPrint(true) diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java index dc14318c87d..4a5b10e6cb3 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java @@ -267,7 +267,7 @@ public class QuestionnaireResponseValidatorR4Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString()).contains("Unknown code for 'http://codesystems.com/system#code1'"); + assertThat(errors.toString()).contains("Unknown code (for 'http://codesystems.com/system#code1')"); assertThat(errors.toString()).contains("QuestionnaireResponse.item[0].answer[0]"); qa = new QuestionnaireResponse(); @@ -278,7 +278,7 @@ public class QuestionnaireResponseValidatorR4Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString()).contains("Unknown code 'http://codesystems.com/system2#code3' for 'http://codesystems.com/system2#code3'"); + assertThat(errors.toString()).contains("Unknown code 'http://codesystems.com/system2#code3'"); assertThat(errors.toString()).contains("QuestionnaireResponse.item[0].answer[0]"); } @@ -327,10 +327,13 @@ public class QuestionnaireResponseValidatorR4Test { public void testMissingRequiredQuestion() { Questionnaire q = new Questionnaire(); + q.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1"); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); @@ -730,9 +733,12 @@ public class QuestionnaireResponseValidatorR4Test { @Test public void testUnexpectedAnswer() { Questionnaire q = new Questionnaire(); + q.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.BOOLEAN); QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1"); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); @@ -748,9 +754,13 @@ public class QuestionnaireResponseValidatorR4Test { @Test public void testUnexpectedGroup() { Questionnaire q = new Questionnaire(); + q.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.BOOLEAN); QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1"); qa.addItem().setLinkId("link1").addItem().setLinkId("link2"); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java index 96e2a3f585f..347d4a0adb0 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java @@ -61,7 +61,7 @@ import org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r4b.model.ValueSet; import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r4b.terminologies.ValueSetExpander; -import org.hl7.fhir.r4b.utils.FHIRPathEngine; +import org.hl7.fhir.r4b.fhirpath.FHIRPathEngine; import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags; import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; @@ -80,6 +80,8 @@ import org.mockito.stubbing.Answer; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -200,7 +202,8 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo if (myValidConcepts.contains(system + "___" + code)) { retVal = new IValidationSupport.CodeValidationResult().setCode(code); } else if (myValidSystems.contains(system)) { - return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage("Unknown code"); + final String message = "Unknown code (for '" + system + "#" + code + "')"; + return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); } else { retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); } @@ -212,9 +215,10 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo @Override public CodeSystem answer(InvocationOnMock theInvocation) { String system = theInvocation.getArgument(0, String.class); - if ("http://loinc.org".equals(system)) { + if ("http://loinc.org".equals(system) + ) { CodeSystem retVal = new CodeSystem(); - retVal.setUrl("http://loinc.org"); + retVal.setUrl(system); retVal.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); ourLog.debug("fetchCodeSystem({}) : {}", new Object[]{theInvocation.getArguments()[0], retVal}); return retVal; @@ -335,9 +339,9 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo @Test public void testValidateCodeWithTailingSpace() { + Patient p = new Patient(); - p - .getMaritalStatus() + p.getMaritalStatus() .addCoding() .setSystem("http://foo") .setCode("AA "); @@ -470,7 +474,12 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo */ @Test public void testValidateDoesntEnforceBestPracticesByDefault() { + addValidConcept("http://loinc.org", "1234"); + Observation input = new Observation(); + input.addPerformer(new Reference("Practitioner/124")); + input.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(Enumerations.ObservationStatus.AMENDED); input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); @@ -487,7 +496,9 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo result = val.validateWithResult(input); all = logResultsAndReturnAll(result); assertTrue(result.isSuccessful()); - assertThat(all).isEmpty(); + assertThat(all).hasSize(1); + assertEquals("Best Practice Recommendation: In general, all observations should have a subject", all.get(0).getMessage()); + assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity()); // With BPs enabled val = ourCtx.newValidator(); @@ -498,7 +509,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo result = val.validateWithResult(input); all = logResultsAndReturnAll(result); assertFalse(result.isSuccessful()); - assertEquals("All observations should have a subject", all.get(0).getMessage()); + assertEquals("Best Practice Recommendation: In general, all observations should have a subject", all.get(0).getMessage()); } @@ -652,12 +663,13 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo @Test public void testLargeBase64() throws IOException { + addValidConcept("http://loinc.org", "1-8"); + String input = IOUtils.toString(FhirInstanceValidatorR4BTest.class.getResourceAsStream("/r4/diagnosticreport-example-gingival-mass.json"), Constants.CHARSET_UTF8); ValidationResult output = myFhirValidator.validateWithResult(input); - List errors = logResultsAndReturnAll(output); - assertThat(errors).hasSize(2); - assertThat(errors.get(0).getMessage()).contains("None of the codings provided are in the value set 'LOINC Diagnostic Report Codes'"); - assertEquals("Base64 encoded values SHOULD not contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway", errors.get(1).getMessage()); + List messages = logResultsAndReturnAll(output); + assertThat(messages).hasSize(1); + assertEquals("Base64 encoded values SHOULD not contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway", messages.get(0).getMessage()); } @Test @@ -970,7 +982,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo ValidationResult output = myFhirValidator.validateWithResult(encoded); assertThat(output.getMessages().size()).as(output.toString()).isEqualTo(1); - assertEquals("The extension http://hl7.org/fhir/v3/ethnicity is unknown, and not allowed here", output.getMessages().get(0).getMessage()); + assertEquals("The extension http://hl7.org/fhir/v3/ethnicity could not be found so is not allowed here", output.getMessages().get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, output.getMessages().get(0).getSeverity()); } @@ -1058,7 +1070,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo ValidationResult output = myFhirValidator.validateWithResult(input); List res = logResultsAndReturnNonInformationalOnes(output); assertThat(res.size()).as(output.toString()).isEqualTo(1); - assertEquals("A code with no system has no defined meaning. A system should be provided", output.getMessages().get(0).getMessage()); + assertEquals("Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided", output.getMessages().get(0).getMessage()); } /** @@ -1107,7 +1119,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo List errors = logResultsAndReturnAll(output); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#12345')", errors.get(0).getMessage()); } @Test @@ -1132,11 +1144,19 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo assertThat(errors.toString()).contains("Observation.device: minimum required = 1, but only found 0"); } + private Observation createObservationWithDefaultSubjectPerfomerEffective() { + Observation observation = new Observation(); + observation.setSubject(new Reference("Patient/123")); + observation.addPerformer(new Reference("Practitioner/124")); + observation.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + return observation; + } + @Test public void testValidateResourceContainingProfileDeclarationDoesntResolve() { addValidConcept("http://loinc.org", "12345"); - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.getMeta().addProfile("http://foo/structuredefinition/myprofile"); @@ -1148,7 +1168,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo List errors = logResultsAndReturnNonInformationalOnes(output); assertThat(errors).hasSize(1); - assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it is unknown", errors.get(0).getMessage()); + assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found", errors.get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); } @@ -1169,7 +1189,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo @Test public void testValidateResourceWithDefaultValueset() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(Enumerations.ObservationStatus.FINAL); @@ -1196,7 +1216,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo ""; ValidationResult output = myFhirValidator.validateWithResult(input); logResultsAndReturnAll(output); - assertEquals("The value provided ('notvalidcode') is not in the value set 'ObservationStatus' (http://hl7.org/fhir/ValueSet/observation-status|4.3.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')", output.getMessages().get(0).getMessage()); + assertEquals("The value provided ('notvalidcode') was not found in the value set 'ObservationStatus' (http://hl7.org/fhir/ValueSet/observation-status|4.3.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')", output.getMessages().get(0).getMessage()); } @Test @@ -1229,7 +1249,9 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo @Test public void testValidateResourceWithExampleBindingCodeValidationFailing() { - Observation input = new Observation(); + addValidConcept("http://loinc.org", "12345"); + + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1257,13 +1279,13 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo ValidationResult output = myFhirValidator.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.size()).as(errors.toString()).isGreaterThan(0); - assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://acme.org#9988877')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1279,7 +1301,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoincWithExpansion() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); ValueSetExpansionComponent expansionComponent = new ValueSetExpansionComponent(); @@ -1295,12 +1317,12 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo ValidationResult output = myFhirValidator.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertThat(errors).hasSize(1); - assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#1234')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingNonLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -1345,8 +1367,8 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo output = myFhirValidator.validateWithResult(input); all = logResultsAndReturnNonInformationalOnes(output); assertThat(all).hasSize(2); - assertThat(all.get(0).getMessage()).contains("The unit 'Heck' is unknown' at position 0 for 'http://unitsofmeasure.org#Heck'"); - assertThat(all.get(1).getMessage()).contains("The value provided ('Heck') is not in the value set 'Body Temperature Units'"); + assertThat(all.get(0).getMessage()).contains("The Coding provided (http://unitsofmeasure.org#Heck) was not found in the value set 'Vital Signs Units' (http://hl7.org/fhir/ValueSet/ucum-vitals-common|4.3.0)"); + assertThat(all.get(1).getMessage()).contains("The value provided ('Heck') was not found in the value set 'Body Temperature Units'"); } @@ -1391,15 +1413,17 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo IValidatorResourceFetcher resourceFetcher = mock(IValidatorResourceFetcher.class); IValidationPolicyAdvisor policyAdvisor = mock(IValidationPolicyAdvisor.class); + when(policyAdvisor.policyForElement(any(), any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.ElementValidationAction.class)); + when(policyAdvisor.policyForCodedContent(any(),any(),any(),any(),any(),any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.CodedContentValidationAction.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); + when(policyAdvisor.policyForContained(any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(ContainedReferenceValidationPolicy.CHECK_TYPE); myInstanceVal.setValidatorResourceFetcher(resourceFetcher); myInstanceVal.setValidatorPolicyAdvisor(policyAdvisor); myFhirValidator.validateWithResult(encoded); verify(resourceFetcher, times(12)).resolveURL(any(), any(), anyString(), anyString(), anyString(), anyBoolean()); - verify(policyAdvisor, times(12)).policyForContained(any(), any(), any(), any(), any(), any(), any()); + verify(policyAdvisor, times(12)).policyForContained(any(), any(), any(), any(), any(), any(), any(), any(), any()); } @Test @@ -1420,6 +1444,10 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo String input = """ { "resourceType": "Invoice", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "status": "draft", "date": "2020-01-08", "totalGross": { @@ -1439,6 +1467,10 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo String input = """ { "resourceType": "Invoice", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "status": "draft", "date": "2020-01-08", "totalGross": { @@ -1448,8 +1480,8 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo }"""; ValidationResult output = myFhirValidator.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.size()).as(errors.toString()).isEqualTo(1); - assertThat(errors.get(0).getMessage()).contains("The value provided ('BLAH') is not in the value set 'CurrencyCode' (http://hl7.org/fhir/ValueSet/currencies|4.3.0), 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')"); + assertThat(errors.size()).as(errors.toString()).isEqualTo(2); + assertThat(errors.get(1).getMessage()).contains("The value provided ('BLAH') was not found in the value set 'CurrencyCode' (http://hl7.org/fhir/ValueSet/currencies|4.3.0), and a code is required from this value set"); } @@ -1458,6 +1490,8 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo public void testValidateReferenceTargetType_Correct() { AllergyIntolerance allergy = new AllergyIntolerance(); + allergy.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + allergy.getClinicalStatus().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical").setCode("active").setDisplay("Active"); allergy.getVerificationStatus().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/allergyintolerance-verification").setCode("confirmed").setDisplay("Confirmed"); allergy.setPatient(new Reference("Patient/123")); @@ -1550,7 +1584,7 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo final ValidationResult output = myFhirValidator.validateWithResult(encoded); final List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors).isEmpty(); + assertFalse(output.isSuccessful()); } @Test @@ -1643,7 +1677,12 @@ public class FhirInstanceValidatorR4BTest extends BaseValidationTestWithInlineMo myMockSupport = mock(IValidationSupport.class); when(myMockSupport.getFhirContext()).thenReturn(ourCtx); - ValidationSupportChain chain = new ValidationSupportChain(myDefaultValidationSupport, myMockSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx), new SnapshotGeneratingValidationSupport(ourCtx)); + ValidationSupportChain chain = new ValidationSupportChain( + myDefaultValidationSupport, + myMockSupport, + new CommonCodeSystemsTerminologyService(ourCtx), + new InMemoryTerminologyServerValidationSupport(ourCtx), + new SnapshotGeneratingValidationSupport(ourCtx)); myValidationSupport = new CachingValidationSupport(chain, theLogicalAnd); myInstanceVal = new FhirInstanceValidator(myValidationSupport); myFhirValidator.registerValidatorModule(myInstanceVal); 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 62bb0a881a9..7068b120d29 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 @@ -59,6 +59,8 @@ import org.mockito.stubbing.Answer; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -72,8 +74,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -91,6 +94,8 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc private Map mySupportedCodeSystemsForExpansion; private FhirValidator myVal; private ArrayList myValidConcepts; + + private Set mySupportedValueSets = new HashSet<>(); private Set myValidSystems = new HashSet<>(); private CachingValidationSupport myValidationSupport; @@ -182,7 +187,8 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc if (myValidConcepts.contains(system + "___" + code)) { retVal = new IValidationSupport.CodeValidationResult().setCode(code); } else if (myValidSystems.contains(system)) { - return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage("Unknown code"); + String theMessage = "Unknown code (for '" + system + "#" + code + "')"; + return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))); } else { retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl); } @@ -282,7 +288,12 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc */ @Test public void testValidateDoesntEnforceBestPracticesByDefault() { + addValidConcept("http://loinc.org", "1234"); + Observation input = new Observation(); + input.addPerformer(new Reference("Practitioner/124")); + input.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(Enumerations.ObservationStatus.AMENDED); input.getCode().addCoding().setSystem("http://loinc.org").setCode("1234").setDisplay("FOO"); @@ -299,7 +310,9 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc result = val.validateWithResult(input); all = logResultsAndReturnAll(result); assertTrue(result.isSuccessful()); - assertThat(all).isEmpty(); + assertThat(all).hasSize(1); + assertEquals("Best Practice Recommendation: In general, all observations should have a subject", all.get(0).getMessage()); + assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity()); // With BPs enabled val = ourCtx.newValidator(); @@ -310,7 +323,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc result = val.validateWithResult(input); all = logResultsAndReturnAll(result); assertFalse(result.isSuccessful()); - assertEquals("All observations should have a subject", all.get(0).getMessage()); + assertEquals("Best Practice Recommendation: In general, all observations should have a subject", all.get(0).getMessage()); } @@ -448,14 +461,21 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc IValidatorResourceFetcher resourceFetcher = mock(IValidatorResourceFetcher.class); IValidationPolicyAdvisor policyAdvisor = mock(IValidationPolicyAdvisor.class); - + when(policyAdvisor.policyForElement(any(), any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.ElementValidationAction.class)); + when(policyAdvisor.policyForCodedContent(any(),any(),any(),any(),any(),any(),any(),any(),any())).thenReturn(EnumSet.allOf(IValidationPolicyAdvisor.CodedContentValidationAction.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(policyAdvisor, times(4)).policyForReference(any(), any(), anyString(), anyString()); + + /* The number of policyForReference invocations is subject to changes in org.hl7.fhir.core InstanceValidator. + The minimum and maximum invocations are based on this test's history and deviations should be investigated. + */ + verify(policyAdvisor, atLeast(4)).policyForReference(any(), any(), anyString(), anyString()); + verify(policyAdvisor, atMost(8)).policyForReference(any(), any(), anyString(), anyString()); + //verify(resourceFetcher, times(3)).fetch(any(), any(), anyString()); } @@ -584,7 +604,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ValidationResult output = myVal.validateWithResult(encoded); assertThat(output.getMessages().size()).as(output.toString()).isEqualTo(1); - assertEquals("The extension http://hl7.org/fhir/v3/ethnicity is unknown, and not allowed here", output.getMessages().get(0).getMessage()); + assertEquals("The extension http://hl7.org/fhir/v3/ethnicity could not be found so is not allowed here", output.getMessages().get(0).getMessage()); assertEquals(ResultSeverityEnum.ERROR, output.getMessages().get(0).getSeverity()); } @@ -673,7 +693,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ValidationResult output = myVal.validateWithResult(input); List res = logResultsAndReturnNonInformationalOnes(output); assertThat(res.size()).as(output.toString()).isEqualTo(1); - assertEquals("A code with no system has no defined meaning. A system should be provided", res.get(0).getMessage()); + assertEquals("Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided", res.get(0).getMessage()); } @Test @@ -695,7 +715,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ValidationResult output = myVal.validateWithResult(input); assertThat(output.getMessages().size()).as(output.toString()).isEqualTo(1); - assertEquals("This does not appear to be a FHIR resource (unknown namespace/name 'noNamespace::Patient')", output.getMessages().get(0).getMessage()); + assertEquals("This content cannot be parsed (unknown or unrecognized XML Root element namespace/name 'noNamespace::Patient')", output.getMessages().get(0).getMessage()); ourLog.info(output.getMessages().get(0).getLocationString()); } @@ -733,7 +753,6 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc */ @Test public void testValidateRelatedPerson() { - /* * Try with a code that is in http://hl7.org/fhir/ValueSet/relatedperson-relationshiptype * and therefore should validate @@ -751,17 +770,22 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc * Now a bad code */ rp = new RelatedPerson(); + rp.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); rp.getPatient().setReference("Patient/1"); rp.addRelationship().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0131").setCode("GAGAGAGA"); results = myVal.validateWithResult(rp); outcome = logResultsAndReturnAll(results); - assertThat(outcome.get(0).getMessage()).contains("None of the codings provided are in the value set 'Patient Relationship Type'"); - assertEquals(ResultSeverityEnum.INFORMATION, outcome.get(0).getSeverity()); - assertThat(outcome.get(1).getMessage()).contains("Unknown code 'http://terminology.hl7.org/CodeSystem/v2-0131#GAGAGAGA'"); - assertEquals(ResultSeverityEnum.ERROR, outcome.get(1).getSeverity()); - assertThat(outcome).hasSize(2); + ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(rp)); + + + assertEquals(2, outcome.size()); + assertThat(outcome.get(0).getMessage()).contains("Unknown code 'http://terminology.hl7.org/CodeSystem/v2-0131#GAGAGAGA'"); + assertEquals(ResultSeverityEnum.ERROR, outcome.get(0).getSeverity()); + assertThat(outcome.get(1).getMessage()).contains("None of the codings provided are in the value set 'Patient Relationship Type'"); + assertEquals(ResultSeverityEnum.INFORMATION, outcome.get(1).getSeverity()); + } @Test @@ -780,8 +804,11 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnAll(output); + ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); + + assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity()); - assertEquals("Unknown code for 'http://loinc.org#12345'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#12345')", errors.get(0).getMessage()); } @Test @@ -810,7 +837,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc public void testValidateResourceContainingProfileDeclarationDoesntResolve() { addValidConcept("http://loinc.org", "12345"); - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.getMeta().addProfile("http://foo/structuredefinition/myprofile"); @@ -820,7 +847,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc myInstanceVal.setValidationSupport(myValidationSupport); ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); - assertThat(errors.toString()).contains("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it is unknown"); + assertThat(errors.toString()).contains("Profile reference 'http://foo/structuredefinition/myprofile' has not been checked because it could not be found"); } @Test @@ -840,7 +867,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithDefaultValueset() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); input.setStatus(Enumerations.ObservationStatus.FINAL); @@ -849,7 +876,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(input)); ValidationResult output = myVal.validateWithResult(input); - assertEquals(output.getMessages().size(), 0); + assertEquals( 0, output.getMessages().size()); } @Test @@ -867,12 +894,22 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ""; ValidationResult output = myVal.validateWithResult(input); logResultsAndReturnAll(output); - assertThat(output.getMessages().get(0).getMessage()).contains("The value provided ('notvalidcode') is not in the value set 'Observation Status' (http://hl7.org/fhir/ValueSet/observation-status|5.0.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')"); + assertThat(output.getMessages().get(0).getMessage()).contains("The value provided ('notvalidcode') was not found in the value set 'Observation Status' (http://hl7.org/fhir/ValueSet/observation-status|5.0.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')"); } + + private Observation createObservationWithDefaultSubjectPerfomerEffective() { + Observation observation = new Observation(); + observation.setSubject(new Reference("Patient/123")); + observation.addPerformer(new Reference("Practitioner/124")); + observation.setEffective(new DateTimeType("2023-01-01T11:22:33Z")); + return observation; + } @Test public void testValidateResourceWithExampleBindingCodeValidationFailing() { - Observation input = new Observation(); + addValidConcept("http://loinc.org", "12345"); + + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -888,7 +925,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithExampleBindingCodeValidationFailingNonLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -900,13 +937,13 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnAll(output); assertThat(errors.size()).as(errors.toString()).isGreaterThan(0); - assertEquals("Unknown code for 'http://acme.org#9988877'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://acme.org#9988877')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -922,7 +959,7 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc @Test public void testValidateResourceWithExampleBindingCodeValidationPassingLoincWithExpansion() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); ValueSetExpansionComponent expansionComponent = new ValueSetExpansionComponent(); @@ -938,12 +975,12 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc ValidationResult output = myVal.validateWithResult(input); List errors = logResultsAndReturnNonInformationalOnes(output); assertThat(errors).hasSize(1); - assertEquals("Unknown code for 'http://loinc.org#1234'", errors.get(0).getMessage()); + assertEquals("Unknown code (for 'http://loinc.org#1234')", errors.get(0).getMessage()); } @Test public void testValidateResourceWithExampleBindingCodeValidationPassingNonLoinc() { - Observation input = new Observation(); + Observation input = createObservationWithDefaultSubjectPerfomerEffective(); input.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); myInstanceVal.setValidationSupport(myValidationSupport); @@ -962,7 +999,11 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc Patient p = new Patient(); p.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); - p.addIdentifier().setSystem("http://example.com/").setValue("12345").getType().addCoding().setSystem("http://example.com/foo/bar").setCode("bar"); + p.addIdentifier().setSystem("http://example.com/").setValue("12345").getType() + .addCoding().setSystem("http://example.com/foo/bar").setCode("bar"); + + ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p)); + ValidationResult output = myVal.validateWithResult(p); List all = logResultsAndReturnAll(output); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java index c1948cec35f..ebd1679278b 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/QuestionnaireResponseValidatorR5Test.java @@ -267,7 +267,7 @@ public class QuestionnaireResponseValidatorR5Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString()).contains("Unknown code for 'http://codesystems.com/system#code1'"); + assertThat(errors.toString()).contains("Unknown code (for 'http://codesystems.com/system#code1')"); assertThat(errors.toString()).contains("QuestionnaireResponse.item[0].answer[0]"); qa = new QuestionnaireResponse(); @@ -278,7 +278,7 @@ public class QuestionnaireResponseValidatorR5Test { errors = myVal.validateWithResult(qa); errors = stripBindingHasNoSourceMessage(errors); ourLog.info(errors.toString()); - assertThat(errors.toString()).contains("Unknown code 'http://codesystems.com/system2#code3' for 'http://codesystems.com/system2#code3'"); + assertThat(errors.toString()).contains("Unknown code 'http://codesystems.com/system2#code3'"); assertThat(errors.toString()).contains("QuestionnaireResponse.item[0].answer[0]"); } @@ -327,10 +327,12 @@ public class QuestionnaireResponseValidatorR5Test { public void testMissingRequiredQuestion() { Questionnaire q = new Questionnaire(); + q.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(org.hl7.fhir.r5.model.Narrative.NarrativeStatus.GENERATED); q.addItem().setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.STRING); q.addItem().setLinkId("link1").setRequired(true).setType(QuestionnaireItemType.STRING); QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(org.hl7.fhir.r5.model.Narrative.NarrativeStatus.GENERATED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1"); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); @@ -622,9 +624,11 @@ public class QuestionnaireResponseValidatorR5Test { @Test public void testUnexpectedAnswer() { Questionnaire q = new Questionnaire(); + q.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(org.hl7.fhir.r5.model.Narrative.NarrativeStatus.GENERATED); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.BOOLEAN); QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(org.hl7.fhir.r5.model.Narrative.NarrativeStatus.GENERATED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1"); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); @@ -640,9 +644,11 @@ public class QuestionnaireResponseValidatorR5Test { @Test public void testUnexpectedGroup() { Questionnaire q = new Questionnaire(); + q.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(org.hl7.fhir.r5.model.Narrative.NarrativeStatus.GENERATED); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.BOOLEAN); QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(org.hl7.fhir.r5.model.Narrative.NarrativeStatus.GENERATED); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); qa.getQuestionnaireElement().setValue("http://example.com/Questionnaire/q1"); qa.addItem().setLinkId("link1").addItem().setLinkId("link2"); diff --git a/hapi-fhir-validation/src/test/resources/bug703.json b/hapi-fhir-validation/src/test/resources/bug703.json index 6495a9fb5cf..6c43d99147b 100644 --- a/hapi-fhir-validation/src/test/resources/bug703.json +++ b/hapi-fhir-validation/src/test/resources/bug703.json @@ -26,7 +26,7 @@ { "system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "316663", - "display": "Viagra" + "display": "sildenafil 100 MG" } ] }, diff --git a/hapi-fhir-validation/src/test/resources/dstu3/valueset-doc-typecodes.json b/hapi-fhir-validation/src/test/resources/dstu3/valueset-doc-typecodes.json new file mode 100644 index 00000000000..c0f25642630 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/dstu3/valueset-doc-typecodes.json @@ -0,0 +1,67 @@ +{ + "resourceType": "ValueSet", + "id": "doc-typecodes", + "meta": { + "lastUpdated": "2019-10-24T11:53:00+11:00", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/shareablevalueset" + ] + }, + "text": { + "status": "generated", + "div": "

FHIR Document Type Codes

FHIR Document Codes - all LOINC codes where scale type = 'DOC'.

\n

Copyright Statement: This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use.

This value set includes codes from the following code systems:

" + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-ballot-status", + "valueString": "Informative" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm", + "valueInteger": 2 + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/structuredefinition-wg", + "valueCode": "sd" + } + ], + "url": "http://hl7.org/fhir/ValueSet/doc-typecodes", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "urn:oid:2.16.840.1.113883.4.642.3.235" + } + ], + "version": "3.0.2", + "name": "FHIR Document Type Codes", + "status": "draft", + "experimental": true, + "date": "2019-10-24T11:53:00+11:00", + "publisher": "HL7", + "contact": [ + { + "telecom": [ + { + "system": "url", + "value": "http://hl7.org" + } + ] + } + ], + "description": "FHIR Document Codes - all LOINC codes where scale type = 'DOC'.", + "copyright": "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use.", + "compose": { + "include": [ + { + "system": "http://loinc.org", + "filter": [ + { + "property": "SCALE_TYP", + "op": "=", + "value": "Doc" + } + ] + } + ] + } +} diff --git a/hapi-fhir-validation/src/test/resources/dstu3/valueset-v2-0131.json b/hapi-fhir-validation/src/test/resources/dstu3/valueset-v2-0131.json new file mode 100644 index 00000000000..8b3987f943c --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/dstu3/valueset-v2-0131.json @@ -0,0 +1,38 @@ +{ + "resourceType" : "ValueSet", + "id" : "v2-0131", + "text" : { + "status" : "generated", + "div" : "" + }, + "url" : "http://terminology.hl7.org/ValueSet/v2-0131", + "identifier" : [{ + "system" : "urn:ietf:rfc:3986", + "value" : "urn:oid:2.16.840.1.113883.21.69" + }], + "version" : "2.0.0", + "name" : "Hl7VSContactRole2", + "title" : "hl7VS-contactRole2", + "status" : "active", + "experimental" : false, + "date" : "2019-12-01", + "publisher" : "Health Level Seven International", + "contact" : [{ + "telecom" : [{ + "system" : "url", + "value" : "http://hl7.org" + }, + { + "system" : "email", + "value" : "hq@HL7.org" + }] + }], + "description" : "Concepts which specify a relationship role that the next of kin/associated parties plays with regard to the patient. Built on the updated code system. Also used in referrals, for example, it may be necessary to identify the contact representative at the clinic that sent a referral.", + "copyright" : "This material derives from the HL7 Terminology (THO). THO is copyright ©1989+ Health Level Seven International and is made available under the CC0 designation. For more licensing information see: https://terminology.hl7.org/license", + "compose" : { + "include" : [{ + "system" : "http://terminology.hl7.org/CodeSystem/v2-0131", + "version" : "2.0.0" + }] + } +} diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json index bcd0bf718c3..eb7f7caa95d 100644 --- a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json +++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json @@ -1,5 +1,9 @@ { "resourceType": "Patient", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "name": [ { "family": "Doe", diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json index 6423ce7a84f..3bd5e1d71e0 100644 --- a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json +++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json @@ -1,5 +1,9 @@ { "resourceType": "Patient", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "name": [ { "family": "Doe", diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json index 65526ac8421..95acfef90cd 100644 --- a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json +++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json @@ -1,5 +1,9 @@ { "resourceType": "Patient", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "name": [ { "family": "Doe", diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json index c169a6c379f..3769991b609 100644 --- a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json +++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json @@ -1,5 +1,9 @@ { "resourceType": "Patient", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "name": [ { "family": "Doe", diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json index 86fc6f27920..c7c03bffd50 100644 --- a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json +++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json @@ -1,5 +1,9 @@ { "resourceType": "Patient", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "name": [ { "family": "Doe", diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json index c5ed811442e..de66f12d695 100644 --- a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json +++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json @@ -1,5 +1,9 @@ { "resourceType": "Patient", + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "name": [ { "family": "Doe", diff --git a/hapi-fhir-validation/src/test/resources/r4/diagnosticreport-example-gingival-mass.json b/hapi-fhir-validation/src/test/resources/r4/diagnosticreport-example-gingival-mass.json index e42df2f0c30..59340d96a8d 100644 --- a/hapi-fhir-validation/src/test/resources/r4/diagnosticreport-example-gingival-mass.json +++ b/hapi-fhir-validation/src/test/resources/r4/diagnosticreport-example-gingival-mass.json @@ -27,7 +27,7 @@ { "system": "http://loinc.org", "code": "1-8", - "display": "Biopsy without Microscopic Description (1 Site/Lesion)-Standard" + "display": "Acyclovir [Susceptibility]" } ], "text": "Biopsy without Microscopic Description (1 Site/Lesion)-Standard" diff --git a/hapi-fhir-validation/src/test/resources/r4/observation-with-body-temp-ucum.json b/hapi-fhir-validation/src/test/resources/r4/observation-with-body-temp-ucum.json index 2f6e9883305..8326ca75687 100644 --- a/hapi-fhir-validation/src/test/resources/r4/observation-with-body-temp-ucum.json +++ b/hapi-fhir-validation/src/test/resources/r4/observation-with-body-temp-ucum.json @@ -6,6 +6,10 @@ "http://hl7.org/fhir/StructureDefinition/bodytemp" ] }, + "text": { + "status" : "generated", + "div" : "
Dummy text
" + }, "status": "final", "category": [ { @@ -28,6 +32,10 @@ "subject": { "reference": "Patient/1" }, + "performer" : [{ + "reference" : "Practitioner/2", + "display" : "A. Langeveld" + }], "effectiveDateTime": "2020-04-30T12:00:00+01:00", "valueQuantity": { "value": 37.5, diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 832ff9e1c85..89fa7ddfc55 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 62350c319a8..692b140b658 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index dab1037b741..5cef31514ed 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. @@ -926,7 +926,7 @@ - 6.1.2.2 + 6.3.11 2.41.1 -Dfile.encoding=UTF-8 -Xmx2048m diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index e2e30018cea..28d6a43340e 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index 50bd90f1550..ce6c398b455 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 96e2b428d85..06390132353 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.4-SNAPSHOT + 7.3.5-SNAPSHOT ../../pom.xml