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 6b998e592af..e19719532d1 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
@@ -928,5 +928,15 @@ public interface IValidationSupport {
}
}
-
+ /**
+ * See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation.
+ *
+ * If true, validation for codings will return a positive result if all codings are valid.
+ * If false, validation for codings will return a positive result if there is any coding that is valid.
+ *
+ * @return if the application has configured validation to use logical AND, as opposed to logical OR, which is the default
+ */
+ default boolean isEnabledValidationForCodingsLogicalAnd() {
+ return false;
+ }
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java
index 5e4c6dc2b0a..174102c6554 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValidationSupportContext.java
@@ -42,4 +42,8 @@ public class ValidationSupportContext {
public Set getCurrentlyGeneratingSnapshots() {
return myCurrentlyGeneratingSnapshots;
}
+
+ public boolean isEnabledValidationForCodingsLogicalAnd() {
+ return myRootValidationSupport.isEnabledValidationForCodingsLogicalAnd();
+ }
}
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_4_0/4461-validation-language-coding-inconsistent-one-incorrect-vs-combo-with-correct.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_4_0/4461-validation-language-coding-inconsistent-one-incorrect-vs-combo-with-correct.yaml
new file mode 100644
index 00000000000..614661ab784
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_4_0/4461-validation-language-coding-inconsistent-one-incorrect-vs-combo-with-correct.yaml
@@ -0,0 +1,8 @@
+---
+type: fix
+issue: 4461
+jira: SMILE-5442
+title: "There is inconsistent validation in the case of an incorrect communications language coding due to validation that looks for the first correct Coding.
+ In the case of a single incorrect code there is one validation result.
+ In the case of one correct code and one incorrect code, there is an entirely different result.
+ This has been fixed by introducing a new boolean flag in CachingValidationSupport as well as a new constructor to leverage it."
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java
index 578553a333d..caa10977064 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/ValidationSupportConfigUtil.java
@@ -27,10 +27,14 @@ public final class ValidationSupportConfigUtil {
private ValidationSupportConfigUtil() {}
public static CachingValidationSupport newCachingValidationSupport(JpaValidationSupportChain theJpaValidationSupportChain) {
+ return newCachingValidationSupport(theJpaValidationSupportChain, false);
+ }
+
+ public static CachingValidationSupport newCachingValidationSupport(JpaValidationSupportChain theJpaValidationSupportChain, boolean theIsEnabledValidationForCodingsLogicalAnd) {
// Short timeout for code translation because TermConceptMappingSvcImpl has its own caching
CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts.defaultValues()
.setTranslateCodeMillis(1000);
- return new CachingValidationSupport(theJpaValidationSupportChain, cacheTimeouts);
+ return new CachingValidationSupport(theJpaValidationSupportChain, cacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd);
}
}
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java
index 5a1e8778e6b..83b9e258cb0 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java
@@ -45,6 +45,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
private final ThreadPoolExecutor myBackgroundExecutor;
private final Map myNonExpiringCache;
private final Cache myExpandValueSetCache;
+ private final boolean myIsEnabledValidationForCodingsLogicalAnd;
/**
* Constructor with default timeouts
@@ -52,7 +53,15 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
* @param theWrap The validation support module to wrap
*/
public CachingValidationSupport(IValidationSupport theWrap) {
- this(theWrap, CacheTimeouts.defaultValues());
+ this(theWrap, CacheTimeouts.defaultValues(), false);
+ }
+
+ public CachingValidationSupport(IValidationSupport theWrap, boolean theIsEnabledValidationForCodingsLogicalAnd) {
+ this(theWrap, CacheTimeouts.defaultValues(), theIsEnabledValidationForCodingsLogicalAnd);
+ }
+
+ public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) {
+ this(theWrap, theCacheTimeouts, false);
}
/**
@@ -61,7 +70,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
* @param theWrap The validation support module to wrap
* @param theCacheTimeouts The timeouts to use
*/
- public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) {
+ public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts, boolean theIsEnabledValidationForCodingsLogicalAnd) {
super(theWrap.getFhirContext(), theWrap);
myExpandValueSetCache = CacheFactory.build(theCacheTimeouts.getExpandValueSetMillis(), 100);
myValidateCodeCache = CacheFactory.build(theCacheTimeouts.getValidateCodeMillis(), 5000);
@@ -85,6 +94,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
threadFactory,
new ThreadPoolExecutor.DiscardPolicy());
+ myIsEnabledValidationForCodingsLogicalAnd = theIsEnabledValidationForCodingsLogicalAnd;
}
@Override
@@ -314,4 +324,8 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
.setMiscMillis(10 * DateUtils.MILLIS_PER_MINUTE);
}
}
+
+ public boolean isEnabledValidationForCodingsLogicalAnd() {
+ return myIsEnabledValidationForCodingsLogicalAnd;
+ }
}
diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java
index 1973482b947..435aadb4977 100644
--- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java
+++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/validator/VersionSpecificWorkerContextWrapper.java
@@ -523,13 +523,22 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
@Override
public ValidationResult validateCode(ValidationOptions theOptions, org.hl7.fhir.r5.model.CodeableConcept code, org.hl7.fhir.r5.model.ValueSet theVs) {
+ List validationResultsOk = new ArrayList<>();
for (Coding next : code.getCoding()) {
ValidationResult retVal = validateCode(theOptions, next, theVs);
if (retVal.isOk()) {
- return retVal;
+ if (myValidationSupportContext.isEnabledValidationForCodingsLogicalAnd()) {
+ validationResultsOk.add(retVal);
+ } else {
+ return retVal;
+ }
}
}
+ if (code.getCoding().size() > 0 && validationResultsOk.size() == code.getCoding().size()) {
+ return validationResultsOk.get(0);
+ }
+
return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null);
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java
index 782864acd9d..16d81f7b191 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupportTest.java
@@ -5,11 +5,15 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.StructureDefinition;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
@@ -30,8 +34,10 @@ public class CachingValidationSupportTest {
@Mock
private IValidationSupport myValidationSupport;
- @Test
- public void testAsyncBackgroundLoading() {
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(booleans = {true, false})
+ public void testAsyncBackgroundLoading(Boolean theIsEnabledValidationForCodingsLogicalAnd) {
StructureDefinition sd0 = (StructureDefinition) new StructureDefinition().setId("SD0");
StructureDefinition sd1 = (StructureDefinition) new StructureDefinition().setId("SD1");
StructureDefinition sd2 = (StructureDefinition) new StructureDefinition().setId("SD2");
@@ -45,10 +51,10 @@ public class CachingValidationSupportTest {
return Collections.singletonList(responses.remove(0));
});
- CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts
+ final CachingValidationSupport.CacheTimeouts cacheTimeouts = CachingValidationSupport.CacheTimeouts
.defaultValues()
.setMiscMillis(1000);
- CachingValidationSupport support = new CachingValidationSupport(myValidationSupport, cacheTimeouts);
+ final CachingValidationSupport support = getSupport(cacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd);
assertEquals(3, responses.size());
List fetched = support.fetchAllNonBaseStructureDefinitions();
@@ -68,15 +74,21 @@ public class CachingValidationSupportTest {
assert fetched != null;
assertSame(sd1, fetched.get(0));
assertEquals(1, responses.size());
+
+ assertEquals(theIsEnabledValidationForCodingsLogicalAnd != null && theIsEnabledValidationForCodingsLogicalAnd,
+ support.isEnabledValidationForCodingsLogicalAnd());
}
- @Test
- public void fetchBinary_normally_accessesSuperOnlyOnce() {
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(booleans = {true, false})
+ public void fetchBinary_normally_accessesSuperOnlyOnce(Boolean theIsEnabledValidationForCodingsLogicalAnd) {
final byte[] EXPECTED_BINARY = "dummyBinaryContent".getBytes();
final String EXPECTED_BINARY_KEY = "dummyBinaryKey";
when(myValidationSupport.getFhirContext()).thenReturn(ourCtx);
when(myValidationSupport.fetchBinary(EXPECTED_BINARY_KEY)).thenReturn(EXPECTED_BINARY);
- CachingValidationSupport support = new CachingValidationSupport(myValidationSupport);
+
+ final CachingValidationSupport support = getSupport(null, theIsEnabledValidationForCodingsLogicalAnd);
final byte[] firstActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
assertEquals(EXPECTED_BINARY,firstActualBinary);
@@ -85,5 +97,25 @@ public class CachingValidationSupportTest {
final byte[] secondActualBinary = support.fetchBinary(EXPECTED_BINARY_KEY);
assertEquals(EXPECTED_BINARY,secondActualBinary);
verify(myValidationSupport, times(1)).fetchBinary(EXPECTED_BINARY_KEY);
+
+ assertEquals(theIsEnabledValidationForCodingsLogicalAnd != null && theIsEnabledValidationForCodingsLogicalAnd,
+ support.isEnabledValidationForCodingsLogicalAnd());
+ }
+
+ @Nonnull
+ private CachingValidationSupport getSupport(@Nullable CachingValidationSupport.CacheTimeouts theCacheTimeouts, @Nullable Boolean theIsEnabledValidationForCodingsLogicalAnd) {
+ if (theCacheTimeouts == null) {
+ if (theIsEnabledValidationForCodingsLogicalAnd == null) {
+ return new CachingValidationSupport(myValidationSupport);
+ }
+
+ return new CachingValidationSupport(myValidationSupport, theIsEnabledValidationForCodingsLogicalAnd);
+ }
+
+ if (theIsEnabledValidationForCodingsLogicalAnd == null) {
+ return new CachingValidationSupport(myValidationSupport, theCacheTimeouts);
+ }
+
+ return new CachingValidationSupport(myValidationSupport, theCacheTimeouts, theIsEnabledValidationForCodingsLogicalAnd);
}
}
diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java
index 35a121071c6..32d108e7448 100644
--- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java
+++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java
@@ -6,7 +6,6 @@ import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
-import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.test.BaseTest;
import ca.uhn.fhir.test.utilities.LoggingExtension;
@@ -124,6 +123,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
private Set myValidSystems = new HashSet<>();
private Map myStructureDefinitionMap = new HashMap<>();
private CachingValidationSupport myValidationSupport;
+ private IValidationSupport myMockSupport;
private void addValidConcept(String theSystem, String theCode) {
myValidSystems.add(theSystem);
@@ -149,26 +149,13 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
@SuppressWarnings("unchecked")
@BeforeEach
public void before() {
- myFhirValidator = ourCtx.newValidator();
- myFhirValidator.setValidateAgainstStandardSchema(false);
- myFhirValidator.setValidateAgainstStandardSchematron(false);
- // This is only used if the validation is performed with validationOptions.isConcurrentBundleValidation = true
- myFhirValidator.setExecutorService(Executors.newFixedThreadPool(4));
-
- IValidationSupport mockSupport = mock(IValidationSupport.class);
- when(mockSupport.getFhirContext()).thenReturn(ourCtx);
-
- ValidationSupportChain chain = new ValidationSupportChain(myDefaultValidationSupport, mockSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx), new SnapshotGeneratingValidationSupport(ourCtx));
- myValidationSupport = new CachingValidationSupport(chain);
- myInstanceVal = new FhirInstanceValidator(myValidationSupport);
-
- myFhirValidator.registerValidatorModule(myInstanceVal);
+ buildValidationSupportWithLogicalAndSupport(false);
mySupportedCodeSystemsForExpansion = new HashMap<>();
myValidConcepts = new ArrayList<>();
- when(mockSupport.expandValueSet(any(), nullable(ValueSetExpansionOptions.class), any(IBaseResource.class))).thenAnswer(t -> {
+ when(myMockSupport.expandValueSet(any(), nullable(ValueSetExpansionOptions.class), any(IBaseResource.class))).thenAnswer(t -> {
ValueSet arg = (ValueSet) t.getArgument(2, IBaseResource.class);
ValueSetExpansionComponent retVal = mySupportedCodeSystemsForExpansion.get(arg.getCompose().getIncludeFirstRep().getSystem());
if (retVal == null) {
@@ -181,7 +168,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
valueset.setExpansion(retVal);
return new ValueSetExpander.ValueSetExpansionOutcome(valueset);
});
- when(mockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() {
+ when(myMockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer() {
@Override
public Boolean answer(InvocationOnMock theInvocation) {
String argument = theInvocation.getArgument(1, String.class);
@@ -190,7 +177,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(mockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer() {
+ when(myMockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer() {
@Override
public IBaseResource answer(InvocationOnMock theInvocation) throws Throwable {
IBaseResource retVal;
@@ -207,7 +194,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(mockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() {
+ when(myMockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer() {
@Override
public IValidationSupport.CodeValidationResult answer(InvocationOnMock theInvocation) {
ConceptValidationOptions options = theInvocation.getArgument(1, ConceptValidationOptions.class);
@@ -227,7 +214,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(mockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer() {
+ when(myMockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer() {
@Override
public CodeSystem answer(InvocationOnMock theInvocation) {
String system = theInvocation.getArgument(0, String.class);
@@ -244,7 +231,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(mockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer() {
+ when(myMockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer() {
@Override
public IBaseResource answer(InvocationOnMock theInvocation) {
String id = (String) theInvocation.getArguments()[0];
@@ -258,7 +245,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(mockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer>() {
+ when(myMockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer>() {
@Override
public List answer(InvocationOnMock theInvocation) {
List retVal = new ArrayList<>(myDefaultValidationSupport.fetchAllStructureDefinitions());
@@ -267,7 +254,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return retVal;
}
});
- when(mockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> {
+ when(myMockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> {
String system = t.getArgument(1, String.class);
String code = t.getArgument(2, String.class);
if (myValidConcepts.contains(system + "___" + code)) {
@@ -276,7 +263,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
return null;
}
});
- when(mockSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> {
+ when(myMockSupport.validateCodeInValueSet(any(), any(), any(), any(), any(), any())).thenAnswer(t -> {
String system = t.getArgument(2, String.class);
String code = t.getArgument(3, String.class);
if (myValidConcepts.contains(system + "___" + code)) {
@@ -848,7 +835,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
public void testValidateProfileWithExtension() throws IOException, FHIRException {
PrePopulatedValidationSupport valSupport = new PrePopulatedValidationSupport(ourCtx);
DefaultProfileValidationSupport defaultSupport = new DefaultProfileValidationSupport(ourCtx);
- CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport, new InMemoryTerminologyServerValidationSupport(ourCtx)));
+ CachingValidationSupport support = new CachingValidationSupport(new ValidationSupportChain(defaultSupport, valSupport, new InMemoryTerminologyServerValidationSupport(ourCtx)), false);
// Prepopulate SDs
valSupport.addStructureDefinition(loadStructureDefinition(defaultSupport, "/r4/myconsent-profile.xml"));
@@ -1615,6 +1602,66 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
}
}
+ @Test
+ public void testPatientSingleCommunicationLanguage_en() throws IOException {
+ final String encoded = loadResource("patient-with-single-comm-lang-en.json");
+
+ final ValidationResult output = myFhirValidator.validateWithResult(encoded);
+ final List errors = logResultsAndReturnNonInformationalOnes(output);
+
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ public void testPatientSingleCommunicationLanguage_en_US_UNDERSCORE_config_false() throws IOException {
+ final String encoded = loadResource("patient-with-single-comm-lang-en_US-UNDERSCORE.json");
+
+ final ValidationResult output = myFhirValidator.validateWithResult(encoded);
+ final List errors = logResultsAndReturnNonInformationalOnes(output);
+
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ public void testPatientSingleCommunicationLanguage_en_US_DASH() throws IOException {
+ final String encoded = loadResource("patient-with-single-comm-lang-en-US-DASH.json");
+
+ final ValidationResult output = myFhirValidator.validateWithResult(encoded);
+ final List errors = logResultsAndReturnNonInformationalOnes(output);
+
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ public void testPatientMultipleCommunicationLanguages_en_US_and_en_UNDERSCORE_config_true() throws IOException {
+ buildValidationSupportWithLogicalAndSupport(true);
+ final String encoded = loadResource("patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json");
+
+ final ValidationResult output = myFhirValidator.validateWithResult(encoded);
+ final List errors = logResultsAndReturnNonInformationalOnes(output);
+
+ assertTrue(errors.isEmpty());
+ }
+
+ @Test
+ public void testPatientMultipleCommunicationLanguages_en_US_and_en_UNDERSCORE_config_false() throws IOException {
+ final String encoded = loadResource("patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json");
+
+ final ValidationResult output = myFhirValidator.validateWithResult(encoded);
+ final List errors = logResultsAndReturnNonInformationalOnes(output);
+
+ assertFalse(errors.isEmpty());
+ }
+
+ @Test
+ public void testPatientMultipleCommunicationLanguages_en_US_and_en_DASH() throws IOException {
+ final String encoded = loadResource("patient-with-multiple-comm-langs-en-US-and-en-DASH.json");
+
+ final ValidationResult output = myFhirValidator.validateWithResult(encoded);
+ final List errors = logResultsAndReturnNonInformationalOnes(output);
+ assertTrue(errors.isEmpty());
+ }
+
private Bundle buildBundle(int theSize, boolean theValidBundle) throws IOException {
BundleBuilder bundleBuilder = new BundleBuilder(ourCtx);
Patient p = ourCtx.newJsonParser().parseResource(Patient.class, loadResource("/r4/concurrent-bundle/patient.json"));
@@ -1656,5 +1703,19 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
TestUtil.randomizeLocaleAndTimezone();
}
+ private void buildValidationSupportWithLogicalAndSupport(boolean theLogicalAnd) {
+ myFhirValidator = ourCtx.newValidator();
+ myFhirValidator.setValidateAgainstStandardSchema(false);
+ myFhirValidator.setValidateAgainstStandardSchematron(false);
+ // This is only used if the validation is performed with validationOptions.isConcurrentBundleValidation = true
+ myFhirValidator.setExecutorService(Executors.newFixedThreadPool(4));
+
+ myMockSupport = mock(IValidationSupport.class);
+ when(myMockSupport.getFhirContext()).thenReturn(ourCtx);
+ ValidationSupportChain chain = new ValidationSupportChain(myDefaultValidationSupport, myMockSupport, new InMemoryTerminologyServerValidationSupport(ourCtx), new CommonCodeSystemsTerminologyService(ourCtx), new SnapshotGeneratingValidationSupport(ourCtx));
+ myValidationSupport = new CachingValidationSupport(chain, theLogicalAnd);
+ myInstanceVal = new FhirInstanceValidator(myValidationSupport);
+ myFhirValidator.registerValidatorModule(myInstanceVal);
+ }
}
diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json
new file mode 100644
index 00000000000..bcd0bf718c3
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-US-and-en-DASH.json
@@ -0,0 +1,31 @@
+{
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Doe",
+ "given": [
+ "Jane",
+ "Philip"
+ ]
+ }
+ ],
+ "birthDate": "1912-04-14",
+ "communication": [
+ {
+ "language": {
+ "coding": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en-US",
+ "display": "US English"
+ },
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en",
+ "display": "English"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json
new file mode 100644
index 00000000000..6423ce7a84f
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en-and-en_US-UNDERSCORE.json
@@ -0,0 +1,31 @@
+{
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Doe",
+ "given": [
+ "Jane",
+ "Philip"
+ ]
+ }
+ ],
+ "birthDate": "1912-04-14",
+ "communication": [
+ {
+ "language": {
+ "coding": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en",
+ "display": "English"
+ },
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en_US",
+ "display": "US English"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json
new file mode 100644
index 00000000000..65526ac8421
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/patient-with-multiple-comm-langs-en_US-and-en-UNDERSCORE.json
@@ -0,0 +1,31 @@
+{
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Doe",
+ "given": [
+ "Jane",
+ "Philip"
+ ]
+ }
+ ],
+ "birthDate": "1912-04-14",
+ "communication": [
+ {
+ "language": {
+ "coding": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en_US",
+ "display": "US English"
+ },
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en",
+ "display": "English"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json
new file mode 100644
index 00000000000..c169a6c379f
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en-US-DASH.json
@@ -0,0 +1,26 @@
+{
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Doe",
+ "given": [
+ "Jane",
+ "Philip"
+ ]
+ }
+ ],
+ "birthDate": "1912-04-14",
+ "communication": [
+ {
+ "language": {
+ "coding": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en-US",
+ "display": "US English"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json
new file mode 100644
index 00000000000..86fc6f27920
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en.json
@@ -0,0 +1,26 @@
+{
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Doe",
+ "given": [
+ "Jane",
+ "Philip"
+ ]
+ }
+ ],
+ "birthDate": "1912-04-14",
+ "communication": [
+ {
+ "language": {
+ "coding": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en",
+ "display": "English"
+ }
+ ]
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json
new file mode 100644
index 00000000000..c5ed811442e
--- /dev/null
+++ b/hapi-fhir-validation/src/test/resources/patient-with-single-comm-lang-en_US-UNDERSCORE.json
@@ -0,0 +1,26 @@
+{
+ "resourceType": "Patient",
+ "name": [
+ {
+ "family": "Doe",
+ "given": [
+ "Jane",
+ "Philip"
+ ]
+ }
+ ],
+ "birthDate": "1912-04-14",
+ "communication": [
+ {
+ "language": {
+ "coding": [
+ {
+ "system": "urn:ietf:bcp:47",
+ "code": "en_US",
+ "display": "US English"
+ }
+ ]
+ }
+ }
+ ]
+}