• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.healthconnect.phr.validations;
18 
19 import static android.healthconnect.cts.phr.utils.ObservationBuilder.LOINC;
20 import static android.healthconnect.cts.phr.utils.ObservationBuilder.ObservationCategory.LABORATORY;
21 import static android.healthconnect.cts.phr.utils.ObservationBuilder.ObservationCategory.SOCIAL_HISTORY;
22 import static android.healthconnect.cts.phr.utils.ObservationBuilder.ObservationCategory.VITAL_SIGNS;
23 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.BEATS_PER_MINUTE;
24 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.BREATHS_PER_MINUTE;
25 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.CELSIUS;
26 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.CENTIMETERS;
27 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.KILOGRAMS;
28 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.KILOGRAMS_PER_M2;
29 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.MILLIMETERS_OF_MERCURY;
30 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.PERCENT;
31 import static android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits.POUNDS;
32 import static android.healthconnect.cts.phr.utils.ObservationBuilder.SNOMED_CT;
33 import static android.healthconnect.cts.phr.utils.ObservationBuilder.makeCodeableConcept;
34 import static android.healthconnect.cts.phr.utils.PhrDataFactory.DATA_SOURCE_ID;
35 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_ALLERGY;
36 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_IMMUNIZATION;
37 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_IMMUNIZATION_FIELD_MISSING_INVALID;
38 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_IMMUNIZATION_ID_EMPTY;
39 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_IMMUNIZATION_ID_NOT_EXISTS;
40 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_IMMUNIZATION_RESOURCE_TYPE_NOT_EXISTS;
41 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_DATA_IMMUNIZATION_UNSUPPORTED_RESOURCE_TYPE;
42 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_RESOURCE_ID_IMMUNIZATION;
43 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_RESOURCE_TYPE_UNSUPPORTED;
44 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_VERSION_R4;
45 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_VERSION_R4B;
46 import static android.healthconnect.cts.phr.utils.PhrDataFactory.FHIR_VERSION_UNSUPPORTED;
47 import static android.healthconnect.cts.phr.utils.PhrDataFactory.getUpsertMedicalResourceRequest;
48 import static android.healthconnect.cts.phr.utils.PhrDataFactory.getUpsertMedicalResourceRequestBuilder;
49 
50 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD;
51 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD_DATABASE;
52 import static com.android.healthfitness.flags.Flags.FLAG_PHR_FHIR_STRUCTURAL_VALIDATION;
53 
54 import static com.google.common.truth.Truth.assertThat;
55 
56 import static org.junit.Assert.assertThrows;
57 
58 import android.health.connect.UpsertMedicalResourceRequest;
59 import android.health.connect.datatypes.FhirResource;
60 import android.health.connect.datatypes.MedicalResource;
61 import android.healthconnect.cts.phr.utils.ConditionBuilder;
62 import android.healthconnect.cts.phr.utils.EncountersBuilder;
63 import android.healthconnect.cts.phr.utils.ImmunizationBuilder;
64 import android.healthconnect.cts.phr.utils.MedicationsBuilder;
65 import android.healthconnect.cts.phr.utils.ObservationBuilder;
66 import android.healthconnect.cts.phr.utils.ObservationBuilder.QuantityUnits;
67 import android.healthconnect.cts.phr.utils.PatientBuilder;
68 import android.healthconnect.cts.phr.utils.PractitionerBuilder;
69 import android.healthconnect.cts.phr.utils.ProcedureBuilder;
70 import android.platform.test.annotations.EnableFlags;
71 import android.platform.test.flag.junit.SetFlagsRule;
72 
73 import com.android.server.healthconnect.storage.request.UpsertMedicalResourceInternalRequest;
74 
75 import com.google.testing.junit.testparameterinjector.TestParameter;
76 import com.google.testing.junit.testparameterinjector.TestParameterInjector;
77 
78 import org.json.JSONArray;
79 import org.json.JSONException;
80 import org.json.JSONObject;
81 import org.junit.Rule;
82 import org.junit.Test;
83 import org.junit.runner.RunWith;
84 
85 import java.util.List;
86 
87 @EnableFlags({
88     FLAG_PERSONAL_HEALTH_RECORD,
89     FLAG_PERSONAL_HEALTH_RECORD_DATABASE,
90     FLAG_PHR_FHIR_STRUCTURAL_VALIDATION
91 })
92 @RunWith(TestParameterInjector.class)
93 public class MedicalResourceValidatorTest {
94     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
95 
96     private FhirResourceValidator mFhirResourceValidator = null;
97 
98     @Test
testValidateAndCreateInternalRequest_validAndR4_populatesInternalRequest()99     public void testValidateAndCreateInternalRequest_validAndR4_populatesInternalRequest() {
100         UpsertMedicalResourceRequest upsertRequest =
101                 new UpsertMedicalResourceRequest.Builder(
102                                 DATA_SOURCE_ID, FHIR_VERSION_R4, FHIR_DATA_IMMUNIZATION)
103                         .build();
104         UpsertMedicalResourceInternalRequest expected =
105                 new UpsertMedicalResourceInternalRequest()
106                         .setDataSourceId(DATA_SOURCE_ID)
107                         .setMedicalResourceType(MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES)
108                         .setFhirResourceType(FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION)
109                         .setFhirResourceId(FHIR_RESOURCE_ID_IMMUNIZATION)
110                         .setFhirVersion(FHIR_VERSION_R4)
111                         .setData(FHIR_DATA_IMMUNIZATION);
112 
113         MedicalResourceValidator validator =
114                 new MedicalResourceValidator(upsertRequest, getFhirResourceValidator());
115         UpsertMedicalResourceInternalRequest validatedRequest =
116                 validator.validateAndCreateInternalRequest();
117 
118         assertThat(validatedRequest).isEqualTo(expected);
119     }
120 
121     @Test
testValidateAndCreateInternalRequest_validAndR4B_populatesInternalRequest()122     public void testValidateAndCreateInternalRequest_validAndR4B_populatesInternalRequest() {
123         UpsertMedicalResourceRequest upsertRequest =
124                 new UpsertMedicalResourceRequest.Builder(
125                                 DATA_SOURCE_ID, FHIR_VERSION_R4B, FHIR_DATA_IMMUNIZATION)
126                         .build();
127         UpsertMedicalResourceInternalRequest expected =
128                 new UpsertMedicalResourceInternalRequest()
129                         .setDataSourceId(DATA_SOURCE_ID)
130                         .setMedicalResourceType(MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES)
131                         .setFhirResourceType(FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION)
132                         .setFhirResourceId(FHIR_RESOURCE_ID_IMMUNIZATION)
133                         .setFhirVersion(FHIR_VERSION_R4B)
134                         .setData(FHIR_DATA_IMMUNIZATION);
135 
136         MedicalResourceValidator validator =
137                 new MedicalResourceValidator(upsertRequest, getFhirResourceValidator());
138         UpsertMedicalResourceInternalRequest validatedRequest =
139                 validator.validateAndCreateInternalRequest();
140 
141         assertThat(validatedRequest).isEqualTo(expected);
142     }
143 
144     @Test
testConstructor_nullValidator_succeeds()145     public void testConstructor_nullValidator_succeeds() {
146         new MedicalResourceValidator(getUpsertMedicalResourceRequest(), null);
147     }
148 
149     @Test
testValidateAndCreateInternalRequest_nullValidator_succeeds()150     public void testValidateAndCreateInternalRequest_nullValidator_succeeds() {
151         UpsertMedicalResourceRequest upsertRequest =
152                 new UpsertMedicalResourceRequest.Builder(
153                                 DATA_SOURCE_ID, FHIR_VERSION_R4, FHIR_DATA_IMMUNIZATION)
154                         .build();
155         UpsertMedicalResourceInternalRequest expected =
156                 new UpsertMedicalResourceInternalRequest()
157                         .setDataSourceId(DATA_SOURCE_ID)
158                         .setMedicalResourceType(MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES)
159                         .setFhirResourceType(FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION)
160                         .setFhirResourceId(FHIR_RESOURCE_ID_IMMUNIZATION)
161                         .setFhirVersion(FHIR_VERSION_R4)
162                         .setData(FHIR_DATA_IMMUNIZATION);
163 
164         MedicalResourceValidator validator = new MedicalResourceValidator(upsertRequest, null);
165         UpsertMedicalResourceInternalRequest validatedRequest =
166                 validator.validateAndCreateInternalRequest();
167 
168         assertThat(validatedRequest).isEqualTo(expected);
169     }
170 
171     @Test
testValidateAndCreateInternalRequest_nullValidator_unknownFieldSucceeds()172     public void testValidateAndCreateInternalRequest_nullValidator_unknownFieldSucceeds() {
173         String immunizationWithUnknownField =
174                 new ImmunizationBuilder()
175                         .setId(FHIR_RESOURCE_ID_IMMUNIZATION)
176                         .set("unknown", "test")
177                         .toJson();
178         UpsertMedicalResourceRequest upsertRequest =
179                 new UpsertMedicalResourceRequest.Builder(
180                                 DATA_SOURCE_ID, FHIR_VERSION_R4, immunizationWithUnknownField)
181                         .build();
182         UpsertMedicalResourceInternalRequest expected =
183                 new UpsertMedicalResourceInternalRequest()
184                         .setDataSourceId(DATA_SOURCE_ID)
185                         .setMedicalResourceType(MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES)
186                         .setFhirResourceType(FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION)
187                         .setFhirResourceId(FHIR_RESOURCE_ID_IMMUNIZATION)
188                         .setFhirVersion(FHIR_VERSION_R4)
189                         .setData(immunizationWithUnknownField);
190 
191         MedicalResourceValidator validator = new MedicalResourceValidator(upsertRequest, null);
192         UpsertMedicalResourceInternalRequest validatedRequest =
193                 validator.validateAndCreateInternalRequest();
194 
195         assertThat(validatedRequest).isEqualTo(expected);
196     }
197 
198     @Test
testValidateAndCreateInternalRequest_nonNullValidator_unknownFieldThrows()199     public void testValidateAndCreateInternalRequest_nonNullValidator_unknownFieldThrows() {
200         String immunizationWithUnknownField =
201                 new ImmunizationBuilder().set("unknown", "test").toJson();
202 
203         UpsertMedicalResourceRequest request =
204                 new UpsertMedicalResourceRequest.Builder(
205                                 DATA_SOURCE_ID, FHIR_VERSION_R4, immunizationWithUnknownField)
206                         .build();
207         MedicalResourceValidator validator =
208                 new MedicalResourceValidator(request, getFhirResourceValidator());
209 
210         Throwable thrown =
211                 assertThrows(
212                         IllegalArgumentException.class,
213                         () -> validator.validateAndCreateInternalRequest());
214         assertThat(thrown).hasMessageThat().contains("Found unexpected field ");
215     }
216 
217     @Test
testValidateAndCreateInternalRequest_fhirResourceWithoutType_throws()218     public void testValidateAndCreateInternalRequest_fhirResourceWithoutType_throws() {
219         MedicalResourceValidator validator =
220                 makeValidator(FHIR_DATA_IMMUNIZATION_RESOURCE_TYPE_NOT_EXISTS);
221 
222         var thrown =
223                 assertThrows(
224                         IllegalArgumentException.class,
225                         validator::validateAndCreateInternalRequest);
226         assertThat(thrown)
227                 .hasMessageThat()
228                 .contains(
229                         "Missing resourceType field for resource with id "
230                                 + FHIR_RESOURCE_ID_IMMUNIZATION);
231     }
232 
233     @Test
testValidateAndCreateInternalRequest_fhirResourceWithoutId_throws()234     public void testValidateAndCreateInternalRequest_fhirResourceWithoutId_throws() {
235         MedicalResourceValidator validator = makeValidator(FHIR_DATA_IMMUNIZATION_ID_NOT_EXISTS);
236 
237         var thrown =
238                 assertThrows(
239                         IllegalArgumentException.class,
240                         validator::validateAndCreateInternalRequest);
241         assertThat(thrown).hasMessageThat().contains("Resource is missing id field");
242     }
243 
244     @Test
testValidateAndCreateInternalRequest_nullValidatorMissingId_throws()245     public void testValidateAndCreateInternalRequest_nullValidatorMissingId_throws() {
246         UpsertMedicalResourceRequest request =
247                 new UpsertMedicalResourceRequest.Builder(
248                                 DATA_SOURCE_ID,
249                                 FHIR_VERSION_R4,
250                                 FHIR_DATA_IMMUNIZATION_ID_NOT_EXISTS)
251                         .build();
252         MedicalResourceValidator validator = new MedicalResourceValidator(request, null);
253 
254         var thrown =
255                 assertThrows(
256                         IllegalArgumentException.class,
257                         validator::validateAndCreateInternalRequest);
258         assertThat(thrown).hasMessageThat().contains("Resource is missing id field");
259     }
260 
261     @Test
testValidateAndCreateInternalRequest_invalidJson_throws()262     public void testValidateAndCreateInternalRequest_invalidJson_throws() {
263         MedicalResourceValidator validator =
264                 makeValidator(FHIR_DATA_IMMUNIZATION_FIELD_MISSING_INVALID);
265 
266         var thrown =
267                 assertThrows(
268                         IllegalArgumentException.class,
269                         validator::validateAndCreateInternalRequest);
270         assertThat(thrown).hasMessageThat().contains("invalid json");
271     }
272 
273     @Test
testValidateAndCreateInternalRequest_emptyId_throws()274     public void testValidateAndCreateInternalRequest_emptyId_throws() {
275         MedicalResourceValidator validator = makeValidator(FHIR_DATA_IMMUNIZATION_ID_EMPTY);
276 
277         var thrown =
278                 assertThrows(
279                         IllegalArgumentException.class,
280                         validator::validateAndCreateInternalRequest);
281         assertThat(thrown).hasMessageThat().contains("id cannot be empty");
282     }
283 
284     @Test
testValidateAndCreateInternalRequest_nullId_throws()285     public void testValidateAndCreateInternalRequest_nullId_throws() {
286         String immunizationJson = new ImmunizationBuilder().set("id", JSONObject.NULL).toJson();
287         MedicalResourceValidator validator = makeValidator(immunizationJson);
288 
289         var thrown =
290                 assertThrows(
291                         IllegalArgumentException.class,
292                         validator::validateAndCreateInternalRequest);
293         assertThat(thrown).hasMessageThat().contains("id should be a string");
294     }
295 
296     @Test
testValidateAndCreateInternalRequest_nonStringId_throws()297     public void testValidateAndCreateInternalRequest_nonStringId_throws() {
298         String immunizationJson = new ImmunizationBuilder().set("id", 123).toJson();
299         MedicalResourceValidator validator = makeValidator(immunizationJson);
300 
301         var thrown =
302                 assertThrows(
303                         IllegalArgumentException.class,
304                         validator::validateAndCreateInternalRequest);
305         assertThat(thrown).hasMessageThat().contains("id should be a string");
306     }
307 
308     @Test
testValidateAndCreateInternalRequest_nullString_succeeds()309     public void testValidateAndCreateInternalRequest_nullString_succeeds() {
310         String immunizationJson = new ImmunizationBuilder().set("id", "null").toJson();
311         MedicalResourceValidator validator = makeValidator(immunizationJson);
312 
313         UpsertMedicalResourceInternalRequest internalRequest =
314                 validator.validateAndCreateInternalRequest();
315 
316         assertThat(internalRequest.getFhirResourceId()).isEqualTo("null");
317     }
318 
319     @Test
testValidateAndCreateInternalRequest_unsupportedFhirVersion_throws()320     public void testValidateAndCreateInternalRequest_unsupportedFhirVersion_throws() {
321         UpsertMedicalResourceRequest upsertRequest =
322                 getUpsertMedicalResourceRequestBuilder()
323                         .setFhirVersion(FHIR_VERSION_UNSUPPORTED)
324                         .build();
325         MedicalResourceValidator validator =
326                 new MedicalResourceValidator(upsertRequest, getFhirResourceValidator());
327 
328         var thrown =
329                 assertThrows(
330                         IllegalArgumentException.class,
331                         validator::validateAndCreateInternalRequest);
332         assertThat(thrown)
333                 .hasMessageThat()
334                 .contains(
335                         "Unsupported FHIR version "
336                                 + FHIR_VERSION_UNSUPPORTED
337                                 + " for resource with id "
338                                 + FHIR_RESOURCE_ID_IMMUNIZATION);
339     }
340 
341     @Test
testValidateAndCreateInternalRequest_unsupportedResourceType_throws()342     public void testValidateAndCreateInternalRequest_unsupportedResourceType_throws() {
343         MedicalResourceValidator validator =
344                 makeValidator(FHIR_DATA_IMMUNIZATION_UNSUPPORTED_RESOURCE_TYPE);
345 
346         var thrown =
347                 assertThrows(
348                         IllegalArgumentException.class,
349                         validator::validateAndCreateInternalRequest);
350         assertThat(thrown)
351                 .hasMessageThat()
352                 .contains(
353                         "Unsupported FHIR resource type "
354                                 + FHIR_RESOURCE_TYPE_UNSUPPORTED
355                                 + " for resource with id "
356                                 + FHIR_RESOURCE_ID_IMMUNIZATION);
357     }
358 
359     @Test
testValidateAndCreateInternalRequest_nullResourceType_throws()360     public void testValidateAndCreateInternalRequest_nullResourceType_throws() {
361         String immunizationJson =
362                 new ImmunizationBuilder()
363                         .setId(FHIR_RESOURCE_ID_IMMUNIZATION)
364                         .set("resourceType", JSONObject.NULL)
365                         .toJson();
366         MedicalResourceValidator validator = makeValidator(immunizationJson);
367 
368         var thrown =
369                 assertThrows(
370                         IllegalArgumentException.class,
371                         validator::validateAndCreateInternalRequest);
372         assertThat(thrown)
373                 .hasMessageThat()
374                 .contains(
375                         "Unsupported FHIR resource type null for resource with id "
376                                 + FHIR_RESOURCE_ID_IMMUNIZATION);
377     }
378 
379     @Test
testValidateAndCreateInternalRequest_containedResourceInResource_throws()380     public void testValidateAndCreateInternalRequest_containedResourceInResource_throws() {
381         String resourceId = "id1";
382         String medicationStatementWithContainedResource =
383                 new MedicationsBuilder.MedicationStatementR4Builder()
384                         .setId(resourceId)
385                         .setContainedMedication(new MedicationsBuilder.MedicationBuilder())
386                         .toJson();
387         MedicalResourceValidator validator =
388                 makeValidator(medicationStatementWithContainedResource);
389 
390         var thrown =
391                 assertThrows(
392                         IllegalArgumentException.class,
393                         validator::validateAndCreateInternalRequest);
394         assertThat(thrown)
395                 .hasMessageThat()
396                 .contains(
397                         "Contained resources are not supported. Found contained resource for"
398                                 + " resource with id "
399                                 + resourceId);
400     }
401 
402     @Test
testValidateAndCreateInternalRequest_containedFieldNotArray_throws()403     public void testValidateAndCreateInternalRequest_containedFieldNotArray_throws()
404             throws JSONException {
405         String resourceId = "id1";
406         String medicationStatementWithInvalidContained =
407                 new MedicationsBuilder.MedicationStatementR4Builder()
408                         .setId(resourceId)
409                         .set("contained", new JSONObject("{}"))
410                         .toJson();
411         MedicalResourceValidator validator = makeValidator(medicationStatementWithInvalidContained);
412 
413         var thrown =
414                 assertThrows(
415                         IllegalArgumentException.class,
416                         validator::validateAndCreateInternalRequest);
417         assertThat(thrown)
418                 .hasMessageThat()
419                 .contains(
420                         "Contained resources are not supported. Found contained field for resource"
421                                 + " with id "
422                                 + resourceId);
423     }
424 
425     @Test
testCalculateMedicalResourceType_allergy()426     public void testCalculateMedicalResourceType_allergy() {
427         MedicalResourceValidator validator = makeValidator(FHIR_DATA_ALLERGY);
428 
429         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
430 
431         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES);
432     }
433 
434     @Test
testCalculateMedicalResourceType_condition()435     public void testCalculateMedicalResourceType_condition() {
436         String fhirData = new ConditionBuilder().toJson();
437         MedicalResourceValidator validator = makeValidator(fhirData);
438 
439         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
440 
441         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_CONDITIONS);
442     }
443 
444     @Test
testCalculateMedicalResourceType_procedure()445     public void testCalculateMedicalResourceType_procedure() {
446         String fhirData = new ProcedureBuilder().toJson();
447         MedicalResourceValidator validator = makeValidator(fhirData);
448 
449         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
450 
451         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PROCEDURES);
452     }
453 
454     @Test
testCalculateMedicalResourceType_medication()455     public void testCalculateMedicalResourceType_medication() {
456         String fhirData = MedicationsBuilder.medication().toJson();
457         MedicalResourceValidator validator = makeValidator(fhirData);
458 
459         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
460 
461         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_MEDICATIONS);
462     }
463 
464     @Test
testCalculateMedicalResourceType_medicationStatement()465     public void testCalculateMedicalResourceType_medicationStatement() {
466         String fhirData = MedicationsBuilder.statementR4().toJson();
467         MedicalResourceValidator validator = makeValidator(fhirData);
468 
469         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
470 
471         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_MEDICATIONS);
472     }
473 
474     @Test
testCalculateMedicalResourceType_medicationRequest()475     public void testCalculateMedicalResourceType_medicationRequest() {
476         String fhirData = MedicationsBuilder.request().toJson();
477         MedicalResourceValidator validator = makeValidator(fhirData);
478 
479         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
480 
481         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_MEDICATIONS);
482     }
483 
484     @Test
testCalculateMedicalResourceType_patient()485     public void testCalculateMedicalResourceType_patient() {
486         String fhirData = new PatientBuilder().toJson();
487         MedicalResourceValidator validator = makeValidator(fhirData);
488 
489         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
490 
491         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PERSONAL_DETAILS);
492     }
493 
494     @Test
testCalculateMedicalResourceType_practitioner()495     public void testCalculateMedicalResourceType_practitioner() {
496         String fhirData = new PractitionerBuilder().toJson();
497         MedicalResourceValidator validator = makeValidator(fhirData);
498 
499         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
500 
501         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PRACTITIONER_DETAILS);
502     }
503 
504     @Test
testCalculateMedicalResourceType_practitionerRole()505     public void testCalculateMedicalResourceType_practitionerRole() {
506         String fhirData = PractitionerBuilder.role().toJson();
507         MedicalResourceValidator validator = makeValidator(fhirData);
508 
509         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
510 
511         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PRACTITIONER_DETAILS);
512     }
513 
514     @Test
testCalculateMedicalResourceType_encounter()515     public void testCalculateMedicalResourceType_encounter() {
516         String fhirData = EncountersBuilder.encounter().toJson();
517         MedicalResourceValidator validator = makeValidator(fhirData);
518 
519         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
520 
521         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VISITS);
522     }
523 
524     @Test
testCalculateMedicalResourceType_location()525     public void testCalculateMedicalResourceType_location() {
526         String fhirData = EncountersBuilder.location().toJson();
527         MedicalResourceValidator validator = makeValidator(fhirData);
528 
529         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
530 
531         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VISITS);
532     }
533 
534     @Test
testCalculateMedicalResourceType_organization()535     public void testCalculateMedicalResourceType_organization() {
536         String fhirData = EncountersBuilder.organization().toJson();
537         MedicalResourceValidator validator = makeValidator(fhirData);
538 
539         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
540 
541         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VISITS);
542     }
543 
544     // IPS artifacts: https://build.fhir.org/ig/HL7/fhir-ips/artifacts.html
545     // pregnancy outcome,  status and expected delivery date.
546     enum PregnancyStatusTestValue {
547         PREGNANT("82810-3", "77386006"), // Pregnancy status, pregnant
548         NOT_PREGNANT("82810-3", "60001007"); // Pregnancy status, not pregnant
549         private final String mCode;
550         private final String mValue;
551 
PregnancyStatusTestValue(String code, String value)552         PregnancyStatusTestValue(String code, String value) {
553             mCode = code;
554             mValue = value;
555         }
556     }
557 
558     enum PregnancyOutcomeTestValue {
559         LIVE_BIRTH("11636-8", 1),
560         NO_LIVE_BIRTH("11636-8", 0),
561         PRETERM("11637-6", 1),
562         NO_PRETERM("11637-6", 0),
563         STILL_LIVING("11638-4", 1),
564         NO_STILL_LIVING("11638-4", 0),
565         TERM("11639-2", 1),
566         NO_TERM("11639-2", 0),
567         TOTAL("11640-0", 1),
568         NO_TOTAL("11640-0", 0),
569         ABORTIONS("11612-9", 1),
570         NO_ABORTIONS("11612-9", 0),
571         INDUCED_ABORTION("11613-7", 1),
572         NO_INDUCED_ABORTIONS("11613-7", 0),
573         SPONTANEOUS_ABORTION("11614-5", 1),
574         NO_SPONTANEOUS_ABORTIONS("11614-5", 0),
575         ECTOPIC_PREGNANCY("33065-4", 1),
576         NO_ECTOPIC_PREGNANCIES("33065-4", 0),
577         ;
578         private final String mCode;
579         private final int mCount;
580 
PregnancyOutcomeTestValue(String code, int count)581         PregnancyOutcomeTestValue(String code, int count) {
582             mCode = code;
583             mCount = count;
584         }
585     }
586 
587     enum SmokingTestValue {
588         FORMER_SMOKER_LOINC("72166-2", makeCodeableConcept(LOINC, "LA15920-4", "Former smoker")),
589         // https://build.fhir.org/ig/HL7/fhir-ips/ValueSet-current-smoking-status-uv-ips.html
590         DAILY(
591                 "72166-2",
592                 makeCodeableConcept(SNOMED_CT, "449868002", "Smokes tobacco daily (finding)")),
593         OCCASIONAL(
594                 "72166-2",
595                 makeCodeableConcept(
596                         SNOMED_CT, "428041000124106", "Occasional tobacco smoker (finding)")),
597         EX_SMOKER("72166-2", makeCodeableConcept(SNOMED_CT, "8517006", "Ex-smoker (finding)")),
598         NEVER(
599                 "72166-2",
600                 makeCodeableConcept(SNOMED_CT, "266919005", "Never smoked tobacco (finding)")),
601         SMOKER("72166-2", makeCodeableConcept(SNOMED_CT, "77176002", "Smoker (finding)")),
602         UNKNOWN(
603                 "72166-2",
604                 makeCodeableConcept(
605                         SNOMED_CT, "266927001", "Tobacco smoking consumption unknown (finding)")),
606         HEAVY(
607                 "72166-2",
608                 makeCodeableConcept(SNOMED_CT, "230063004", "Heavy cigarette Smoker (finding)")),
609         LIGHT(
610                 "72166-2",
611                 makeCodeableConcept(SNOMED_CT, "230060001", "Light cigarette Smoker (finding)")),
612         ;
613         private final String mCode;
614         private final JSONObject mValueCodeableConcept;
615 
SmokingTestValue(String code, JSONObject value)616         SmokingTestValue(String code, JSONObject value) {
617             mCode = code;
618             mValueCodeableConcept = value;
619         }
620     }
621 
622     enum VitalSignsTestValue {
623         // https://hl7.org/fhir/R5/observation-vitalsigns.html
624         SAT_O2("2708-6", 95, PERCENT),
625         RESPIRATORY_RATE("9279-1", 26, BREATHS_PER_MINUTE),
626         HEART_RATE("8867-4", 100, BEATS_PER_MINUTE),
627         BODY_TEMPERATURE("8867-4", 36.4, CELSIUS),
628         BODY_HEIGHT("8302-2", 152, CENTIMETERS),
629         HEAD_CIRCUMFERENCE("9843-4", 51.2, CENTIMETERS),
630         BODY_WEIGHT_LBS("29463-7", 185, POUNDS),
631         BODY_WEIGHT_KG("29463-7", 77, KILOGRAMS),
632         BODY_MASS_INDEX("39156-5", 16.2, KILOGRAMS_PER_M2),
633         SYSTOLIC_BLOOD_PRESSURE("8480-6", 26, MILLIMETERS_OF_MERCURY),
634         DIASTOLIC_BLOOD_PRESSURE("8462-4", 26, MILLIMETERS_OF_MERCURY),
635         ;
636         private final String mCode;
637         private final Number mValue;
638         private final QuantityUnits mUnits;
639 
VitalSignsTestValue(String code, Number value, QuantityUnits units)640         VitalSignsTestValue(String code, Number value, QuantityUnits units) {
641             mCode = code;
642             mValue = value;
643             mUnits = units;
644         }
645     }
646 
647     @Test
testCalculateMedicalResourceType_pregnancyStatus_pregnancy( @estParameter PregnancyStatusTestValue testValue)648     public void testCalculateMedicalResourceType_pregnancyStatus_pregnancy(
649             @TestParameter PregnancyStatusTestValue testValue) {
650         String fhirData =
651                 new ObservationBuilder()
652                         .setCode(LOINC, testValue.mCode)
653                         .setValueCodeableConcept(SNOMED_CT, testValue.mValue)
654                         .toJson();
655         MedicalResourceValidator validator = makeValidator(fhirData);
656 
657         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
658 
659         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PREGNANCY);
660     }
661 
662     @Test
testCalculateMedicalResourceType_pregnancyOutcome_pregnancy( @estParameter PregnancyOutcomeTestValue testValue)663     public void testCalculateMedicalResourceType_pregnancyOutcome_pregnancy(
664             @TestParameter PregnancyOutcomeTestValue testValue) {
665         String fhirData =
666                 new ObservationBuilder()
667                         .setCode(LOINC, testValue.mCode)
668                         .setValueQuantity(testValue.mCount, QuantityUnits.COUNT)
669                         .toJson();
670         MedicalResourceValidator validator = makeValidator(fhirData);
671 
672         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
673 
674         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PREGNANCY);
675     }
676 
677     @Test
testCalculateMedicalResourceType_expectedDeliveryDate_pregnancy( @estParameter{"11778-8", "11779-6", "11780-4"}) String code)678     public void testCalculateMedicalResourceType_expectedDeliveryDate_pregnancy(
679             @TestParameter({"11778-8", "11779-6", "11780-4"}) String code) {
680         // https://build.fhir.org/ig/HL7/fhir-ips/ValueSet-edd-method-uv-ips.html
681         String fhirData =
682                 new ObservationBuilder()
683                         .setCode(LOINC, code)
684                         .removeAllEffectiveMultiTypeFields()
685                         .set("effectiveDateTime", "2021-04-20")
686                         .removeAllValueMultiTypeFields()
687                         .set("valueDateTime", "2021-08-07")
688                         .toJson();
689         MedicalResourceValidator validator = makeValidator(fhirData);
690 
691         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
692 
693         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PREGNANCY);
694     }
695 
696     @Test
testCalculateMedicalResourceType_categorySocialHistory_socialHistory()697     public void testCalculateMedicalResourceType_categorySocialHistory_socialHistory() {
698         String fhirData = new ObservationBuilder().setCategory(SOCIAL_HISTORY).toJson();
699         MedicalResourceValidator validator = makeValidator(fhirData);
700 
701         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
702 
703         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY);
704     }
705 
706     @Test
testCalculateMedicalResourceType_smoking_socialHistory( @estParameter SmokingTestValue value)707     public void testCalculateMedicalResourceType_smoking_socialHistory(
708             @TestParameter SmokingTestValue value) {
709         // https://build.fhir.org/ig/HL7/fhir-ips/StructureDefinition-Observation-tobaccouse-uv-ips.html
710         String fhirData =
711                 new ObservationBuilder()
712                         .setCode(LOINC, value.mCode)
713                         .removeAllValueMultiTypeFields()
714                         .set("valueCodeableConcept", value.mValueCodeableConcept)
715                         .toJson();
716         MedicalResourceValidator validator = makeValidator(fhirData);
717 
718         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
719 
720         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY);
721     }
722 
723     @Test
testCalculateMedicalResourceType_ipsAlcohol_socialHistory()724     public void testCalculateMedicalResourceType_ipsAlcohol_socialHistory() throws JSONException {
725         // https://build.fhir.org/ig/HL7/fhir-ips/StructureDefinition-Observation-alcoholuse-uv-ips.html
726         String fhirData =
727                 new ObservationBuilder()
728                         .setCode(LOINC, "74013-4")
729                         .setValueQuantity(2, QuantityUnits.GLASSES_OF_WINE_PER_DAY)
730                         .toJson();
731         MedicalResourceValidator validator = makeValidator(fhirData);
732 
733         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
734 
735         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY);
736     }
737 
738     @Test
testCalculateMedicalResourceType_categoryVitals_vitalSigns()739     public void testCalculateMedicalResourceType_categoryVitals_vitalSigns() {
740         String fhirData = new ObservationBuilder().setCategory(VITAL_SIGNS).toJson();
741         MedicalResourceValidator validator = makeValidator(fhirData);
742 
743         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
744 
745         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VITAL_SIGNS);
746     }
747 
748     @Test
testCalculateMedicalResourceType_vitalSigns_vitalSigns( @estParameter VitalSignsTestValue value)749     public void testCalculateMedicalResourceType_vitalSigns_vitalSigns(
750             @TestParameter VitalSignsTestValue value) {
751         // From https://hl7.org/fhir/R5/observation-vitalsigns.html
752         String fhirData =
753                 new ObservationBuilder()
754                         .setCode(LOINC, value.mCode)
755                         .setValueQuantity(value.mValue, value.mUnits)
756                         .toJson();
757         MedicalResourceValidator validator = makeValidator(fhirData);
758 
759         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
760 
761         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VITAL_SIGNS);
762     }
763 
764     @Test
testCalculateMedicalResourceType_vitalSignsPanel_vitalSigns()765     public void testCalculateMedicalResourceType_vitalSignsPanel_vitalSigns() {
766         // From https://hl7.org/fhir/R5/observation-example-vitals-panel.json.html
767         String fhirData =
768                 "{  \"resourceType\" : \"Observation\",  \"id\" : \"vitals-panel\",  \"meta\" : {  "
769                     + "  \"profile\" : [\"http://hl7.org/fhir/StructureDefinition/vitalsigns\"]  },"
770                     + "  \"status\" : \"final\","
771                         // category deliberately left out to check categorization by code
772                         + "  \"code\" : {    \"coding\" : [{      \"system\" :"
773                         + " \"http://loinc.org\",      \"code\" : \"85353-1\",      \"display\" :"
774                         + " \"Vital signs, weight, height, head circumference, oxygen saturation"
775                         + " and BMI panel\"    }],    \"text\" : \"Vital signs Panel\"  }, "
776                         + " \"subject\" : {    \"reference\" : \"Patient/example\"  }, "
777                         + " \"effectiveDateTime\" : \"1999-07-02\",  \"hasMember\" : [{   "
778                         + " \"reference\" : \"Observation/respiratory-rate\",    \"display\" :"
779                         + " \"Respiratory Rate\"  },  {    \"reference\" :"
780                         + " \"Observation/heart-rate\",    \"display\" : \"Heart Rate\"  },  {   "
781                         + " \"reference\" : \"Observation/blood-pressure\",    \"display\" :"
782                         + " \"Blood Pressure\"  },  {    \"reference\" :"
783                         + " \"Observation/body-temperature\",    \"display\" : \"Body Temperature\""
784                         + "  }]}";
785         MedicalResourceValidator validator = makeValidator(fhirData);
786 
787         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
788 
789         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VITAL_SIGNS);
790     }
791 
792     @Test
testCalculateMedicalResourceType_bloodPressurePanel_vitalSigns()793     public void testCalculateMedicalResourceType_bloodPressurePanel_vitalSigns() {
794         // From https://hl7.org/fhir/R5/observation-example-bloodpressure.json.html
795         String fhirData =
796                 "{  \"resourceType\" : \"Observation\",  \"id\" : \"blood-pressure\",  \"meta\" : {"
797                     + "    \"profile\" : [\"http://hl7.org/fhir/StructureDefinition/vitalsigns\"] "
798                     + " },  \"identifier\" : [{    \"system\" : \"urn:ietf:rfc:3986\",    \"value\""
799                     + " : \"urn:uuid:187e0c12-8dd2-67e2-99b2-bf273c878281\"  }],  \"basedOn\" : [{ "
800                     + "   \"identifier\" : {      \"system\" : \"https://acme.org/identifiers\",   "
801                     + "   \"value\" : \"1234\"    }  }],  \"status\" : \"final\","
802                         // category deliberately left out to test categorization by code
803                         + "  \"code\" : {    \"coding\" : [{      \"system\" :"
804                         + " \"http://loinc.org\",      \"code\" : \"85354-9\",      \"display\" :"
805                         + " \"Blood pressure panel with all children optional\"    }],    \"text\""
806                         + " : \"Blood pressure systolic & diastolic\"  },  \"subject\" : {   "
807                         + " \"reference\" : \"Patient/example\"  },  \"effectiveDateTime\" :"
808                         + " \"2012-09-17\",  \"performer\" : [{    \"reference\" :"
809                         + " \"Practitioner/example\"  }],  \"interpretation\" : [{    \"coding\" :"
810                         + " [{      \"system\" :"
811                         + " \"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation\","
812                         + "      \"code\" : \"L\",      \"display\" : \"low\"    }],    \"text\" :"
813                         + " \"Below low normal\"  }],  \"bodySite\" : {    \"coding\" : [{     "
814                         + " \"system\" : \"http://snomed.info/sct\",      \"code\" : \"368209003\","
815                         + "      \"display\" : \"Right arm\"    }]  },  \"component\" : [{   "
816                         + " \"code\" : {      \"coding\" : [{        \"system\" :"
817                         + " \"http://loinc.org\",        \"code\" : \"8480-6\",        \"display\""
818                         + " : \"Systolic blood pressure\"      },      {        \"system\" :"
819                         + " \"http://snomed.info/sct\",        \"code\" : \"271649006\",       "
820                         + " \"display\" : \"Systolic blood pressure\"      },      {       "
821                         + " \"system\" : \"http://acme.org/devices/clinical-codes\",       "
822                         + " \"code\" : \"bp-s\",        \"display\" : \"Systolic Blood pressure\"  "
823                         + "    }]    },    \"valueQuantity\" : {      \"value\" : 107,     "
824                         + " \"unit\" : \"mmHg\",      \"system\" : \"http://unitsofmeasure.org\",  "
825                         + "    \"code\" : \"mm[Hg]\"    },    \"interpretation\" : [{     "
826                         + " \"coding\" : [{        \"system\" :"
827                         + " \"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation\","
828                         + "        \"code\" : \"N\",        \"display\" : \"normal\"      }],     "
829                         + " \"text\" : \"Normal\"    }]  },  {    \"code\" : {      \"coding\" : [{"
830                         + "        \"system\" : \"http://loinc.org\",        \"code\" : \"8462-4\","
831                         + "        \"display\" : \"Diastolic blood pressure\"      }]    },   "
832                         + " \"valueQuantity\" : {      \"value\" : 60,      \"unit\" : \"mmHg\",   "
833                         + "   \"system\" : \"http://unitsofmeasure.org\",      \"code\" :"
834                         + " \"mm[Hg]\"    },    \"interpretation\" : [{      \"coding\" : [{       "
835                         + " \"system\" :"
836                         + " \"http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation\","
837                         + "        \"code\" : \"L\",        \"display\" : \"low\"      }],     "
838                         + " \"text\" : \"Below low normal\"    }]  }]}";
839         MedicalResourceValidator validator = makeValidator(fhirData);
840 
841         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
842 
843         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VITAL_SIGNS);
844     }
845 
846     @Test
testCalculateMedicalResourceType_categoryLaboratory_laboratoryResults()847     public void testCalculateMedicalResourceType_categoryLaboratory_laboratoryResults() {
848         String fhirData = new ObservationBuilder().setCategory(LABORATORY).toJson();
849         MedicalResourceValidator validator = makeValidator(fhirData);
850 
851         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
852 
853         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_LABORATORY_RESULTS);
854     }
855 
856     @Test
testPregancyHigherPriorityThanSocialHistory()857     public void testPregancyHigherPriorityThanSocialHistory() {
858         PregnancyStatusTestValue status = PregnancyStatusTestValue.PREGNANT;
859         String fhirData =
860                 new ObservationBuilder()
861                         .setCode(LOINC, status.mCode)
862                         .setValueCodeableConcept(SNOMED_CT, status.mValue)
863                         .setCategory(SOCIAL_HISTORY)
864                         .toJson();
865         MedicalResourceValidator validator = makeValidator(fhirData);
866 
867         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
868 
869         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_PREGNANCY);
870     }
871 
872     @Test
testSocialHistoryHigherPriorityThanVitalSigns()873     public void testSocialHistoryHigherPriorityThanVitalSigns() throws JSONException {
874         JSONArray categories =
875                 new JSONArray(
876                         List.of(
877                                 VITAL_SIGNS.toFhirCodeableConcept(),
878                                 SOCIAL_HISTORY.toFhirCodeableConcept()));
879         String fhirData = new ObservationBuilder().set("category", categories).toJson();
880         MedicalResourceValidator validator = makeValidator(fhirData);
881 
882         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
883 
884         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_SOCIAL_HISTORY);
885     }
886 
887     @Test
testVitalSignsHigherPriorityThanLabResults()888     public void testVitalSignsHigherPriorityThanLabResults() throws JSONException {
889         JSONArray categories =
890                 new JSONArray(
891                         List.of(
892                                 LABORATORY.toFhirCodeableConcept(),
893                                 VITAL_SIGNS.toFhirCodeableConcept()));
894         String fhirData = new ObservationBuilder().set("category", categories).toJson();
895         MedicalResourceValidator validator = makeValidator(fhirData);
896 
897         int type = validator.validateAndCreateInternalRequest().getMedicalResourceType();
898 
899         assertThat(type).isEqualTo(MedicalResource.MEDICAL_RESOURCE_TYPE_VITAL_SIGNS);
900     }
901 
makeValidator(String fhirData)902     private MedicalResourceValidator makeValidator(String fhirData) {
903         UpsertMedicalResourceRequest request =
904                 new UpsertMedicalResourceRequest.Builder(DATA_SOURCE_ID, FHIR_VERSION_R4, fhirData)
905                         .build();
906         return new MedicalResourceValidator(request, getFhirResourceValidator());
907     }
908 
getFhirResourceValidator()909     private FhirResourceValidator getFhirResourceValidator() {
910         if (mFhirResourceValidator == null) {
911             mFhirResourceValidator = new FhirResourceValidator();
912         }
913         return mFhirResourceValidator;
914     }
915 }
916