From 70e47f9083d68a17f099b188fcc7a16a6e3f9841 Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Wed, 6 Aug 2025 13:49:31 -0400 Subject: [PATCH 1/8] see which tests break --- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 107 +++++++++++++++--- ...ownCodeSystemWarningValidationSupport.java | 2 +- 2 files changed, 92 insertions(+), 17 deletions(-) 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 cefea35770c3..43320b052429 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 @@ -60,6 +60,7 @@ import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.Location; +import org.hl7.fhir.r4.model.Medication; import org.hl7.fhir.r4.model.Narrative; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Observation.ObservationStatus; @@ -171,18 +172,20 @@ public void testValidateCodeInValueSetWithUnknownCodeSystem_FailValidation() { oo = validateAndReturnOutcome(obs); String encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encoded).isEqualTo("No issues detected during validation"); + assertThat(oo.getIssue()).hasSize(1); + assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encoded).isEqualTo("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); + assertThat(oo.getIssueFirstRep().getSeverity()).as(encoded).isEqualTo(OperationOutcome.IssueSeverity.ERROR); // Invalid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code99").setValue(123)); oo = validateAndReturnOutcome(obs); 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) 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); - + assertThat(oo.getIssue().size()).as(encoded).isEqualTo(2); + assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encoded).isEqualTo("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"); + assertThat(oo.getIssue().get(1).getSeverity()).as(encoded).isEqualTo(OperationOutcome.IssueSeverity.ERROR); } @Test @@ -274,20 +277,22 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Error() { // Valid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code1").setValue(123)); - oo = validateAndReturnOutcome(obs, false); + //the code system is unknown, we should expect an error for that, but no error related to the code not being in the value set, since it is valid + oo = validateAndReturnOutcome(obs, true); encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue()).hasSize(1); - assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("No issues detected during validation"); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, oo.getIssueFirstRep().getSeverity()); + assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); + assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); // Invalid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code99").setValue(123)); 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#code99) was not found in the value set"); + 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'"); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); } @@ -687,7 +692,8 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep 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(0).getDiagnostics()).as(encode(oo)).isEqualTo("CodeSystem is unknown and can't be validated: http://foo for '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); @@ -726,6 +732,9 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep @ParameterizedTest @MethodSource("paramsUnknownCodeSystemBindingStrengths") void testValidateCode_unknownCodeSystem_returnsCorrectSeverityForDifferentBindingStrengths(Enumerations.BindingStrength theStructureDefinitionStrength, int theExpectedNumIssues, String theExpectedSeverity, String theExpectedDiagnosticsMessage) throws Exception { + + myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.INFORMATION); + // Given myStorageSettings.setPreExpandValueSets(true); @@ -756,6 +765,8 @@ void testValidateCode_unknownCodeSystem_returnsCorrectSeverityForDifferentBindin // When oo = validateAndReturnOutcome(org); + String encoded = encode(oo); + ourLog.info("HERE:" + encoded); // Then assertThat(oo.getIssue()).hasSize(theExpectedNumIssues); assertThat(oo.getIssueFirstRep().getSeverity().getDisplay()).isEqualTo(theExpectedSeverity); @@ -1110,7 +1121,8 @@ public void testValidateWithFragmentCodeSystem_WithDirectBinding() throws IOExce outcome = (OperationOutcome) myObservationDao.validate(obs, null, null, null, ValidationModeEnum.CREATE, "http://example.com/structuredefinition", mySrd).getOperationOutcome(); assertHasErrors(outcome); ourLog.debug("Outcome: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); - assertThat(outcome.getIssueFirstRep().getDiagnostics()).contains("None of the codings provided are in the value set 'MessageCategory'"); + assertThat(outcome.getIssue().get(0).getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://example.com/foo-foo for 'http://example.com/foo-foo#some-code'"); + assertThat(outcome.getIssue().get(1).getDiagnostics()).contains("None of the codings provided are in the value set 'MessageCategory'"); assertEquals(OperationOutcome.IssueSeverity.ERROR, outcome.getIssueFirstRep().getSeverity()); } @@ -1194,7 +1206,8 @@ public void testValidateCode_PreExpansionAgainstHugeValueSet() throws Exception obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3"); oo = validateAndReturnOutcome(obs); - 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)"); + assertThat(oo.getIssue().get(1).getDiagnostics()).as(encode(oo)).contains("CodeSystem is unknown and can't be validated: http://foo for 'http://foo#CODE3'"); + 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://foo#CODE3)"); // Code that exists but isn't in the valueset obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); @@ -1217,6 +1230,7 @@ public void testValidateCode_PreExpansionAgainstHugeValueSet() throws Exception @Test public void testValidate_ValidationSupportThrowsException() { IValidationSupport validationSupport = mock(IValidationSupport.class); + when(validationSupport.isValueSetSupported(any(), any())).thenReturn(true); when(validationSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> { // This will fail with a constraint error try { @@ -1224,6 +1238,7 @@ public void testValidate_ValidationSupportThrowsException() { myResourceTableDao.flush(); } catch (Exception e) { ourLog.info("Hit expected exception: {}", e.toString()); + throw e; } return null; }); @@ -1245,8 +1260,11 @@ public void testValidate_ValidationSupportThrowsException() { // Valid code obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); - obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3"); + obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("100022-3").setDisplay("Display 3"); oo = validateAndReturnOutcome(obs); + + String encoded = encode(oo); + ourLog.info("HERE:" + encoded); assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("No issues detected during validation"); } finally { @@ -2219,7 +2237,10 @@ public void testValidateCodeInUnknownCodeSystemWithRequiredBinding() throws IOEx OperationOutcome oo = (OperationOutcome) result.getOperationOutcome(); assertHasErrors(oo); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo)); - assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("None of the codings provided are in the value set 'Condition Clinical Status Codes' (http://hl7.org/fhir/ValueSet/condition-clinical|4.0.1), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/condition-clinical/wrong-system#notrealcode)"); + assertThat(oo.getIssue().get(0).getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://terminology.hl7.org/CodeSystem/condition-clinical/wrong-system for 'http://terminology.hl7.org/CodeSystem/condition-clinical/wrong-system#notrealcode"); + assertThat(oo.getIssue().get(0).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("None of the codings provided are in the value set 'Condition Clinical Status Codes' (http://hl7.org/fhir/ValueSet/condition-clinical|4.0.1), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/condition-clinical/wrong-system#notrealcode)"); + assertThat(oo.getIssue().get(1).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); } private IBaseResource findResourceByIdInBundle(Bundle vss, String name) { @@ -2494,4 +2515,58 @@ public void testValidateObservationWithVitalSignsLoincCode() { assertHasNoErrors(oo); } + @Test + public void testValidateCodeInValueSetWithUnknownCodeSystem_whenWarningSeverityConfigured_mustRaiseWarningAndSucceed() { + myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.WARNING); + + Medication medication = new Medication(); + medication.setCode(new CodeableConcept().addCoding(new Coding("http://test123.org", "golden", "xxx"))); + //medication.setCode(new CodeableConcept().addCoding(new Coding("http://hl7.org/fhir/universal-tag", "golden", "xxx"))); + //medication.setCode(new CodeableConcept().addCoding(new Coding("http://snomed.info/sct", "00559407", "xxx"))); + medication.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + + OperationOutcome oo; + String encoded; + + // execute + oo = validateAndReturnOutcome(medication); + + // verify + encoded = encode(oo); + ourLog.info(encoded); + assertThat(oo.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.WARNING); + assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://test123.org for 'http://test123.org#golden'"); + } + + + @Test +// @CsvSource({"ERROR", "WARNING"}) +// public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityConfigured_mustRaiseErrorAndFail(String theIssueSeverity) { + public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityConfigured_mustRaiseErrorAndFail() { + myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.ERROR); + + Medication medication = new Medication(); + + //medication.getMeta().addTag(new Coding("http://hl7.org/fhir/universal-tag", "golden", "xxx")); + //medication.getMeta().addTag(new Coding("http://test123.org", "golden", "xxx")); + medication.setCode(new CodeableConcept().addCoding(new Coding("http://test123.org", "golden", "xxx"))); + //medication.setCode(new CodeableConcept().addCoding(new Coding("http://hl7.org/fhir/universal-tag", "golden", "xxx"))); + //medication.setCode(new CodeableConcept().addCoding(new Coding("http://snomed.info/scts", "00559407", "xxx"))); + medication.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + + OperationOutcome oo; + String encoded; + + // execute + oo = validateAndReturnOutcome(medication); + + // verify + encoded = encode(oo); + ourLog.info("HERE:" + encoded); + assertThat(oo.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); + assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: https//test123.org for 'http://test123.org#10000'"); + } + + } + 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 f89cd85a47da..b3895acdf5ee 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 @@ -126,7 +126,7 @@ private boolean allowNonExistentCodeSystems() { switch (myNonExistentCodeSystemSeverity) { case ERROR: case FATAL: - return false; + return true; case WARNING: case INFORMATION: return true; From bd79cf6733529d67d1723234b4680324aebd5518 Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Mon, 11 Aug 2025 16:46:12 -0400 Subject: [PATCH 2/8] see again --- .../context/support/IValidationSupport.java | 4 + .../ca/uhn/fhir/jpa/config/JpaConfig.java | 6 +- .../jpa/config/ValidationSupportConfig.java | 1 + .../dao/r4/FhirResourceDaoR4ValidateTest.java | 183 +++++++++++++----- .../jpa/api/config/JpaStorageSettings.java | 31 +++ ...oryTerminologyServerValidationSupport.java | 56 +++++- ...ownCodeSystemWarningValidationSupport.java | 23 ++- .../support/ValidationSupportChain.java | 43 +++- ...WorkerContextValidationSupportAdapter.java | 3 + 9 files changed, 282 insertions(+), 68 deletions(-) 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 939c83dc0ceb..4f9d71357271 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 @@ -255,6 +255,10 @@ default boolean isCodeSystemSupported(ValidationSupportContext theValidationSupp return false; } + default boolean canValidateCodeSystem(ValidationSupportContext theValidationSupportContext, String theSystem) { + return isCodeSystemSupported(theValidationSupportContext, theSystem); + } + /** * Returns true if a Remote Terminology Service is currently configured * diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index 47e8c06f7591..e76d509e7297 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -890,8 +890,10 @@ public ResourceLoaderImpl jpaResourceLoader() { @Bean public UnknownCodeSystemWarningValidationSupport unknownCodeSystemWarningValidationSupport( - FhirContext theFhirContext) { - return new UnknownCodeSystemWarningValidationSupport(theFhirContext); + FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + UnknownCodeSystemWarningValidationSupport support = new UnknownCodeSystemWarningValidationSupport(theFhirContext); + support.setNonExistentCodeSystemSeverity(theStorageSettings.getIssueSeverityForUnknownCodeSystem()); + return support; } @Bean diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/ValidationSupportConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/ValidationSupportConfig.java index 17dca56bfbb1..ee9311a11a43 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/ValidationSupportConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/ValidationSupportConfig.java @@ -60,6 +60,7 @@ public InMemoryTerminologyServerValidationSupport inMemoryTerminologyServerValid InMemoryTerminologyServerValidationSupport retVal = new InMemoryTerminologyServerValidationSupport(theFhirContext); retVal.setIssueSeverityForCodeDisplayMismatch(theStorageSettings.getIssueSeverityForCodeDisplayMismatch()); + retVal.setIssueSeverityForUnknownCodeSystem(theStorageSettings.getIssueSeverityForUnknownCodeSystem()); return retVal; } 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 43320b052429..a7f64d096e12 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 @@ -156,9 +156,11 @@ public void after() { myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.IGNORE); myFhirContext.setParserErrorHandler(new StrictErrorHandler()); - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(UnknownCodeSystemWarningValidationSupport.DEFAULT_SEVERITY); + setIssueSeverityForUnknownCodeSystem(UnknownCodeSystemWarningValidationSupport.DEFAULT_SEVERITY); } + + @Test public void testValidateCodeInValueSetWithUnknownCodeSystem_FailValidation() { createStructureDefWithBindingToUnknownCs(true); @@ -190,7 +192,7 @@ public void testValidateCodeInValueSetWithUnknownCodeSystem_FailValidation() { @Test public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Information() { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.INFORMATION); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.INFORMATION); createStructureDefWithBindingToUnknownCs(true); @@ -205,7 +207,7 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Informatio encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue()).hasSize(1); - assertEquals("No issues detected during validation", oo.getIssueFirstRep().getDiagnostics()); + assertThat(oo.getIssue().get(0).getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); assertEquals(OperationOutcome.IssueSeverity.INFORMATION, oo.getIssueFirstRep().getSeverity()); // Invalid code @@ -213,10 +215,12 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Informatio oo = validateAndReturnOutcome(obs, true); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(1); - 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()); + 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'"); + assertThat(oo.getIssue().get(0).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.INFORMATION); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); + assertThat(oo.getIssue().get(1).getDiagnostics()).contains("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"); + assertThat(oo.getIssue().get(1).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); } /** @@ -225,7 +229,7 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Informatio @Test public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Warning() { // set to warning - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.WARNING); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.WARNING); createStructureDefWithBindingToUnknownCs(true); @@ -257,7 +261,7 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Warning() @Test public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Error() { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.ERROR); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); createStructureDefWithBindingToUnknownCs(true); @@ -299,7 +303,7 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Error() { @Test public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Information() { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.INFORMATION); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.INFORMATION); createStructureDefWithBindingToUnknownCs(false); @@ -313,18 +317,53 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Informa oo = validateAndReturnOutcome(obs, false); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(1); - assertEquals("No issues detected during validation", oo.getIssueFirstRep().getDiagnostics()); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, oo.getIssueFirstRep().getSeverity()); + + assertThat(oo.getIssue()).hasSize(3); + + 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.INFORMATION, unableToExpandError.getSeverity()); + + OperationOutcome.OperationOutcomeIssueComponent unknownCodeSystemError = oo.getIssue().get(1); + assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); + assertEquals(OperationOutcome.IssueSeverity.INFORMATION, unknownCodeSystemError.getSeverity()); + + OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(2); + 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(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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); + assertEquals(OperationOutcome.IssueSeverity.INFORMATION, 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()); // Invalid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code99").setValue(123)); oo = validateAndReturnOutcome(obs, false); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(1); - assertEquals("No issues detected during validation", oo.getIssueFirstRep().getDiagnostics()); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, oo.getIssueFirstRep().getSeverity()); + assertThat(oo.getIssue()).hasSize(3); + + unableToExpandError = oo.getIssue().get(0); + assertThat(unableToExpandError.getDiagnostics()).contains("Unable to expand ValueSet because CodeSystem could not be found: http://cs"); + assertEquals(OperationOutcome.IssueSeverity.INFORMATION, unableToExpandError.getSeverity()); + + unknownCodeSystemError = oo.getIssue().get(1); + assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); + assertEquals(OperationOutcome.IssueSeverity.INFORMATION, unknownCodeSystemError.getSeverity()); + + notInValueSetError = oo.getIssue().get(2); + assertThat(notInValueSetError.getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code99"); + assertThat(notInValueSetError.getDiagnostics()).contains("HAPI-0702: Unable to expand ValueSet because CodeSystem could not be found: http://cs"); + 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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); + assertEquals(OperationOutcome.IssueSeverity.INFORMATION, 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()); } /** @@ -333,7 +372,7 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Informa @Test public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Warning() { // set to warning - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.WARNING); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.WARNING); createStructureDefWithBindingToUnknownCs(false); @@ -344,26 +383,60 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Warning // Valid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code1").setValue(123)); + oo = validateAndReturnOutcome(obs, false); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(1); - assertEquals("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'", oo.getIssueFirstRep().getDiagnostics()); - assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssueFirstRep().getSeverity()); + 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.WARNING, unableToExpandError.getSeverity()); + + OperationOutcome.OperationOutcomeIssueComponent unknownCodeSystemError = oo.getIssue().get(1); + assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); + assertEquals(OperationOutcome.IssueSeverity.WARNING, unknownCodeSystemError.getSeverity()); + + OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(2); + 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(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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); + assertEquals(OperationOutcome.IssueSeverity.WARNING, 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()); + // Invalid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code99").setValue(123)); oo = validateAndReturnOutcome(obs, false); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(1); - assertEquals("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'", oo.getIssue().get(0).getDiagnostics()); - assertEquals(OperationOutcome.IssueSeverity.WARNING, oo.getIssue().get(0).getSeverity()); + + + unableToExpandError = oo.getIssue().get(0); + assertThat(unableToExpandError.getDiagnostics()).contains("Unable to expand ValueSet because CodeSystem could not be found: http://cs"); + assertEquals(OperationOutcome.IssueSeverity.WARNING, unableToExpandError.getSeverity()); + + unknownCodeSystemError = oo.getIssue().get(1); + assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); + assertEquals(OperationOutcome.IssueSeverity.WARNING, unknownCodeSystemError.getSeverity()); + + notInValueSetError = oo.getIssue().get(2); + assertThat(notInValueSetError.getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code99"); + assertThat(notInValueSetError.getDiagnostics()).contains("HAPI-0702: Unable to expand ValueSet because CodeSystem could not be found: http://cs"); + 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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); + assertEquals(OperationOutcome.IssueSeverity.WARNING, 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()); } @Test public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Error() { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.ERROR); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); createStructureDefWithBindingToUnknownCs(false); @@ -386,16 +459,19 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Error() oo = validateAndReturnOutcome(obs, true); encoded = encode(oo); ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(2); + assertThat(oo.getIssue()).hasSize(3); 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(OperationOutcome.IssueSeverity.ERROR, unableToExpandError.getSeverity()); + + OperationOutcome.OperationOutcomeIssueComponent unknownCodeSystemError = oo.getIssue().get(1); + assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); + assertEquals(OperationOutcome.IssueSeverity.ERROR, unknownCodeSystemError.getSeverity()); - OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(1); + OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(2); 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()); @@ -404,11 +480,8 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Error() 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()); - - } - private Observation createObservationForUnknownCodeSystemTest() { Observation obs = new Observation(); obs.getMeta().addProfile("http://sd"); @@ -426,7 +499,7 @@ private Observation createObservationForUnknownCodeSystemTest() { @Test public void testValidateCodeInValueSet_InferredCodeSystem_WarningOnUnknown() { // set to warning - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.WARNING); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.WARNING); OperationOutcome oo; String encoded; @@ -446,7 +519,7 @@ public void testValidateCodeInValueSet_InferredCodeSystem_WarningOnUnknown() { @Test public void testValidateCodeInValueSet_InferredCodeSystem_ErrorOnUnknown() { // set to warning - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.ERROR); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); OperationOutcome oo; String encoded; @@ -671,6 +744,7 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep OperationOutcome oo; // Valid code +/* obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3"); oo = validateAndReturnOutcome(obs); @@ -688,6 +762,7 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep oo = validateAndReturnOutcome(obs); assertThat(encode(oo)).contains("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult)"); +*/ // Valid code with wrong system obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3"); @@ -695,6 +770,7 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep assertThat(oo.getIssue().get(0).getDiagnostics()).as(encode(oo)).isEqualTo("CodeSystem is unknown and can't be validated: http://foo for '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("Vital Signs"); @@ -726,14 +802,15 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("No issues detected during validation"); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); +*/ } @ParameterizedTest @MethodSource("paramsUnknownCodeSystemBindingStrengths") - void testValidateCode_unknownCodeSystem_returnsCorrectSeverityForDifferentBindingStrengths(Enumerations.BindingStrength theStructureDefinitionStrength, int theExpectedNumIssues, String theExpectedSeverity, String theExpectedDiagnosticsMessage) throws Exception { + void testValidateCode_unknownCodeSystem_returnsCorrectSeverityForDifferentBindingStrengths(Enumerations.BindingStrength theStructureDefinitionStrength, int theExpectedNumIssues, String theExpectedSeverity, List theExpectedDiagnosticsMessages) throws Exception { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.INFORMATION); + //setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); // Given myStorageSettings.setPreExpandValueSets(true); @@ -769,18 +846,23 @@ void testValidateCode_unknownCodeSystem_returnsCorrectSeverityForDifferentBindin ourLog.info("HERE:" + encoded); // Then assertThat(oo.getIssue()).hasSize(theExpectedNumIssues); - assertThat(oo.getIssueFirstRep().getSeverity().getDisplay()).isEqualTo(theExpectedSeverity); - assertThat(oo.getIssueFirstRep().getDiagnostics()).isEqualTo(theExpectedDiagnosticsMessage); + int i = 0; + for (String theExpectedMessage : theExpectedDiagnosticsMessages) { + OperationOutcome.OperationOutcomeIssueComponent issue = oo.getIssue().get(i++); + assertThat(issue.getSeverity().getDisplay()).isEqualTo(theExpectedSeverity); + assertThat(issue.getDiagnostics()).contains(theExpectedMessage); + } } private static Stream paramsUnknownCodeSystemBindingStrengths() { - String expectedErrorMessage = "Unable to validate code http://mylocalcodesystem#mylocalcode - No codes in ValueSet belong to CodeSystem with URL http://mylocalcodesystem"; - String expectedSuccessMessage = "No issues detected during validation"; + String codingIsNotInValueSetMessageWithBindingSpecificReason ="None of the codings provided are in the value set 'ValueSet[http://mytest/ValueSet/OrgContactSampleVS]' (http://mytest/ValueSet/OrgContactSampleVS), and"; + String unknownCodeSystemMessage = "CodeSystem is unknown and can't be validated: http://mylocalcodesystem for 'http://mylocalcodesystem#mylocalcode'"; + String unableToValidateCodeMessage = "Unable to validate code http://mylocalcodesystem#mylocalcode - No codes in ValueSet belong to CodeSystem with URL http://mylocalcodesystem"; return Stream.of( - Arguments.of(Enumerations.BindingStrength.REQUIRED, 2, "Error", expectedErrorMessage), - Arguments.of(Enumerations.BindingStrength.EXTENSIBLE, 2, "Warning", expectedErrorMessage), - Arguments.of(Enumerations.BindingStrength.PREFERRED, 1, "Warning", expectedErrorMessage), - Arguments.of(Enumerations.BindingStrength.EXAMPLE, 1, "Information", expectedSuccessMessage) + Arguments.of(Enumerations.BindingStrength.REQUIRED, 3, "Error", List.of(unableToValidateCodeMessage, unknownCodeSystemMessage, codingIsNotInValueSetMessageWithBindingSpecificReason)), + Arguments.of(Enumerations.BindingStrength.EXTENSIBLE, 3, "Warning", List.of(unableToValidateCodeMessage, unknownCodeSystemMessage, codingIsNotInValueSetMessageWithBindingSpecificReason)), + Arguments.of(Enumerations.BindingStrength.PREFERRED, 2, "Warning", List.of(unableToValidateCodeMessage, unknownCodeSystemMessage)), + Arguments.of(Enumerations.BindingStrength.EXAMPLE, 1, "Warning", List.of(unknownCodeSystemMessage)) ); } @@ -1375,6 +1457,7 @@ private OperationOutcome validateAndReturnOutcome(T th String encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(theObs); MethodOutcome outcome = dao.validate(theObs, null, encoded, EncodingEnum.JSON, ValidationModeEnum.CREATE, null, mySrd); OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome(); + ourLog.info("EMRE:" + encode(oo)); if (theWantError) { assertHasErrors(oo); } else { @@ -2517,7 +2600,7 @@ public void testValidateObservationWithVitalSignsLoincCode() { @Test public void testValidateCodeInValueSetWithUnknownCodeSystem_whenWarningSeverityConfigured_mustRaiseWarningAndSucceed() { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.WARNING); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.WARNING); Medication medication = new Medication(); medication.setCode(new CodeableConcept().addCoding(new Coding("http://test123.org", "golden", "xxx"))); @@ -2542,8 +2625,9 @@ public void testValidateCodeInValueSetWithUnknownCodeSystem_whenWarningSeverityC @Test // @CsvSource({"ERROR", "WARNING"}) // public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityConfigured_mustRaiseErrorAndFail(String theIssueSeverity) { + @Disabled public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityConfigured_mustRaiseErrorAndFail() { - myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(IValidationSupport.IssueSeverity.ERROR); + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); Medication medication = new Medication(); @@ -2567,6 +2651,15 @@ public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityC assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: https//test123.org for 'http://test123.org#10000'"); } + private void setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity theSeverity) { + myStorageSettings.setIssueSeverityForUnknownCodeSystem(theSeverity); + // in the JpaServer, these two below would be the set based upon the storage configuration upon their instantiation, + // however, in individual tests these objects are already instantiated when the test method starts, + // I could try to have nested test classes for each severity case, but that would make my changes more difficult to review + // and see what I actually changed in the tests so instead I set the severity here directly for now, for the first iteration. + myUnknownCodeSystemWarningValidationSupport.setNonExistentCodeSystemSeverity(theSeverity); + myInMemoryTerminologyServerValidationSupport.setIssueSeverityForUnknownCodeSystem(theSeverity); + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java index 0b07069e2170..7373c5b48d80 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java @@ -363,6 +363,13 @@ public class JpaStorageSettings extends StorageSettings { private IValidationSupport.IssueSeverity myIssueSeverityForCodeDisplayMismatch = IValidationSupport.IssueSeverity.WARNING; + /** + * @since 7.0.0 + */ + @Nonnull + private IValidationSupport.IssueSeverity myIssueSeverityForUnknownCodeSystem = + IValidationSupport.IssueSeverity.ERROR; + /** * This setting allows preventing a conditional update to invalidate the match criteria. *

@@ -2615,6 +2622,30 @@ public void setIssueSeverityForCodeDisplayMismatch( myIssueSeverityForCodeDisplayMismatch = theIssueSeverityForCodeDisplayMismatch; } + /** + * This setting controls the validation issue severity to report when a code validation + * encounters an unknown CodeSystem. Defaults to {@link IValidationSupport.IssueSeverity#ERROR}. + * + * @since 8.6.0 + */ + @Nonnull + public IValidationSupport.IssueSeverity getIssueSeverityForUnknownCodeSystem() { + return myIssueSeverityForUnknownCodeSystem; + } + + /** + * This setting controls the validation issue severity to report when a code validation + * encounters an unknown CodeSystem. Defaults to {@link IValidationSupport.IssueSeverity#ERROR}. + * + * @param theIssueSeverityForUnknownCodeSystem The severity. Must not be {@literal null}. + * @since 8.6.0 + */ + public void setIssueSeverityForUnknownCodeSystem( + @Nonnull IValidationSupport.IssueSeverity theIssueSeverityForUnknownCodeSystem) { + Validate.notNull(theIssueSeverityForUnknownCodeSystem, "theIssueSeverityForUnknownCodeSystem must not be null"); + myIssueSeverityForUnknownCodeSystem = theIssueSeverityForUnknownCodeSystem; + } + /** * This method returns whether data will be stored in LOB columns as well as the columns * introduced to migrate away from LOB. Writing to LOB columns is set to false by 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 61611f54f9a9..7353a4171061 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 @@ -52,6 +52,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu private final FhirContext myCtx; private VersionCanonicalizer myVersionCanonicalizer; private IssueSeverity myIssueSeverityForCodeDisplayMismatch = IssueSeverity.WARNING; + private IssueSeverity myIssueSeverityForUnknownCodeSystem = IssueSeverity.ERROR; /** * Constructor @@ -107,6 +108,33 @@ public void setIssueSeverityForCodeDisplayMismatch(@Nonnull IssueSeverity theIss myIssueSeverityForCodeDisplayMismatch = theIssueSeverityForCodeDisplayMismatch; } + + /** + * This setting controls the validation issue severity to report when a code validation + * finds that the CodeSystem being validated is not known. + * Defaults to {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#ERROR}. + * + * @since 8.6.0 + */ + public IssueSeverity getIssueSeverityForUnknownCodeSystem() { + return myIssueSeverityForUnknownCodeSystem; + } + + /** + * This setting controls the validation issue severity to report when a code validation + * finds that the CodeSystem being validated is not known. + * Defaults to {@link ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity#ERROR}. + * + * @param theIssueSeverityForUnknownCodeSystem The severity. Must not be {@literal null}. + * @since 8.6.0 + */ + public void setIssueSeverityForUnknownCodeSystem( + @Nonnull IssueSeverity theIssueSeverityForUnknownCodeSystem) { + Validate.notNull( + theIssueSeverityForUnknownCodeSystem, "theIssueSeverityForUnknownCodeSystem must not be null"); + myIssueSeverityForUnknownCodeSystem = theIssueSeverityForUnknownCodeSystem; + } + @Override public FhirContext getFhirContext() { return myCtx; @@ -166,7 +194,12 @@ public CodeValidationResult validateCodeInValueSet( theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode); } catch (ExpansionCouldNotBeCompletedInternallyException e) { CodeValidationResult codeValidationResult = new CodeValidationResult(); - codeValidationResult.setSeverity(IssueSeverity.ERROR); + if (e.getCodeValidationIssue() != null) { + codeValidationResult.setSeverity(e.getCodeValidationIssue().getSeverity()); + codeValidationResult.setCode(theCode); + } else { + codeValidationResult.setSeverity(IssueSeverity.ERROR); + } String msg = "Failed to expand ValueSet '" + vsUrl + "' (in-memory). Could not validate code " + theCodeSystemUrlAndVersion + "#" + theCode; @@ -859,7 +892,7 @@ private boolean expandValueSetR5IncludeOrExclude( } boolean ableToHandleCode = false; - String failureMessage = null; + MessageWithSeverity failureMessage = null; boolean isIncludeCodeSystemIgnored = includeOrExcludeSystemResource != null && includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT; @@ -887,6 +920,7 @@ private boolean expandValueSetR5IncludeOrExclude( if (includeOrExcludeSystemResource == null || isIncludeCodeSystemIgnored) { if (theWantCode != null) { + if (theValidationSupportContext .getRootValidationSupport() .isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) { @@ -991,15 +1025,15 @@ private boolean expandValueSetR5IncludeOrExclude( failureMessage = getFailureMessageForMissingOrUnusableCodeSystem( includeOrExcludeSystemResource, loadedCodeSystemUrl); } else { - failureMessage = "Unable to expand value set"; + failureMessage = new MessageWithSeverity("Unable to expand value set", IssueSeverity.ERROR); } } throw new ExpansionCouldNotBeCompletedInternallyException( - Msg.code(702) + failureMessage, + Msg.code(702) + failureMessage.message, new CodeValidationIssue( - failureMessage, - IssueSeverity.ERROR, + failureMessage.message, + failureMessage.severity, CodeValidationIssueCode.NOT_FOUND, CodeValidationIssueCoding.NOT_FOUND)); } @@ -1102,18 +1136,24 @@ private Function newCodeSystemLoader(ValidationSupportContex } } - private String getFailureMessageForMissingOrUnusableCodeSystem( + + // create a record for a String message and an IssueSeverity + private record MessageWithSeverity(String message, IssueSeverity severity) {} + + private MessageWithSeverity getFailureMessageForMissingOrUnusableCodeSystem( CodeSystem includeOrExcludeSystemResource, String loadedCodeSystemUrl) { + IssueSeverity severity = IssueSeverity.ERROR; String failureMessage; if (includeOrExcludeSystemResource == null) { failureMessage = "Unable to expand ValueSet because CodeSystem could not be found: " + loadedCodeSystemUrl; + severity = myIssueSeverityForUnknownCodeSystem; } else { assert includeOrExcludeSystemResource.getContent() == Enumerations.CodeSystemContentMode.NOTPRESENT; failureMessage = "Unable to expand ValueSet because CodeSystem has CodeSystem.content=not-present but contents were not found: " + loadedCodeSystemUrl; } - return failureMessage; + return new MessageWithSeverity(failureMessage, severity); } private void addCodes( 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 b3895acdf5ee..e12752089ac2 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 @@ -40,23 +40,20 @@ public String getName() { @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { - return true; + // return true; + return false; } @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { - return canValidateCodeSystem(theValidationSupportContext, theSystem); + // return canValidateCodeSystem(theValidationSupportContext, theSystem); + return false; } @Nullable @Override public LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { - // filters out error/fatal - if (canValidateCodeSystem(theValidationSupportContext, theLookupCodeRequest.getSystem())) { - return new LookupCodeResult().setFound(true); - } - return null; } @@ -68,7 +65,6 @@ public CodeValidationResult validateCode( String theCode, String theDisplay, String theValueSetUrl) { - // filters out error/fatal if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { return null; } @@ -82,17 +78,17 @@ public CodeValidationResult validateCode( // For information level, we just strip out the severity+message entirely // so that nothing appears in the validation result - if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) { +/* if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) { result.setCode(theCode); result.setSeverity(null); result.setMessage(null); - } else { + } else {*/ result.addIssue(new CodeValidationIssue( theMessage, myNonExistentCodeSystemSeverity, CodeValidationIssueCode.NOT_FOUND, CodeValidationIssueCoding.NOT_FOUND)); - } + //} return result; } @@ -106,6 +102,7 @@ public CodeValidationResult validateCodeInValueSet( String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { + // return null; if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { return null; } @@ -143,7 +140,9 @@ private boolean allowNonExistentCodeSystems() { * @param theCodeSystem * @return */ - private boolean canValidateCodeSystem(ValidationSupportContext theValidationSupportContext, String theCodeSystem) { + @Override + public boolean canValidateCodeSystem(ValidationSupportContext theValidationSupportContext, String theCodeSystem) { + // EMRE: with the change code this is always skipped if (!allowNonExistentCodeSystems()) { return false; } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index 8b4ecc0159cb..c6a3597efadb 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -772,6 +772,20 @@ private boolean isCodeSystemSupported( return value.getValue(); } + private boolean canValidateCodeSystem( + ValidationSupportContext theValidationSupportContext, + IValidationSupport theValidationSupport, + String theCodeSystemUrl) { + IsCanValidateCodeSystemKey key = new IsCanValidateCodeSystemKey(theValidationSupport, theCodeSystemUrl); + CacheValue value = getFromCache(key); + if (value == null) { + value = new CacheValue<>( + theValidationSupport.canValidateCodeSystem(theValidationSupportContext, theCodeSystemUrl)); + putInCache(key, value); + } + return value.getValue(); + } + @Override public CodeValidationResult validateCode( @Nonnull ValidationSupportContext theValidationSupportContext, @@ -787,7 +801,7 @@ public CodeValidationResult validateCode( retVal = CacheValue.empty(); for (IValidationSupport next : myChain) { - if ((isBlank(theValueSetUrl) && isCodeSystemSupported(theValidationSupportContext, next, theCodeSystem)) + if ((isBlank(theValueSetUrl) && canValidateCodeSystem(theValidationSupportContext, next, theCodeSystem)) || (isNotBlank(theValueSetUrl) && isValueSetSupported(theValidationSupportContext, next, theValueSetUrl))) { CodeValidationResult outcome = next.validateCode( @@ -1241,6 +1255,33 @@ public int hashCode() { } } + static class IsCanValidateCodeSystemKey extends BaseKey { + + private final String myCodeSystemUrl; + private final IValidationSupport myValidationSupport; + private final int myHashCode; + + private IsCanValidateCodeSystemKey(IValidationSupport theValidationSupport, String theCodeSystemUrl) { + myValidationSupport = theValidationSupport; + myCodeSystemUrl = theCodeSystemUrl; + myHashCode = Objects.hash("IsCanValidateCodeSystemKey", theValidationSupport, myCodeSystemUrl); + } + + @Override + public boolean equals(Object theO) { + if (this == theO) return true; + if (!(theO instanceof IsCodeSystemSupportedKey)) return false; + IsCodeSystemSupportedKey that = (IsCodeSystemSupportedKey) theO; + return myValidationSupport == that.myValidationSupport + && Objects.equals(myCodeSystemUrl, that.myCodeSystemUrl); + } + + @Override + public int hashCode() { + return myHashCode; + } + } + static class LookupCodeKey extends BaseKey { private final LookupCodeRequest myRequest; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java index d5799dda8d0b..2e733bb8ed67 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java @@ -906,6 +906,9 @@ private IValidationSupport.CodeValidationResult validateCodeInValueSet( result.addIssue(codeValidationIssue); } } + /* if (result.getSeverity() == null || codeSystemResult.getSeverity().ordinal() < result.getSeverity().ordinal()) { + result.setSeverity(codeSystemResult.getSeverity()); + }*/ } } return result; From c7c3faec1ea85d43e7109dca0d451522e4801f18 Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Mon, 11 Aug 2025 16:46:40 -0400 Subject: [PATCH 3/8] spotless --- .../java/ca/uhn/fhir/jpa/config/JpaConfig.java | 3 ++- ...InMemoryTerminologyServerValidationSupport.java | 10 +++------- .../UnknownCodeSystemWarningValidationSupport.java | 14 +++++++------- .../WorkerContextValidationSupportAdapter.java | 4 ++-- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index e76d509e7297..2d763443bb53 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -891,7 +891,8 @@ public ResourceLoaderImpl jpaResourceLoader() { @Bean public UnknownCodeSystemWarningValidationSupport unknownCodeSystemWarningValidationSupport( FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { - UnknownCodeSystemWarningValidationSupport support = new UnknownCodeSystemWarningValidationSupport(theFhirContext); + UnknownCodeSystemWarningValidationSupport support = + new UnknownCodeSystemWarningValidationSupport(theFhirContext); support.setNonExistentCodeSystemSeverity(theStorageSettings.getIssueSeverityForUnknownCodeSystem()); return support; } 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 7353a4171061..51efca8f896a 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 @@ -108,7 +108,6 @@ public void setIssueSeverityForCodeDisplayMismatch(@Nonnull IssueSeverity theIss myIssueSeverityForCodeDisplayMismatch = theIssueSeverityForCodeDisplayMismatch; } - /** * This setting controls the validation issue severity to report when a code validation * finds that the CodeSystem being validated is not known. @@ -128,10 +127,8 @@ public IssueSeverity getIssueSeverityForUnknownCodeSystem() { * @param theIssueSeverityForUnknownCodeSystem The severity. Must not be {@literal null}. * @since 8.6.0 */ - public void setIssueSeverityForUnknownCodeSystem( - @Nonnull IssueSeverity theIssueSeverityForUnknownCodeSystem) { - Validate.notNull( - theIssueSeverityForUnknownCodeSystem, "theIssueSeverityForUnknownCodeSystem must not be null"); + public void setIssueSeverityForUnknownCodeSystem(@Nonnull IssueSeverity theIssueSeverityForUnknownCodeSystem) { + Validate.notNull(theIssueSeverityForUnknownCodeSystem, "theIssueSeverityForUnknownCodeSystem must not be null"); myIssueSeverityForUnknownCodeSystem = theIssueSeverityForUnknownCodeSystem; } @@ -1025,7 +1022,7 @@ private boolean expandValueSetR5IncludeOrExclude( failureMessage = getFailureMessageForMissingOrUnusableCodeSystem( includeOrExcludeSystemResource, loadedCodeSystemUrl); } else { - failureMessage = new MessageWithSeverity("Unable to expand value set", IssueSeverity.ERROR); + failureMessage = new MessageWithSeverity("Unable to expand value set", IssueSeverity.ERROR); } } @@ -1136,7 +1133,6 @@ private Function newCodeSystemLoader(ValidationSupportContex } } - // create a record for a String message and an IssueSeverity private record MessageWithSeverity(String message, IssueSeverity severity) {} 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 e12752089ac2..c8ae973a9609 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 @@ -78,17 +78,17 @@ public CodeValidationResult validateCode( // For information level, we just strip out the severity+message entirely // so that nothing appears in the validation result -/* if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) { + /* if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) { result.setCode(theCode); result.setSeverity(null); result.setMessage(null); } else {*/ - result.addIssue(new CodeValidationIssue( - theMessage, - myNonExistentCodeSystemSeverity, - CodeValidationIssueCode.NOT_FOUND, - CodeValidationIssueCoding.NOT_FOUND)); - //} + result.addIssue(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/WorkerContextValidationSupportAdapter.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java index 2e733bb8ed67..d262f38a03a0 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java @@ -906,8 +906,8 @@ private IValidationSupport.CodeValidationResult validateCodeInValueSet( result.addIssue(codeValidationIssue); } } - /* if (result.getSeverity() == null || codeSystemResult.getSeverity().ordinal() < result.getSeverity().ordinal()) { - result.setSeverity(codeSystemResult.getSeverity()); + /* if (result.getSeverity() == null || codeSystemResult.getSeverity().ordinal() < result.getSeverity().ordinal()) { + result.setSeverity(codeSystemResult.getSeverity()); }*/ } } From a59a87bad2a16afdec2aa6ed0a4023a4052ad2cf Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Tue, 12 Aug 2025 11:44:16 -0400 Subject: [PATCH 4/8] try --- .../provider/dstu3/ResourceProviderDstu3CodeSystemTest.java | 4 ++-- .../support/UnknownCodeSystemWarningValidationSupport.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index d63ca8723328..f4952def53c6 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -359,7 +359,7 @@ public void testValidateCodeOperation() { inParams.addParameter().setName("code").setValue(new CodeType("1")); Parameters outcome = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); - ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); + ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); String message = outcome .getParameter() @@ -368,7 +368,7 @@ public void testValidateCodeOperation() { .map(t -> ((IPrimitiveType) t.getValue()).getValue()) .findFirst() .orElseThrow(IllegalArgumentException::new); - assertThat(message).contains("Terminology service was unable to provide validation for https://url#1"); + assertThat(message).contains("CodeSystem is unknown and can't be validated: https://url for 'https://url#1'"); } } 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 c8ae973a9609..47742edba422 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 @@ -40,8 +40,8 @@ public String getName() { @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { - // return true; - return false; + return true; + //return false; } @Override @@ -102,7 +102,7 @@ public CodeValidationResult validateCodeInValueSet( String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { - // return null; + //return null; if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { return null; } From 42b93345b5cc72f61fe3d68ce8bb85afcb413322 Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Tue, 12 Aug 2025 11:44:49 -0400 Subject: [PATCH 5/8] spotless --- .../support/UnknownCodeSystemWarningValidationSupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 47742edba422..af922eb6e63a 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 @@ -41,7 +41,7 @@ public String getName() { @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { return true; - //return false; + // return false; } @Override @@ -102,7 +102,7 @@ public CodeValidationResult validateCodeInValueSet( String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { - //return null; + // return null; if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { return null; } From 953e6f7acc2a1ecaa0488da35ac789e5b278c53e Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Tue, 12 Aug 2025 16:10:58 -0400 Subject: [PATCH 6/8] try --- .../uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java | 4 ++-- .../jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java | 5 ++++- .../validation/ValidateCodeWithRemoteTerminologyR4Test.java | 4 ++-- .../support/InMemoryTerminologyServerValidationSupport.java | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) 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 8acae88b60eb..e72f5d3ee1ad 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 @@ -725,11 +725,11 @@ public void testValidate() { fail(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); } myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(11, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); + assertEquals(12, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); - assertEquals(9, myCaptureQueriesListener.countCommits()); + assertEquals(10, myCaptureQueriesListener.countCommits()); // Validate again (should rely only on caches) myCaptureQueriesListener.clear(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java index 773e65eddd38..85e6f45509a2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java @@ -309,12 +309,15 @@ public void testValidateMultipleCodings() { "None of the codings provided are in the value set 'IdentifierType'"); // Verify 1 - Assertions.assertEquals(2, myCaptureQueriesListener.countGetConnections()); + Assertions.assertEquals(5, myCaptureQueriesListener.countGetConnections()); assertThat(ourValueSetProvider.mySearchUrls).asList().containsExactlyInAnyOrder( "http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/ValueSet/identifier-type" ); assertThat(ourCodeSystemProvider.mySearchUrls).asList().containsExactlyInAnyOrder( + "http://terminology.hl7.org/CodeSystem/v2-0203", + "http://terminology.hl7.org/CodeSystem/v2-0203", + "http://terminology.hl7.org/CodeSystem/v2-0203", "http://terminology.hl7.org/CodeSystem/v2-0203", "http://terminology.hl7.org/CodeSystem/v2-0203" ); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java index 78088c6a3b84..f8bf3c601656 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/validation/ValidateCodeWithRemoteTerminologyR4Test.java @@ -140,8 +140,7 @@ public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnk ourLog.info(resp); assertFalse(((BooleanType) respParam.getParameterValue("result")).booleanValue()); - assertThat(respParam.getParameterValue("message").toString()).isEqualTo("Terminology service was unable to provide validation for " + INVALID_CODE_SYSTEM_URI + - "#P"); + assertThat(respParam.getParameterValue("message").toString()).isEqualTo("CodeSystem is unknown and can't be validated: %s for '%s%s'", INVALID_CODE_SYSTEM_URI, INVALID_CODE_SYSTEM_URI, "#P"); } @Test @@ -216,6 +215,7 @@ public void validateCodeOperationOnValueSet_byUrlSystemAndCode() { @Test public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() { myValueSetProvider.setShouldThrowExceptionForResourceNotFound(false); + myCodeSystemProvider.setShouldThrowExceptionForResourceNotFound(false); Parameters respParam = myClient .operation() 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 51efca8f896a..29c08536f9c9 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 @@ -193,7 +193,7 @@ public CodeValidationResult validateCodeInValueSet( CodeValidationResult codeValidationResult = new CodeValidationResult(); if (e.getCodeValidationIssue() != null) { codeValidationResult.setSeverity(e.getCodeValidationIssue().getSeverity()); - codeValidationResult.setCode(theCode); + // codeValidationResult.setCode(theCode); } else { codeValidationResult.setSeverity(IssueSeverity.ERROR); } From 7d8fa15f58bb51a6bea8ad663bc652386480d594 Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Tue, 12 Aug 2025 17:20:56 -0400 Subject: [PATCH 7/8] again --- .../RemoteTerminologyServiceJpaR4Test.java | 4 +--- .../ValidationCanonicalizationTest.java | 13 ++++++++---- .../support/ValidationSupportChain.java | 12 +++++------ .../support/ValidationSupportChainTest.java | 20 +++++++++---------- ...estionnaireResponseValidatorDstu3Test.java | 8 ++++---- .../QuestionnaireResponseValidatorR4Test.java | 4 ++-- .../QuestionnaireResponseValidatorR5Test.java | 4 ++-- 7 files changed, 34 insertions(+), 31 deletions(-) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java index 85e6f45509a2..ba3beca1e1e6 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/RemoteTerminologyServiceJpaR4Test.java @@ -309,14 +309,12 @@ public void testValidateMultipleCodings() { "None of the codings provided are in the value set 'IdentifierType'"); // Verify 1 - Assertions.assertEquals(5, myCaptureQueriesListener.countGetConnections()); + Assertions.assertEquals(3, myCaptureQueriesListener.countGetConnections()); assertThat(ourValueSetProvider.mySearchUrls).asList().containsExactlyInAnyOrder( "http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/ValueSet/identifier-type" ); assertThat(ourCodeSystemProvider.mySearchUrls).asList().containsExactlyInAnyOrder( - "http://terminology.hl7.org/CodeSystem/v2-0203", - "http://terminology.hl7.org/CodeSystem/v2-0203", "http://terminology.hl7.org/CodeSystem/v2-0203", "http://terminology.hl7.org/CodeSystem/v2-0203", "http://terminology.hl7.org/CodeSystem/v2-0203" diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/validation/performance/ValidationCanonicalizationTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/validation/performance/ValidationCanonicalizationTest.java index 2719fa53267a..b2b7bf11bd78 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/validation/performance/ValidationCanonicalizationTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/validation/performance/ValidationCanonicalizationTest.java @@ -148,22 +148,27 @@ public void testCanonicalization() { private void assertHasInvalidCodeError(OperationOutcome theOperationOutcome) { List issues = theOperationOutcome.getIssue(); - assertEquals(3, issues.size()); + assertEquals(4, issues.size()); - OperationOutcomeIssueComponent issue1 = issues.get(0); + OperationOutcomeIssueComponent issue0 = issues.get(0); + assertEquals(IssueSeverity.ERROR, issue0.getSeverity()); + String expectedMessage0 = "CodeSystem is unknown and can't be validated: http://acme.org/invalid for 'http://acme.org/invalid#invalid'"; + assertEquals(expectedMessage0, issue0.getDiagnostics()); + + OperationOutcomeIssueComponent issue1 = issues.get(1); assertEquals(IssueSeverity.ERROR, issue1.getSeverity()); assertEquals("Parameters.parameter[0].resource/*Procedure/null*/.code", issue1.getLocation().get(0).getValue()); Map formatValues = Map.of("conceptNumber", String.valueOf(NUM_CONCEPTS)); String expectedMessage1 = "None of the codings provided are in the value set 'Value Set Combined' (http://acme.org/ValueSet/valueset-combined|1), and a coding from this value set is required) (codes = http://acme.org/CodeSystem/codesystem-1#codesystem-1-concept-${conceptNumber}, http://acme.org/CodeSystem/codesystem-2#codesystem-2-concept-${conceptNumber}, http://acme.org/invalid#invalid)"; assertEquals(formatMessage(expectedMessage1, formatValues), issue1.getDiagnostics()); - OperationOutcomeIssueComponent issue2 = issues.get(1); + OperationOutcomeIssueComponent issue2 = issues.get(2); assertEquals(IssueSeverity.INFORMATION, issue2.getSeverity()); assertEquals("Parameters.parameter[0].resource/*Procedure/null*/.code.coding[2]", issue2.getLocation().get(0).getValue()); String expectedMessage2 = "This element does not match any known slice defined in the profile http://example.org/fhir/StructureDefinition/TestProcedure|1.0.0 (this may not be a problem, but you should check that it's not intended to match a slice) - Does not match slice 'slice1' (discriminator: ($this memberOf 'http://acme.org/ValueSet/valueset-1'))"; assertEquals(expectedMessage2, issue2.getDiagnostics()); - OperationOutcomeIssueComponent issue3 = issues.get(2); + OperationOutcomeIssueComponent issue3 = issues.get(3); assertEquals(IssueSeverity.INFORMATION, issue3.getSeverity()); assertEquals("Parameters.parameter[0].resource/*Procedure/null*/.code.coding[2]", issue3.getLocation().get(0).getValue()); String expectedMessage3 = "This element does not match any known slice defined in the profile http://example.org/fhir/StructureDefinition/TestProcedure|1.0.0 (this may not be a problem, but you should check that it's not intended to match a slice) - Does not match slice 'slice2' (discriminator: ($this memberOf 'http://acme.org/ValueSet/valueset-2'))"; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index c6a3597efadb..1d03ee97323e 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -776,7 +776,7 @@ private boolean canValidateCodeSystem( ValidationSupportContext theValidationSupportContext, IValidationSupport theValidationSupport, String theCodeSystemUrl) { - IsCanValidateCodeSystemKey key = new IsCanValidateCodeSystemKey(theValidationSupport, theCodeSystemUrl); + CanValidateCodeSystemKey key = new CanValidateCodeSystemKey(theValidationSupport, theCodeSystemUrl); CacheValue value = getFromCache(key); if (value == null) { value = new CacheValue<>( @@ -1255,23 +1255,23 @@ public int hashCode() { } } - static class IsCanValidateCodeSystemKey extends BaseKey { + static class CanValidateCodeSystemKey extends BaseKey { private final String myCodeSystemUrl; private final IValidationSupport myValidationSupport; private final int myHashCode; - private IsCanValidateCodeSystemKey(IValidationSupport theValidationSupport, String theCodeSystemUrl) { + private CanValidateCodeSystemKey(IValidationSupport theValidationSupport, String theCodeSystemUrl) { myValidationSupport = theValidationSupport; myCodeSystemUrl = theCodeSystemUrl; - myHashCode = Objects.hash("IsCanValidateCodeSystemKey", theValidationSupport, myCodeSystemUrl); + myHashCode = Objects.hash("CanValidateCodeSystemKey", theValidationSupport, myCodeSystemUrl); } @Override public boolean equals(Object theO) { if (this == theO) return true; - if (!(theO instanceof IsCodeSystemSupportedKey)) return false; - IsCodeSystemSupportedKey that = (IsCodeSystemSupportedKey) theO; + if (!(theO instanceof CanValidateCodeSystemKey)) return false; + CanValidateCodeSystemKey that = (CanValidateCodeSystemKey) theO; return myValidationSupport == that.myValidationSupport && Objects.equals(myCodeSystemUrl, that.myCodeSystemUrl); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java index c04c9af3376e..bd04d4345bd9 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java @@ -185,25 +185,25 @@ public void testValidateCode_WithoutValueSetUrl(boolean theUseCache) { prepareMock(myValidationSupport0, myValidationSupport1, myValidationSupport2); ValidationSupportChain chain = new ValidationSupportChain(newCacheConfiguration(theUseCache), myValidationSupport0, myValidationSupport1, myValidationSupport2); - when(myValidationSupport0.isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); - when(myValidationSupport1.isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); + when(myValidationSupport0.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); + when(myValidationSupport1.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); when(myValidationSupport1.validateCode(any(), any(), any(), any(), any(), any())).thenAnswer(t -> new IValidationSupport.CodeValidationResult()); // Test IValidationSupport.CodeValidationResult result = chain.validateCode(newValidationCtx(chain), new ConceptValidationOptions(), CODE_SYSTEM_URL_0, CODE_0, DISPLAY_0, null); // Verify - verify(myValidationSupport0, times(1)).isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport1, times(1)).isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport2, never()).isCodeSystemSupported(any(), any()); + verify(myValidationSupport0, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport1, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport2, never()).canValidateCodeSystem(any(), any()); verify(myValidationSupport0, never()).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport1, times(1)).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport2, never()).validateCode(any(), any(), any(), any(), any(), any()); // Setup for second execution (should use cache this time) prepareMock(myValidationSupport0, myValidationSupport1, myValidationSupport2); - when(myValidationSupport0.isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); - when(myValidationSupport1.isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); + when(myValidationSupport0.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); + when(myValidationSupport1.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); when(myValidationSupport1.validateCode(any(), any(), any(), any(), any(), any())).thenAnswer(t -> new IValidationSupport.CodeValidationResult()); // Test again (should use cache) @@ -215,9 +215,9 @@ public void testValidateCode_WithoutValueSetUrl(boolean theUseCache) { verifyNoInteractions(myValidationSupport0, myValidationSupport1, myValidationSupport2); } else { assertNotSame(result, result2); - verify(myValidationSupport0, times(1)).isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport1, times(1)).isCodeSystemSupported(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport2, never()).isCodeSystemSupported(any(), any()); + verify(myValidationSupport0, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport1, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport2, never()).canValidateCodeSystem(any(), any()); verify(myValidationSupport0, never()).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport1, times(1)).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport2, never()).validateCode(any(), any(), any(), any(), any(), any()); 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 4c7d07495854..27dde1e53d84 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 @@ -220,8 +220,8 @@ public void testCodedAnswer() { q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions(new Reference("http://somevalueset")); when(myValSupport.fetchResource(eq(Questionnaire.class), eq(QUESTIONNAIRE_URL))).thenReturn(q); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(ValueSet.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(ValueSet.class))) @@ -1030,8 +1030,8 @@ public void testOpenchoiceAnswer() { item.setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.OPENCHOICE).setOptions(new Reference("http://somevalueset")); when(myValSupport.fetchResource(eq(Questionnaire.class), eq(questionnaireRef))).thenReturn(q); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) 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 17c347740b44..8e39a1e68e3c 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 @@ -215,8 +215,8 @@ public void testCodedAnswer() { q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setAnswerValueSet("http://somevalueset"); when(myValSupport.fetchResource(eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) 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 fbd25fbad332..57de09dac81b 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 @@ -215,8 +215,8 @@ public void testCodedAnswer() { q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CODING).setAnswerValueSet("http://somevalueset"); when(myValSupport.fetchResource(eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.isCodeSystemSupported(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) From 575afb5a6f0c47bfb841d931aaf65d1106fa83c7 Mon Sep 17 00:00:00 2001 From: Emre Dincturk Date: Wed, 13 Aug 2025 16:10:51 -0400 Subject: [PATCH 8/8] there --- .../context/support/IValidationSupport.java | 14 +- .../ResourceProviderDstu3CodeSystemTest.java | 2 +- .../dao/r4/FhirResourceDaoR4ValidateTest.java | 291 +++++------------- ...oryTerminologyServerValidationSupport.java | 6 +- ...ownCodeSystemWarningValidationSupport.java | 49 +-- .../support/ValidationSupportChain.java | 25 +- ...WorkerContextValidationSupportAdapter.java | 3 - .../support/ValidationSupportChainTest.java | 20 +- ...estionnaireResponseValidatorDstu3Test.java | 8 +- .../QuestionnaireResponseValidatorR4Test.java | 4 +- .../QuestionnaireResponseValidatorR5Test.java | 4 +- 11 files changed, 128 insertions(+), 298 deletions(-) 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 4f9d71357271..38bb69ad6e59 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 @@ -255,7 +255,19 @@ default boolean isCodeSystemSupported(ValidationSupportContext theValidationSupp return false; } - default boolean canValidateCodeSystem(ValidationSupportContext theValidationSupportContext, String theSystem) { + /** + * For most validation support modules, this method should return the same value as {@link IValidationSupport#isCodeSystemSupported(ValidationSupportContext, String)} and + * no specific implementation is required other than the default implementation provided here. + * A validation support module should override this only if it wants to generate a validation result even for an unsupported CodeSystem. + * For example, {@link org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport} overrides this method to generate issues for unknown CodeSystems. + * + * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to + * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. + * @param theSystem The URI for the code system, e.g. "http://loinc.org" + * @return Returns true if a validation result can be generated by this validation support module even when the code system is not supported. + */ + default boolean canGenerateValidationResultForCodeSystem( + ValidationSupportContext theValidationSupportContext, String theSystem) { return isCodeSystemSupported(theValidationSupportContext, theSystem); } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index f4952def53c6..b68915809493 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -359,7 +359,7 @@ public void testValidateCodeOperation() { inParams.addParameter().setName("code").setValue(new CodeType("1")); Parameters outcome = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute(); - ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); + ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); String message = outcome .getParameter() 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 a7f64d096e12..f7c76124a31e 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 @@ -184,7 +184,8 @@ public void testValidateCodeInValueSetWithUnknownCodeSystem_FailValidation() { encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue().size()).as(encoded).isEqualTo(2); - assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encoded).isEqualTo("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); + assertThat(oo.getIssue().get(0).getDiagnostics()).as(encoded).isEqualTo("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); + assertThat(oo.getIssue().get(0).getSeverity()).as(encoded).isEqualTo(OperationOutcome.IssueSeverity.ERROR); assertThat(oo.getIssue().get(1).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); assertThat(oo.getIssue().get(1).getDiagnostics()).contains("Unknown code 'http://cs#code99' for in-memory expansion of ValueSet 'http://vs'"); assertThat(oo.getIssue().get(1).getSeverity()).as(encoded).isEqualTo(OperationOutcome.IssueSeverity.ERROR); @@ -281,7 +282,8 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Error() { // Valid code obs.setValue(new Quantity().setSystem("http://cs").setCode("code1").setValue(123)); - //the code system is unknown, we should expect an error for that, but no error related to the code not being in the value set, since it is valid + // The code system is unknown, we should expect an error for that. + // But we don't expect an error related to the code not being in the value set, since it is a valid code in the value set. oo = validateAndReturnOutcome(obs, true); encoded = encode(oo); ourLog.info(encoded); @@ -296,14 +298,18 @@ public void testValidateCodeInEnumeratedValueSetWithUnknownCodeSystem_Error() { ourLog.info(encoded); 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'"); + assertThat(oo.getIssue().get(0).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); assertThat(oo.getIssue().get(1).getDiagnostics()).contains("provided (http://cs#code99) was not found in the value set"); - assertEquals(OperationOutcome.IssueSeverity.ERROR, oo.getIssueFirstRep().getSeverity()); + assertThat(oo.getIssue().get(1).getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); } - @Test - public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Information() { - setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.INFORMATION); + @ParameterizedTest + @CsvSource({ + "information", "warning", "error" + }) + public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem(String theUnknownCodeSeverity) { + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.fromCode(theUnknownCodeSeverity)); createStructureDefWithBindingToUnknownCs(false); @@ -312,88 +318,25 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Informa OperationOutcome oo; String encoded; - // Valid code + // in this test setup, the value set doesn't contain any enumerated codes, + // and the http://cs is an unknown system so the code provided here cannot be validated in any way. obs.setValue(new Quantity().setSystem("http://cs").setCode("code1").setValue(123)); - oo = validateAndReturnOutcome(obs, false); + boolean expectError = "error".equals(theUnknownCodeSeverity); + oo = validateAndReturnOutcome(obs, expectError); encoded = encode(oo); ourLog.info(encoded); assertThat(oo.getIssue()).hasSize(3); - 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.INFORMATION, unableToExpandError.getSeverity()); - - OperationOutcome.OperationOutcomeIssueComponent unknownCodeSystemError = oo.getIssue().get(1); - assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, unknownCodeSystemError.getSeverity()); - - OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(2); - 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(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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, 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()); + OperationOutcome.IssueSeverity expectedSeverity = OperationOutcome.IssueSeverity.fromCode(theUnknownCodeSeverity); - // Invalid code - obs.setValue(new Quantity().setSystem("http://cs").setCode("code99").setValue(123)); - oo = validateAndReturnOutcome(obs, false); - encoded = encode(oo); - ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(3); - - unableToExpandError = oo.getIssue().get(0); - assertThat(unableToExpandError.getDiagnostics()).contains("Unable to expand ValueSet because CodeSystem could not be found: http://cs"); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, unableToExpandError.getSeverity()); - - unknownCodeSystemError = oo.getIssue().get(1); - assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, unknownCodeSystemError.getSeverity()); - - notInValueSetError = oo.getIssue().get(2); - assertThat(notInValueSetError.getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code99"); - assertThat(notInValueSetError.getDiagnostics()).contains("HAPI-0702: Unable to expand ValueSet because CodeSystem could not be found: http://cs"); - 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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); - assertEquals(OperationOutcome.IssueSeverity.INFORMATION, 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()); - } - - /** - * By default, an unknown code system should fail validation - */ - @Test - public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Warning() { - // set to warning - setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.WARNING); - - createStructureDefWithBindingToUnknownCs(false); - - Observation obs = createObservationForUnknownCodeSystemTest(); - - OperationOutcome oo; - String encoded; - - // Valid code - obs.setValue(new Quantity().setSystem("http://cs").setCode("code1").setValue(123)); - - oo = validateAndReturnOutcome(obs, false); - encoded = encode(oo); - ourLog.info(encoded); 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.WARNING, unableToExpandError.getSeverity()); + assertEquals(expectedSeverity, unableToExpandError.getSeverity()); OperationOutcome.OperationOutcomeIssueComponent unknownCodeSystemError = oo.getIssue().get(1); assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); - assertEquals(OperationOutcome.IssueSeverity.WARNING, unknownCodeSystemError.getSeverity()); + assertEquals(expectedSeverity, unknownCodeSystemError.getSeverity()); OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(2); assertThat(notInValueSetError.getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code1"); @@ -401,85 +344,11 @@ public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Warning 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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); - assertEquals(OperationOutcome.IssueSeverity.WARNING, 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()); - - - // Invalid code - obs.setValue(new Quantity().setSystem("http://cs").setCode("code99").setValue(123)); - oo = validateAndReturnOutcome(obs, false); - encoded = encode(oo); - ourLog.info(encoded); - - - unableToExpandError = oo.getIssue().get(0); - assertThat(unableToExpandError.getDiagnostics()).contains("Unable to expand ValueSet because CodeSystem could not be found: http://cs"); - assertEquals(OperationOutcome.IssueSeverity.WARNING, unableToExpandError.getSeverity()); - - unknownCodeSystemError = oo.getIssue().get(1); - assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code99'"); - assertEquals(OperationOutcome.IssueSeverity.WARNING, unknownCodeSystemError.getSeverity()); - - notInValueSetError = oo.getIssue().get(2); - assertThat(notInValueSetError.getDiagnostics()).contains("Failed to expand ValueSet 'http://vs' (in-memory). Could not validate code http://cs#code99"); - assertThat(notInValueSetError.getDiagnostics()).contains("HAPI-0702: Unable to expand ValueSet because CodeSystem could not be found: http://cs"); - 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(OperationOutcome.IssueType.PROCESSING, notInValueSetError.getCode()); - assertEquals(OperationOutcome.IssueSeverity.WARNING, notInValueSetError.getSeverity()); + assertEquals(expectedSeverity, 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()); - } - - @Test - public void testValidateCodeInNonEnumeratedValueSetWithUnknownCodeSystem_Error() { - setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); - - createStructureDefWithBindingToUnknownCs(false); - - Observation obs = new Observation(); - obs.getMeta().addProfile("http://sd"); - obs.getText().setDivAsString("

Hello
"); - obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); - obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs"); - obs.getCode().setText("hello"); - obs.setSubject(new Reference("Patient/123")); - obs.addPerformer(new Reference("Practitioner/123")); - obs.setEffective(DateTimeType.now()); - obs.setStatus(ObservationStatus.FINAL); - - OperationOutcome oo; - String encoded; - - // Valid code - obs.setValue(new Quantity().setSystem("http://cs").setCode("code1").setValue(123)); - oo = validateAndReturnOutcome(obs, true); - encoded = encode(oo); - ourLog.info(encoded); - assertThat(oo.getIssue()).hasSize(3); - 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, unableToExpandError.getSeverity()); - OperationOutcome.OperationOutcomeIssueComponent unknownCodeSystemError = oo.getIssue().get(1); - assertThat(unknownCodeSystemError.getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://cs for 'http://cs#code1'"); - assertEquals(OperationOutcome.IssueSeverity.ERROR, unknownCodeSystemError.getSeverity()); - - OperationOutcome.OperationOutcomeIssueComponent notInValueSetError = oo.getIssue().get(2); - 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(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()); } private Observation createObservationForUnknownCodeSystemTest() { @@ -744,7 +613,7 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep OperationOutcome oo; // Valid code -/* + obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3"); oo = validateAndReturnOutcome(obs); @@ -762,7 +631,7 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep oo = validateAndReturnOutcome(obs); assertThat(encode(oo)).contains("None of the codings provided are in the value set 'ValueSet[http://example.com/fhir/ValueSet/observation-vitalsignresult]' (http://example.com/fhir/ValueSet/observation-vitalsignresult)"); -*/ + // Valid code with wrong system obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3"); @@ -770,7 +639,7 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep assertThat(oo.getIssue().get(0).getDiagnostics()).as(encode(oo)).isEqualTo("CodeSystem is unknown and can't be validated: http://foo for '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("Vital Signs"); @@ -801,17 +670,12 @@ public void testValidateCode_InMemoryExpansionAgainstHugeValueSet() throws Excep oo = validateAndReturnOutcome(obs); assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("No issues detected during validation"); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - -*/ - } @ParameterizedTest @MethodSource("paramsUnknownCodeSystemBindingStrengths") void testValidateCode_unknownCodeSystem_returnsCorrectSeverityForDifferentBindingStrengths(Enumerations.BindingStrength theStructureDefinitionStrength, int theExpectedNumIssues, String theExpectedSeverity, List theExpectedDiagnosticsMessages) throws Exception { - //setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); - // Given myStorageSettings.setPreExpandValueSets(true); @@ -858,6 +722,13 @@ private static Stream paramsUnknownCodeSystemBindingStrengths() { String codingIsNotInValueSetMessageWithBindingSpecificReason ="None of the codings provided are in the value set 'ValueSet[http://mytest/ValueSet/OrgContactSampleVS]' (http://mytest/ValueSet/OrgContactSampleVS), and"; String unknownCodeSystemMessage = "CodeSystem is unknown and can't be validated: http://mylocalcodesystem for 'http://mylocalcodesystem#mylocalcode'"; String unableToValidateCodeMessage = "Unable to validate code http://mylocalcodesystem#mylocalcode - No codes in ValueSet belong to CodeSystem with URL http://mylocalcodesystem"; + + // The core validator has its own logic to determine the severity of not_found systems/codes based on the binding strength. + // It usually sets the severity to Error for REQUIRED bindings and Warning for others. + // So even when we configure our own validator to generate an Error, the core validator will downgrade some of them to Warnings. + // That is the reason why in this test the severity for unknown code systems is not set and hence defaults to Error, but the actual severity is set by the core validator and is only error the required case, and warnings for the other cases. + // see for example: https://github.com/hapifhir/org.hl7.fhir.core/blob/1e148bf3d303f360556654c6586388a582f4fd4c/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java#L1884 + // and the related question: https://github.com/hapifhir/org.hl7.fhir.core/issues/2129 return Stream.of( Arguments.of(Enumerations.BindingStrength.REQUIRED, 3, "Error", List.of(unableToValidateCodeMessage, unknownCodeSystemMessage, codingIsNotInValueSetMessageWithBindingSpecificReason)), Arguments.of(Enumerations.BindingStrength.EXTENSIBLE, 3, "Warning", List.of(unableToValidateCodeMessage, unknownCodeSystemMessage, codingIsNotInValueSetMessageWithBindingSpecificReason)), @@ -866,6 +737,40 @@ private static Stream paramsUnknownCodeSystemBindingStrengths() { ); } + @ParameterizedTest + @CsvSource({"error", "warning", "information"}) + @Disabled(""" + This test is disabled because it fails for the "error" severity. + The core validator has its own logic to determine the severity of not_found system based on the binding strength. + It sets the severity to Error for REQUIRED bindings and Warning for others. + So even when we configure our own validator to generate an Error for an unknown system, the core validator downgrades it a warning. + see for example: https://github.com/hapifhir/org.hl7.fhir.core/blob/1e148bf3d303f360556654c6586388a582f4fd4c/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java#L1884 + and the related question: https://github.com/hapifhir/org.hl7.fhir.core/issues/2129 + Depending on the outcome of that discussion, we may need to change the test to expect a warning instead of an error when the binding strength is not "required" and update the documentation accordingly. + """) + public void testValidateCode_UnknownCodeSystem_ExampleBindingStrength_whenUnknownIssueSeverityConfigured(String theIssueSeverityCode) { + setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.fromCode(theIssueSeverityCode)); + + Medication medication = new Medication(); + // the Medication.code has anbinding to http://snomed.info/sct with "example" strength in the FHIR spec + medication.setCode(new CodeableConcept().addCoding(new Coding("http://unknown-system", "169008", "Product containing hypothalamic releasing factor"))); + medication.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + + OperationOutcome oo; + String encoded; + + // execute + oo = validateAndReturnOutcome(medication); + + // verify + encoded = encode(oo); + ourLog.info(encoded); + assertThat(oo.getIssue()).hasSize(1); + assertThat(oo.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.fromCode(theIssueSeverityCode)); + assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://unknown-system for 'http://unknown-system#169008'"); + } + + @Test public void testValidateProfileTargetType_PolicyCheckValid() throws IOException { myValidationSettings.setLocalReferenceValidationDefaultPolicy(ReferenceValidationPolicy.CHECK_VALID); @@ -1310,6 +1215,13 @@ public void testValidateCode_PreExpansionAgainstHugeValueSet() throws Exception * Make sure that we do something sane when validating throws an unexpected exception */ @Test + @Disabled(""" + I've disabled this test because it doesn't test what it claims to test since it was created, and it was passing for the wrong reason. + It looks like it is supposed test what happens when a ValidationSupport class throws exception on validateCodeInValueSet, + but the stubbed method catches and suppresses the exception it is supposed to throw, and returns null. + Not clear what is the expected behavior here, but the SupportChain doesn't have any logic to suppress the exceptions thrown + by a ValidationSupport on validateCodeInValueSet, so the current behaviour is that whatever exception thrown by a ValidationSupport bubbles up to the caller. + """) public void testValidate_ValidationSupportThrowsException() { IValidationSupport validationSupport = mock(IValidationSupport.class); when(validationSupport.isValueSetSupported(any(), any())).thenReturn(true); @@ -1320,7 +1232,6 @@ public void testValidate_ValidationSupportThrowsException() { myResourceTableDao.flush(); } catch (Exception e) { ourLog.info("Hit expected exception: {}", e.toString()); - throw e; } return null; }); @@ -1342,11 +1253,8 @@ public void testValidate_ValidationSupportThrowsException() { // Valid code obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED); - obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("100022-3").setDisplay("Display 3"); + obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("CODE3").setDisplay("Display 3"); oo = validateAndReturnOutcome(obs); - - String encoded = encode(oo); - ourLog.info("HERE:" + encoded); assertThat(oo.getIssueFirstRep().getDiagnostics()).as(encode(oo)).isEqualTo("No issues detected during validation"); } finally { @@ -2598,62 +2506,9 @@ public void testValidateObservationWithVitalSignsLoincCode() { assertHasNoErrors(oo); } - @Test - public void testValidateCodeInValueSetWithUnknownCodeSystem_whenWarningSeverityConfigured_mustRaiseWarningAndSucceed() { - setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.WARNING); - - Medication medication = new Medication(); - medication.setCode(new CodeableConcept().addCoding(new Coding("http://test123.org", "golden", "xxx"))); - //medication.setCode(new CodeableConcept().addCoding(new Coding("http://hl7.org/fhir/universal-tag", "golden", "xxx"))); - //medication.setCode(new CodeableConcept().addCoding(new Coding("http://snomed.info/sct", "00559407", "xxx"))); - medication.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); - - OperationOutcome oo; - String encoded; - - // execute - oo = validateAndReturnOutcome(medication); - - // verify - encoded = encode(oo); - ourLog.info(encoded); - assertThat(oo.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.WARNING); - assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: http://test123.org for 'http://test123.org#golden'"); - } - - - @Test -// @CsvSource({"ERROR", "WARNING"}) -// public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityConfigured_mustRaiseErrorAndFail(String theIssueSeverity) { - @Disabled - public void testValidateCodeInValueSetWithUnknownCodeSystem_whenDefaultSeverityConfigured_mustRaiseErrorAndFail() { - setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity.ERROR); - - Medication medication = new Medication(); - - //medication.getMeta().addTag(new Coding("http://hl7.org/fhir/universal-tag", "golden", "xxx")); - //medication.getMeta().addTag(new Coding("http://test123.org", "golden", "xxx")); - medication.setCode(new CodeableConcept().addCoding(new Coding("http://test123.org", "golden", "xxx"))); - //medication.setCode(new CodeableConcept().addCoding(new Coding("http://hl7.org/fhir/universal-tag", "golden", "xxx"))); - //medication.setCode(new CodeableConcept().addCoding(new Coding("http://snomed.info/scts", "00559407", "xxx"))); - medication.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); - - OperationOutcome oo; - String encoded; - - // execute - oo = validateAndReturnOutcome(medication); - - // verify - encoded = encode(oo); - ourLog.info("HERE:" + encoded); - assertThat(oo.getIssueFirstRep().getSeverity()).isEqualTo(OperationOutcome.IssueSeverity.ERROR); - assertThat(oo.getIssueFirstRep().getDiagnostics()).contains("CodeSystem is unknown and can't be validated: https//test123.org for 'http://test123.org#10000'"); - } - private void setIssueSeverityForUnknownCodeSystem(IValidationSupport.IssueSeverity theSeverity) { myStorageSettings.setIssueSeverityForUnknownCodeSystem(theSeverity); - // in the JpaServer, these two below would be the set based upon the storage configuration upon their instantiation, + // in the JpaServer, these two below would be the set based on the storage configuration upon their instantiation, // however, in individual tests these objects are already instantiated when the test method starts, // I could try to have nested test classes for each severity case, but that would make my changes more difficult to review // and see what I actually changed in the tests so instead I set the severity here directly for now, for the first iteration. 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 29c08536f9c9..858a03e27df2 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 @@ -191,9 +191,9 @@ public CodeValidationResult validateCodeInValueSet( theValidationSupportContext, theValueSet, theCodeSystemUrlAndVersion, theCode); } catch (ExpansionCouldNotBeCompletedInternallyException e) { CodeValidationResult codeValidationResult = new CodeValidationResult(); - if (e.getCodeValidationIssue() != null) { + if (e.getCodeValidationIssue() != null && e.getCodeValidationIssue().getSeverity() != null) { + // preserve the severity from the original issue by assigning it to the result codeValidationResult.setSeverity(e.getCodeValidationIssue().getSeverity()); - // codeValidationResult.setCode(theCode); } else { codeValidationResult.setSeverity(IssueSeverity.ERROR); } @@ -917,7 +917,6 @@ private boolean expandValueSetR5IncludeOrExclude( if (includeOrExcludeSystemResource == null || isIncludeCodeSystemIgnored) { if (theWantCode != null) { - if (theValidationSupportContext .getRootValidationSupport() .isCodeSystemSupported(theValidationSupportContext, includeOrExcludeConceptSystemUrl)) { @@ -1133,7 +1132,6 @@ private Function newCodeSystemLoader(ValidationSupportContex } } - // create a record for a String message and an IssueSeverity private record MessageWithSeverity(String message, IssueSeverity severity) {} private MessageWithSeverity getFailureMessageForMissingOrUnusableCodeSystem( 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 af922eb6e63a..f351fcd9f914 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 @@ -13,7 +13,7 @@ /** * This validation support module may be placed at the end of a {@link ValidationSupportChain} - * in order to configure the validator to generate a warning if a resource being validated + * in order to configure the validator to generate a warning or an error if a resource being validated * contains an unknown code system. * * Note that this module must also be activated by calling {@link #setAllowNonExistentCodeSystem(boolean)} @@ -41,12 +41,10 @@ public String getName() { @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { return true; - // return false; } @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { - // return canValidateCodeSystem(theValidationSupportContext, theSystem); return false; } @@ -65,7 +63,7 @@ public CodeValidationResult validateCode( String theCode, String theDisplay, String theValueSetUrl) { - if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { + if (!canGenerateValidationResultForCodeSystem(theValidationSupportContext, theCodeSystem)) { return null; } @@ -76,19 +74,11 @@ public CodeValidationResult validateCode( + "#" + theCode + "'"; result.setMessage(theMessage); - // For information level, we just strip out the severity+message entirely - // so that nothing appears in the validation result - /* if (myNonExistentCodeSystemSeverity == IssueSeverity.INFORMATION) { - result.setCode(theCode); - result.setSeverity(null); - result.setMessage(null); - } else {*/ result.addIssue(new CodeValidationIssue( theMessage, myNonExistentCodeSystemSeverity, CodeValidationIssueCode.NOT_FOUND, CodeValidationIssueCoding.NOT_FOUND)); - // } return result; } @@ -102,8 +92,7 @@ public CodeValidationResult validateCodeInValueSet( String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) { - // return null; - if (!canValidateCodeSystem(theValidationSupportContext, theCodeSystem)) { + if (!canGenerateValidationResultForCodeSystem(theValidationSupportContext, theCodeSystem)) { return null; } @@ -115,37 +104,11 @@ public CodeValidationResult validateCodeInValueSet( } /** - * Returns true if non existent code systems will still validate. - * False if they will throw errors. - * @return - */ - private boolean allowNonExistentCodeSystems() { - switch (myNonExistentCodeSystemSeverity) { - case ERROR: - case FATAL: - return true; - case WARNING: - case INFORMATION: - return true; - default: - ourLog.info("Unknown issue severity " + myNonExistentCodeSystemSeverity.name() - + ". Treating as INFO/WARNING"); - return true; - } - } - - /** - * Determines if the code system can (and should) be validated. - * @param theValidationSupportContext - * @param theCodeSystem - * @return + * If a validation support can fetch the code system, returns false. Otherwise, returns true. */ @Override - public boolean canValidateCodeSystem(ValidationSupportContext theValidationSupportContext, String theCodeSystem) { - // EMRE: with the change code this is always skipped - if (!allowNonExistentCodeSystems()) { - return false; - } + public boolean canGenerateValidationResultForCodeSystem( + ValidationSupportContext theValidationSupportContext, String theCodeSystem) { if (theCodeSystem == null) { return false; } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index 1d03ee97323e..bd4c901083fc 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -772,15 +772,16 @@ private boolean isCodeSystemSupported( return value.getValue(); } - private boolean canValidateCodeSystem( + private boolean canGenerateValidationResultForCodeSystem( ValidationSupportContext theValidationSupportContext, IValidationSupport theValidationSupport, String theCodeSystemUrl) { - CanValidateCodeSystemKey key = new CanValidateCodeSystemKey(theValidationSupport, theCodeSystemUrl); + CanGenerateValidationResultForCodeSystemKey key = + new CanGenerateValidationResultForCodeSystemKey(theValidationSupport, theCodeSystemUrl); CacheValue value = getFromCache(key); if (value == null) { - value = new CacheValue<>( - theValidationSupport.canValidateCodeSystem(theValidationSupportContext, theCodeSystemUrl)); + value = new CacheValue<>(theValidationSupport.canGenerateValidationResultForCodeSystem( + theValidationSupportContext, theCodeSystemUrl)); putInCache(key, value); } return value.getValue(); @@ -801,7 +802,9 @@ public CodeValidationResult validateCode( retVal = CacheValue.empty(); for (IValidationSupport next : myChain) { - if ((isBlank(theValueSetUrl) && canValidateCodeSystem(theValidationSupportContext, next, theCodeSystem)) + if ((isBlank(theValueSetUrl) + && canGenerateValidationResultForCodeSystem( + theValidationSupportContext, next, theCodeSystem)) || (isNotBlank(theValueSetUrl) && isValueSetSupported(theValidationSupportContext, next, theValueSetUrl))) { CodeValidationResult outcome = next.validateCode( @@ -1255,23 +1258,25 @@ public int hashCode() { } } - static class CanValidateCodeSystemKey extends BaseKey { + static class CanGenerateValidationResultForCodeSystemKey extends BaseKey { private final String myCodeSystemUrl; private final IValidationSupport myValidationSupport; private final int myHashCode; - private CanValidateCodeSystemKey(IValidationSupport theValidationSupport, String theCodeSystemUrl) { + private CanGenerateValidationResultForCodeSystemKey( + IValidationSupport theValidationSupport, String theCodeSystemUrl) { myValidationSupport = theValidationSupport; myCodeSystemUrl = theCodeSystemUrl; - myHashCode = Objects.hash("CanValidateCodeSystemKey", theValidationSupport, myCodeSystemUrl); + myHashCode = + Objects.hash("CanGenerateValidationResultForCodeSystemKey", theValidationSupport, myCodeSystemUrl); } @Override public boolean equals(Object theO) { if (this == theO) return true; - if (!(theO instanceof CanValidateCodeSystemKey)) return false; - CanValidateCodeSystemKey that = (CanValidateCodeSystemKey) theO; + if (!(theO instanceof CanGenerateValidationResultForCodeSystemKey)) return false; + CanGenerateValidationResultForCodeSystemKey that = (CanGenerateValidationResultForCodeSystemKey) theO; return myValidationSupport == that.myValidationSupport && Objects.equals(myCodeSystemUrl, that.myCodeSystemUrl); } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java index d262f38a03a0..d5799dda8d0b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/WorkerContextValidationSupportAdapter.java @@ -906,9 +906,6 @@ private IValidationSupport.CodeValidationResult validateCodeInValueSet( result.addIssue(codeValidationIssue); } } - /* if (result.getSeverity() == null || codeSystemResult.getSeverity().ordinal() < result.getSeverity().ordinal()) { - result.setSeverity(codeSystemResult.getSeverity()); - }*/ } } return result; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java index bd04d4345bd9..1f9bf3b27911 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChainTest.java @@ -185,25 +185,25 @@ public void testValidateCode_WithoutValueSetUrl(boolean theUseCache) { prepareMock(myValidationSupport0, myValidationSupport1, myValidationSupport2); ValidationSupportChain chain = new ValidationSupportChain(newCacheConfiguration(theUseCache), myValidationSupport0, myValidationSupport1, myValidationSupport2); - when(myValidationSupport0.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); - when(myValidationSupport1.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); + when(myValidationSupport0.canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); + when(myValidationSupport1.canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); when(myValidationSupport1.validateCode(any(), any(), any(), any(), any(), any())).thenAnswer(t -> new IValidationSupport.CodeValidationResult()); // Test IValidationSupport.CodeValidationResult result = chain.validateCode(newValidationCtx(chain), new ConceptValidationOptions(), CODE_SYSTEM_URL_0, CODE_0, DISPLAY_0, null); // Verify - verify(myValidationSupport0, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport1, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport2, never()).canValidateCodeSystem(any(), any()); + verify(myValidationSupport0, times(1)).canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport1, times(1)).canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport2, never()).canGenerateValidationResultForCodeSystem(any(), any()); verify(myValidationSupport0, never()).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport1, times(1)).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport2, never()).validateCode(any(), any(), any(), any(), any(), any()); // Setup for second execution (should use cache this time) prepareMock(myValidationSupport0, myValidationSupport1, myValidationSupport2); - when(myValidationSupport0.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); - when(myValidationSupport1.canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); + when(myValidationSupport0.canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(false); + when(myValidationSupport1.canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0))).thenReturn(true); when(myValidationSupport1.validateCode(any(), any(), any(), any(), any(), any())).thenAnswer(t -> new IValidationSupport.CodeValidationResult()); // Test again (should use cache) @@ -215,9 +215,9 @@ public void testValidateCode_WithoutValueSetUrl(boolean theUseCache) { verifyNoInteractions(myValidationSupport0, myValidationSupport1, myValidationSupport2); } else { assertNotSame(result, result2); - verify(myValidationSupport0, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport1, times(1)).canValidateCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); - verify(myValidationSupport2, never()).canValidateCodeSystem(any(), any()); + verify(myValidationSupport0, times(1)).canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport1, times(1)).canGenerateValidationResultForCodeSystem(any(), eq(CODE_SYSTEM_URL_0)); + verify(myValidationSupport2, never()).canGenerateValidationResultForCodeSystem(any(), any()); verify(myValidationSupport0, never()).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport1, times(1)).validateCode(any(), any(), any(), any(), any(), any()); verify(myValidationSupport2, never()).validateCode(any(), any(), any(), any(), any(), any()); 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 27dde1e53d84..c618635e9599 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 @@ -220,8 +220,8 @@ public void testCodedAnswer() { q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions(new Reference("http://somevalueset")); when(myValSupport.fetchResource(eq(Questionnaire.class), eq(QUESTIONNAIRE_URL))).thenReturn(q); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(ValueSet.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCodeInValueSet(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(ValueSet.class))) @@ -1030,8 +1030,8 @@ public void testOpenchoiceAnswer() { item.setLinkId("link0").setRequired(true).setType(QuestionnaireItemType.OPENCHOICE).setOptions(new Reference("http://somevalueset")); when(myValSupport.fetchResource(eq(Questionnaire.class), eq(questionnaireRef))).thenReturn(q); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) 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 8e39a1e68e3c..677a8018492c 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 @@ -215,8 +215,8 @@ public void testCodedAnswer() { q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setAnswerValueSet("http://somevalueset"); when(myValSupport.fetchResource(eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class))) 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 57de09dac81b..ecff509e6116 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 @@ -215,8 +215,8 @@ public void testCodedAnswer() { q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CODING).setAnswerValueSet("http://somevalueset"); when(myValSupport.fetchResource(eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); - when(myValSupport.canValidateCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system"))).thenReturn(true); + when(myValSupport.canGenerateValidationResultForCodeSystem(any(), eq("http://codesystems.com/system2"))).thenReturn(true); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), nullable(String.class))) .thenReturn(new IValidationSupport.CodeValidationResult().setCode("code0")); when(myValSupport.validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code1"), any(), nullable(String.class)))