Introduce new logical AND boolean in CachingValidationSupport that influences Coding validation in VersionSpecificWorkerContextWrapper (#4433)

* Failing unit test with a combination of both communication language types plus two passing tests with each language type individually.

* Failing unit test with a combination of both communication language types plus two passing tests with each language type individually.

* Change patient name and birhdate.

* Latest unit test enhancements.

* Preliminary solution that seems to fix the bug and not break module unit tests.  Instead of VersionSpecificWorkerContextWrapper.validateCode() returning immediately upon the first OK validation, collect all the OKs and return the first if all codings are OK.

* Try another solution to output the correct error message without breaking other tests.

* Try a solution focused on a ValueSet with a languages URI.

* Working but messy config to toggle the AND/OR behaviour in VersionSpecificWorkerContextWrapper.validateCode().  Need to do a lot of cleanup, unit/integration tests and possibly documentation.

* Working but messy solution to toggle the AND/OR behaviour in VersionSpecificWorkerContextWrapper.validateCode().  Need to do a lot of cleanup, unit/integration tests and possibly documentation.

* Reverse changes that are not needed for the final solution.  Also, add a convenient method to ValidationSupportContext and call it.

* Update FhirInstanceValidatorR4Test to set the new logical AND flag.  Rename all references to the new flag.

* Revert all classes to master that were changed to call the new constructor.  Add new changelog.

* Revert other false param constructor calls.

* Add code reuse to ValidationSupportConfigUtil and increase test coverage for CachingValidationSupportTest.

* Code review feedback:  Clarify property description.  Fix parameter names.  Add extra check for empty codings.
This commit is contained in:
Luke deGruchy 2023-01-24 14:40:28 -05:00 committed by GitHub
parent 527a948285
commit 1755eb46e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 351 additions and 38 deletions

View File

@ -928,5 +928,15 @@ public interface IValidationSupport {
}
}
/**
* See VersionSpecificWorkerContextWrapper#validateCode in hapi-fhir-validation.
* <p>
* 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;
}
}

View File

@ -42,4 +42,8 @@ public class ValidationSupportContext {
public Set<String> getCurrentlyGeneratingSnapshots() {
return myCurrentlyGeneratingSnapshots;
}
public boolean isEnabledValidationForCodingsLogicalAnd() {
return myRootValidationSupport.isEnabledValidationForCodingsLogicalAnd();
}
}

View File

@ -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."

View File

@ -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);
}
}

View File

@ -45,6 +45,7 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
private final ThreadPoolExecutor myBackgroundExecutor;
private final Map<Object, Object> myNonExpiringCache;
private final Cache<String, Object> 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;
}
}

View File

@ -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<ValidationResult> 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);
}

View File

@ -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<IBaseResource> 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);
}
}

View File

@ -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<String> myValidSystems = new HashSet<>();
private Map<String, StructureDefinition> 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<Boolean>() {
when(myMockSupport.isCodeSystemSupported(any(), nullable(String.class))).thenAnswer(new Answer<Boolean>() {
@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<IBaseResource>() {
when(myMockSupport.fetchResource(nullable(Class.class), nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
@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<IValidationSupport.CodeValidationResult>() {
when(myMockSupport.validateCode(any(), any(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenAnswer(new Answer<IValidationSupport.CodeValidationResult>() {
@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<CodeSystem>() {
when(myMockSupport.fetchCodeSystem(nullable(String.class))).thenAnswer(new Answer<CodeSystem>() {
@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<IBaseResource>() {
when(myMockSupport.fetchStructureDefinition(nullable(String.class))).thenAnswer(new Answer<IBaseResource>() {
@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<List<StructureDefinition>>() {
when(myMockSupport.fetchAllStructureDefinitions()).thenAnswer(new Answer<List<StructureDefinition>>() {
@Override
public List<StructureDefinition> answer(InvocationOnMock theInvocation) {
List<StructureDefinition> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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<SingleValidationMessage> 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);
}
}

View File

@ -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"
}
]
}
}
]
}

View File

@ -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"
}
]
}
}
]
}

View File

@ -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"
}
]
}
}
]
}

View File

@ -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"
}
]
}
}
]
}

View File

@ -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"
}
]
}
}
]
}

View File

@ -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"
}
]
}
}
]
}