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 android.healthconnect.cts.phr.utils;
18 
19 import static android.health.connect.datatypes.FhirResource.FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE;
20 import static android.health.connect.datatypes.FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION;
21 import static android.health.connect.datatypes.FhirVersion.parseFhirVersion;
22 import static android.health.connect.datatypes.MedicalResource.MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES;
23 import static android.health.connect.datatypes.MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES;
24 
25 import android.health.connect.CreateMedicalDataSourceRequest;
26 import android.health.connect.GetMedicalDataSourcesRequest;
27 import android.health.connect.MedicalResourceId;
28 import android.health.connect.UpsertMedicalResourceRequest;
29 import android.health.connect.datatypes.FhirResource;
30 import android.health.connect.datatypes.FhirVersion;
31 import android.health.connect.datatypes.MedicalDataSource;
32 import android.health.connect.datatypes.MedicalResource;
33 import android.net.Uri;
34 
35 import com.google.common.truth.Correspondence;
36 
37 import org.json.JSONException;
38 import org.json.JSONObject;
39 
40 import java.time.Instant;
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.Set;
45 import java.util.UUID;
46 
47 public class PhrDataFactory {
48     private static final int FHIR_BASE_URI_CHARACTER_LIMIT = 2000;
49     private static final int MEDICAL_DATA_SOURCE_DISPLAY_NAME_CHARACTER_LIMIT = 90;
50 
51     public static final int MAX_ALLOWED_MEDICAL_DATA_SOURCES = 20;
52 
53     /**
54      * String version code for FHIR version <a href="https://hl7.org/fhir/r4/versions.html">R4</a>.
55      */
56     public static final String R4_VERSION_STRING = "4.0.1";
57 
58     /**
59      * {@link FhirVersion} for FHIR version <a href="https://hl7.org/fhir/r4/versions.html">R4</a>.
60      */
61     public static final FhirVersion FHIR_VERSION_R4 = parseFhirVersion(R4_VERSION_STRING);
62 
63     /** String version code for FHIR version <a href="https://www.hl7.org/fhir/R4B/">R4B</a>. */
64     public static final String R4B_VERSION_STRING = "4.3.0";
65 
66     /** {@link FhirVersion} for FHIR version <a href="https://www.hl7.org/fhir/R4B/">R4B</a>. */
67     public static final FhirVersion FHIR_VERSION_R4B = parseFhirVersion(R4B_VERSION_STRING);
68 
69     public static final String UNSUPPORTED_VERSION_STRING = "4.5.5";
70     public static final FhirVersion FHIR_VERSION_UNSUPPORTED =
71             parseFhirVersion(UNSUPPORTED_VERSION_STRING);
72 
73     public static final String RESOURCE_ID_FIELD_NAME = "id";
74 
75     public static final UUID DATA_SOURCE_UUID = UUID.randomUUID();
76     public static final String DATA_SOURCE_ID = DATA_SOURCE_UUID.toString();
77     public static final String DATA_SOURCE_PACKAGE_NAME = "com.example.app";
78     public static final Uri DATA_SOURCE_FHIR_BASE_URI =
79             Uri.parse("https://fhir.com/oauth/api/FHIR/R4/");
80     public static final Uri DATA_SOURCE_FHIR_BASE_URI_MAX_CHARS =
81             Uri.parse("d".repeat(FHIR_BASE_URI_CHARACTER_LIMIT));
82     public static final Uri DATA_SOURCE_FHIR_BASE_URI_EXCEEDED_CHARS =
83             Uri.parse("d".repeat(FHIR_BASE_URI_CHARACTER_LIMIT + 1));
84     public static final String DATA_SOURCE_DISPLAY_NAME = "Hospital X";
85     public static final String DATA_SOURCE_DISPLAY_NAME_MAX_CHARS =
86             "d".repeat(MEDICAL_DATA_SOURCE_DISPLAY_NAME_CHARACTER_LIMIT);
87     public static final String DATA_SOURCE_DISPLAY_NAME_EXCEEDED_CHARS =
88             "d".repeat(MEDICAL_DATA_SOURCE_DISPLAY_NAME_CHARACTER_LIMIT + 1);
89     public static final Instant DATA_SOURCE_LAST_DATA_UPDATE_TIME =
90             Instant.parse("2024-09-10T00:02:00Z");
91     public static final FhirVersion DATA_SOURCE_FHIR_VERSION = FHIR_VERSION_R4;
92     public static final UUID DIFFERENT_DATA_SOURCE_UUID = UUID.randomUUID();
93     public static final String DIFFERENT_DATA_SOURCE_ID = DIFFERENT_DATA_SOURCE_UUID.toString();
94     public static final String DIFFERENT_DATA_SOURCE_PACKAGE_NAME = "com.other.app";
95     public static final Uri DIFFERENT_DATA_SOURCE_BASE_URI =
96             Uri.parse("https://fhir.com/oauth/api/FHIR/R5/");
97     public static final String DIFFERENT_DATA_SOURCE_DISPLAY_NAME = "Doctor Y";
98     public static final Instant DIFFERENT_DATA_SOURCE_LAST_DATA_UPDATE_TIME =
99             Instant.parse("2023-01-01T00:02:00Z");
100     public static final FhirVersion DIFFERENT_DATA_SOURCE_FHIR_VERSION = FHIR_VERSION_R4B;
101 
102     public static final String FHIR_RESOURCE_ID_IMMUNIZATION = "Immunization1";
103     public static final String FHIR_DATA_IMMUNIZATION =
104             new ImmunizationBuilder().setId(FHIR_RESOURCE_ID_IMMUNIZATION).toJson();
105     public static final String DIFFERENT_FHIR_RESOURCE_ID_IMMUNIZATION = "Immunization2";
106     public static final String DIFFERENT_FHIR_DATA_IMMUNIZATION =
107             new ImmunizationBuilder().setId(DIFFERENT_FHIR_RESOURCE_ID_IMMUNIZATION).toJson();
108 
109     public static final String FHIR_DATA_IMMUNIZATION_ID_NOT_EXISTS =
110             new ImmunizationBuilder().removeField(RESOURCE_ID_FIELD_NAME).toJson();
111     public static final String FHIR_DATA_IMMUNIZATION_ID_EMPTY =
112             new ImmunizationBuilder().setId("").toJson();
113     public static final String FHIR_DATA_IMMUNIZATION_RESOURCE_TYPE_NOT_EXISTS =
114             new ImmunizationBuilder()
115                     .setId(FHIR_RESOURCE_ID_IMMUNIZATION)
116                     .removeField("resourceType")
117                     .toJson();
118     public static final String FHIR_DATA_IMMUNIZATION_FIELD_MISSING_INVALID = "{\"id\" : }";
119     public static final String FHIR_RESOURCE_TYPE_UNSUPPORTED = "StructureDefinition";
120     public static final String FHIR_DATA_IMMUNIZATION_UNSUPPORTED_RESOURCE_TYPE =
121             "{\"resourceType\" : \"StructureDefinition\", \"id\" : \"Immunization1\"}";
122 
123     public static final String FHIR_RESOURCE_ID_ALLERGY = "Allergy1";
124     public static final String FHIR_DATA_ALLERGY =
125             new AllergyBuilder().setId(FHIR_RESOURCE_ID_ALLERGY).toJson();
126     public static final String DIFFERENT_FHIR_RESOURCE_ID_ALLERGY = "Allergy2";
127     public static final String DIFFERENT_FHIR_DATA_ALLERGY =
128             new AllergyBuilder().setId(DIFFERENT_FHIR_RESOURCE_ID_ALLERGY).toJson();
129     public static final String FHIR_DATA_CONDITION = new ConditionBuilder().toJson();
130     public static final String FHIR_DATA_MEDICATION =
131             new MedicationsBuilder.MedicationBuilder().toJson();
132     public static final String FHIR_DATA_Patient = new PatientBuilder().toJson();
133     public static final String FHIR_DATA_PRACTITIONER = new PractitionerBuilder().toJson();
134     public static final String FHIR_DATA_ENCOUNTER = EncountersBuilder.encounter().toJson();
135     public static final String FHIR_DATA_PROCEDURE = new ProcedureBuilder().toJson();
136     public static final String FHIR_DATA_OBSERVATION_PREGNANCY =
137             new ObservationBuilder()
138                     .setId("1")
139                     .setPregnancyStatus(ObservationBuilder.PregnancyStatus.NOT_PREGNANT)
140                     .toJson();
141     public static final String FHIR_DATA_OBSERVATION_SOCIAL_HISTORY =
142             new ObservationBuilder()
143                     .setId("2")
144                     .setCategory(ObservationBuilder.ObservationCategory.SOCIAL_HISTORY)
145                     .toJson();
146     public static final String FHIR_DATA_OBSERVATION_VITAL_SIGNS =
147             new ObservationBuilder()
148                     .setId("3")
149                     .setCategory(ObservationBuilder.ObservationCategory.VITAL_SIGNS)
150                     .toJson();
151     public static final String FHIR_DATA_OBSERVATION_LABS =
152             new ObservationBuilder()
153                     .setId("4")
154                     .setCategory(ObservationBuilder.ObservationCategory.LABORATORY)
155                     .toJson();
156 
157     public static final String PAGE_TOKEN = "111";
158 
159     public static final Correspondence<MedicalDataSource, MedicalDataSource>
160             MEDICAL_DATA_SOURCE_EQUIVALENCE =
161                     Correspondence.from(
162                             PhrDataFactory::isMedicalDataSourceEqual, "isMedicalDataSourceEqual");
163 
164     /** Creates and returns a {@link MedicalDataSource.Builder} with default arguments. */
getMedicalDataSourceBuilderRequiredFieldsOnly()165     public static MedicalDataSource.Builder getMedicalDataSourceBuilderRequiredFieldsOnly() {
166         return new MedicalDataSource.Builder(
167                 DATA_SOURCE_ID,
168                 DATA_SOURCE_PACKAGE_NAME,
169                 DATA_SOURCE_FHIR_BASE_URI,
170                 DATA_SOURCE_DISPLAY_NAME,
171                 DATA_SOURCE_FHIR_VERSION);
172     }
173 
174     /** Creates and returns a {@link MedicalDataSource.Builder} with default arguments. */
getMedicalDataSourceBuilderWithOptionalFields()175     public static MedicalDataSource.Builder getMedicalDataSourceBuilderWithOptionalFields() {
176         return getMedicalDataSourceBuilderRequiredFieldsOnly()
177                 .setLastDataUpdateTime(DATA_SOURCE_LAST_DATA_UPDATE_TIME);
178     }
179 
180     /**
181      * Creates and returns a {@link MedicalDataSource} with default arguments for required fields.
182      */
getMedicalDataSourceRequiredFieldsOnly()183     public static MedicalDataSource getMedicalDataSourceRequiredFieldsOnly() {
184         return getMedicalDataSourceBuilderRequiredFieldsOnly().build();
185     }
186 
187     /** Creates and returns a {@link MedicalDataSource} with default arguments. */
getMedicalDataSourceWithOptionalFields()188     public static MedicalDataSource getMedicalDataSourceWithOptionalFields() {
189         return getMedicalDataSourceBuilderWithOptionalFields().build();
190     }
191 
192     /**
193      * Creates and returns a {@link GetMedicalDataSourcesRequest} with given {@code packageNames}.
194      */
getGetMedicalDataSourceRequest( Set<String> packageNames)195     public static GetMedicalDataSourcesRequest getGetMedicalDataSourceRequest(
196             Set<String> packageNames) {
197         GetMedicalDataSourcesRequest.Builder builder = new GetMedicalDataSourcesRequest.Builder();
198         for (String packageName : packageNames) {
199             builder.addPackageName(packageName);
200         }
201         return builder.build();
202     }
203 
204     /**
205      * Creates and returns a {@link CreateMedicalDataSourceRequest.Builder} with default arguments
206      * for required fields.
207      */
208     public static CreateMedicalDataSourceRequest.Builder
getCreateMedicalDataSourceRequestBuilder()209             getCreateMedicalDataSourceRequestBuilder() {
210         return new CreateMedicalDataSourceRequest.Builder(
211                 DATA_SOURCE_FHIR_BASE_URI, DATA_SOURCE_DISPLAY_NAME, DATA_SOURCE_FHIR_VERSION);
212     }
213 
214     /**
215      * Creates and returns a {@link CreateMedicalDataSourceRequest.Builder} with default arguments,
216      * with the given suffix appended to the base URI and name, to enable different data sources to
217      * be created.
218      */
getCreateMedicalDataSourceRequestBuilder( String suffix)219     public static CreateMedicalDataSourceRequest.Builder getCreateMedicalDataSourceRequestBuilder(
220             String suffix) {
221         Uri fhirBaseUri = Uri.withAppendedPath(DATA_SOURCE_FHIR_BASE_URI, "/" + suffix);
222         return new CreateMedicalDataSourceRequest.Builder(
223                 fhirBaseUri, DATA_SOURCE_DISPLAY_NAME + " " + suffix, DATA_SOURCE_FHIR_VERSION);
224     }
225 
226     /** Creates and returns a {@link CreateMedicalDataSourceRequest} with default arguments. */
getCreateMedicalDataSourceRequest()227     public static CreateMedicalDataSourceRequest getCreateMedicalDataSourceRequest() {
228         return getCreateMedicalDataSourceRequestBuilder().build();
229     }
230 
231     /**
232      * Creates and returns a {@link CreateMedicalDataSourceRequest} with the default arguments, with
233      * the given suffix appended to the base URI and name, to enable different data sources to be
234      * created.
235      */
getCreateMedicalDataSourceRequest(String suffix)236     public static CreateMedicalDataSourceRequest getCreateMedicalDataSourceRequest(String suffix) {
237         return getCreateMedicalDataSourceRequestBuilder(suffix).build();
238     }
239 
240     /**
241      * Creates and returns a {@link FhirResource.Builder} with default arguments.
242      *
243      * <p>By default, it contains the {@link PhrDataFactory#FHIR_DATA_IMMUNIZATION}.
244      */
getFhirResourceBuilder()245     public static FhirResource.Builder getFhirResourceBuilder() {
246         return new FhirResource.Builder(
247                 FHIR_RESOURCE_TYPE_IMMUNIZATION,
248                 FHIR_RESOURCE_ID_IMMUNIZATION,
249                 FHIR_DATA_IMMUNIZATION);
250     }
251 
252     /**
253      * Creates and returns a {@link FhirResource} with default arguments.
254      *
255      * <p>By default, it contains the {@link PhrDataFactory#FHIR_DATA_IMMUNIZATION}.
256      */
getFhirResource()257     public static FhirResource getFhirResource() {
258         return getFhirResourceBuilder().build();
259     }
260 
261     /**
262      * Creates and returns a {@link FhirResource} with the status field of the {@link
263      * PhrDataFactory#FHIR_DATA_IMMUNIZATION} updated.
264      */
getUpdatedImmunizationFhirResource()265     public static FhirResource getUpdatedImmunizationFhirResource() throws JSONException {
266         return new FhirResource.Builder(
267                         FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION,
268                         FHIR_RESOURCE_ID_IMMUNIZATION,
269                         addCompletedStatus(FHIR_DATA_IMMUNIZATION))
270                 .build();
271     }
272 
273     /**
274      * Creates and returns a {@link FhirResource} with the status field of the {@link
275      * PhrDataFactory#FHIR_DATA_ALLERGY} updated.
276      */
getUpdatedAllergyFhirResource()277     public static FhirResource getUpdatedAllergyFhirResource() throws JSONException {
278         return new FhirResource.Builder(
279                         FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE,
280                         FHIR_RESOURCE_ID_ALLERGY,
281                         addCompletedStatus(FHIR_DATA_ALLERGY))
282                 .build();
283     }
284 
285     /**
286      * Creates and returns a {@link FhirResource} with {@link
287      * PhrDataFactory#DIFFERENT_FHIR_DATA_IMMUNIZATION} data.
288      */
getFhirResourceDifferentImmunization()289     public static FhirResource getFhirResourceDifferentImmunization() {
290         return new FhirResource.Builder(
291                         FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION,
292                         DIFFERENT_FHIR_RESOURCE_ID_IMMUNIZATION,
293                         DIFFERENT_FHIR_DATA_IMMUNIZATION)
294                 .build();
295     }
296 
297     /**
298      * Creates and returns a {@link FhirResource} with {@link PhrDataFactory#FHIR_DATA_ALLERGY}
299      * data.
300      */
getFhirResourceAllergy()301     public static FhirResource getFhirResourceAllergy() {
302         return new FhirResource.Builder(
303                         FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE,
304                         FHIR_RESOURCE_ID_ALLERGY,
305                         FHIR_DATA_ALLERGY)
306                 .build();
307     }
308 
309     /**
310      * Creates and returns a {@link FhirResource} with {@link
311      * PhrDataFactory#DIFFERENT_FHIR_DATA_ALLERGY} data.
312      */
getFhirResourceDifferentAllergy()313     public static FhirResource getFhirResourceDifferentAllergy() {
314         return new FhirResource.Builder(
315                         FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE,
316                         DIFFERENT_FHIR_RESOURCE_ID_ALLERGY,
317                         DIFFERENT_FHIR_DATA_ALLERGY)
318                 .build();
319     }
320 
321     /**
322      * Creates and returns a {@link MedicalResource.Builder} with default arguments.
323      *
324      * <p>By default, it contains the {@link PhrDataFactory#FHIR_DATA_IMMUNIZATION}.
325      */
getMedicalResourceBuilder()326     public static MedicalResource.Builder getMedicalResourceBuilder() {
327         return new MedicalResource.Builder(
328                 MEDICAL_RESOURCE_TYPE_VACCINES, DATA_SOURCE_ID, FHIR_VERSION_R4, getFhirResource());
329     }
330 
331     /**
332      * Creates and returns a {@link MedicalResource} with default arguments.
333      *
334      * <p>By default, it contains the {@link PhrDataFactory#FHIR_DATA_IMMUNIZATION}.
335      */
getMedicalResource()336     public static MedicalResource getMedicalResource() {
337         return getMedicalResourceBuilder().build();
338     }
339 
340     /**
341      * Creates and returns a {@link MedicalResource} of type {@link
342      * MedicalResource#MEDICAL_RESOURCE_TYPE_VACCINES} which contains {@link
343      * PhrDataFactory#getFhirResource} data, with the given {@code dataSourceId}.
344      */
createVaccineMedicalResource(String dataSourceId)345     public static MedicalResource createVaccineMedicalResource(String dataSourceId) {
346         return new MedicalResource.Builder(
347                         MEDICAL_RESOURCE_TYPE_VACCINES,
348                         dataSourceId,
349                         FHIR_VERSION_R4,
350                         getFhirResource())
351                 .build();
352     }
353 
354     /**
355      * Creates and returns a {@link MedicalResource} of type {@link
356      * MedicalResource#MEDICAL_RESOURCE_TYPE_VACCINES} which contains {@link
357      * PhrDataFactory#getFhirResourceDifferentImmunization} data, with the given {@code
358      * dataSourceId}.
359      *
360      * <p>The contained FHIR data has a different resource ID than the above {@link
361      * PhrDataFactory#createVaccineMedicalResource}.
362      */
createDifferentVaccineMedicalResource(String dataSourceId)363     public static MedicalResource createDifferentVaccineMedicalResource(String dataSourceId) {
364         return new MedicalResource.Builder(
365                         MEDICAL_RESOURCE_TYPE_VACCINES,
366                         dataSourceId,
367                         FHIR_VERSION_R4,
368                         getFhirResourceDifferentImmunization())
369                 .build();
370     }
371 
372     /**
373      * Creates and returns a {@link MedicalResource} of type {@link
374      * MedicalResource#MEDICAL_RESOURCE_TYPE_VACCINES} which contains {@link
375      * PhrDataFactory#getUpdatedImmunizationFhirResource} data, with the given {@code dataSourceId}.
376      *
377      * <p>The contained FHIR data has the same resource ID as the above {@link
378      * PhrDataFactory#createVaccineMedicalResource}, but data is updated with a "status" field
379      * added.
380      */
createUpdatedVaccineMedicalResource(String dataSourceId)381     public static MedicalResource createUpdatedVaccineMedicalResource(String dataSourceId)
382             throws JSONException {
383         return new MedicalResource.Builder(
384                         MEDICAL_RESOURCE_TYPE_VACCINES,
385                         dataSourceId,
386                         FHIR_VERSION_R4,
387                         getUpdatedImmunizationFhirResource())
388                 .build();
389     }
390 
391     /**
392      * Creates and returns a {@link MedicalResource} of type {@link
393      * MedicalResource#MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES} which contains {@link
394      * PhrDataFactory#getFhirResourceAllergy} data, with the given {@code dataSourceId}.
395      */
createAllergyMedicalResource(String dataSourceId)396     public static MedicalResource createAllergyMedicalResource(String dataSourceId) {
397         return new MedicalResource.Builder(
398                         MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES,
399                         dataSourceId,
400                         FHIR_VERSION_R4,
401                         getFhirResourceAllergy())
402                 .build();
403     }
404 
405     /**
406      * Creates and returns a {@link MedicalResource} of type {@link
407      * MedicalResource#MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES} which contains {@link
408      * PhrDataFactory#getFhirResourceDifferentAllergy} data, with the given {@code dataSourceId}.
409      *
410      * <p>The contained FHIR data has a different resource ID than the above {@link
411      * PhrDataFactory#createAllergyMedicalResource}.
412      */
createDifferentAllergyMedicalResource(String dataSourceId)413     public static MedicalResource createDifferentAllergyMedicalResource(String dataSourceId) {
414         return new MedicalResource.Builder(
415                         MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES,
416                         dataSourceId,
417                         FHIR_VERSION_R4,
418                         getFhirResourceDifferentAllergy())
419                 .build();
420     }
421 
422     /**
423      * Creates and returns a {@link MedicalResource} of type {@link
424      * MedicalResource#MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES} which contains {@link
425      * PhrDataFactory#getUpdatedAllergyFhirResource} data, with the given {@code dataSourceId}.
426      *
427      * <p>The contained FHIR data has the same resource ID as the above {@link
428      * PhrDataFactory#createAllergyMedicalResource}, but data is updated with a "status" field
429      * added.
430      */
createUpdatedAllergyMedicalResource(String dataSourceId)431     public static MedicalResource createUpdatedAllergyMedicalResource(String dataSourceId)
432             throws JSONException {
433         return new MedicalResource.Builder(
434                         MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES,
435                         dataSourceId,
436                         FHIR_VERSION_R4,
437                         getUpdatedAllergyFhirResource())
438                 .build();
439     }
440 
441     /**
442      * Creates and returns a {@link UpsertMedicalResourceRequest.Builder} with default arguments.
443      */
getUpsertMedicalResourceRequestBuilder()444     public static UpsertMedicalResourceRequest.Builder getUpsertMedicalResourceRequestBuilder() {
445         return new UpsertMedicalResourceRequest.Builder(
446                 DATA_SOURCE_ID, FHIR_VERSION_R4, FHIR_DATA_IMMUNIZATION);
447     }
448 
449     /** Creates and returns a {@link UpsertMedicalResourceRequest} with default arguments. */
getUpsertMedicalResourceRequest()450     public static UpsertMedicalResourceRequest getUpsertMedicalResourceRequest() {
451         return getUpsertMedicalResourceRequestBuilder().build();
452     }
453 
454     /**
455      * Creates and returns a {@link MedicalResourceId} with default arguments.
456      *
457      * <p>By default, it contains the {@link FhirResource#FHIR_RESOURCE_TYPE_IMMUNIZATION} and
458      * {@link PhrDataFactory#FHIR_RESOURCE_ID_IMMUNIZATION}.
459      */
getMedicalResourceId()460     public static MedicalResourceId getMedicalResourceId() {
461         return new MedicalResourceId(
462                 DATA_SOURCE_ID, FHIR_RESOURCE_TYPE_IMMUNIZATION, FHIR_RESOURCE_ID_IMMUNIZATION);
463     }
464 
465     /** Returns the FHIR resource id field from the given {@code fhirJSON} string. */
getFhirResourceId(String fhirJSON)466     public static String getFhirResourceId(String fhirJSON) throws JSONException {
467         return new JSONObject(fhirJSON).getString(RESOURCE_ID_FIELD_NAME);
468     }
469 
470     /** Returns an updated FHIR JSON string with an added status field. */
addCompletedStatus(String fhirJSON)471     public static String addCompletedStatus(String fhirJSON) throws JSONException {
472         JSONObject jsonObj = new JSONObject(fhirJSON);
473         jsonObj.put("status", "completed");
474         return jsonObj.toString();
475     }
476 
477     /**
478      * Creates a number of vaccine resources based on the given {@code numOfResources} and {@code
479      * dataSourceId}.
480      */
createVaccineMedicalResources( int numOfResources, String dataSourceId)481     public static List<MedicalResource> createVaccineMedicalResources(
482             int numOfResources, String dataSourceId) {
483         FhirVersion fhirVersion = parseFhirVersion(R4_VERSION_STRING);
484         List<MedicalResource> medicalResources = new ArrayList<>();
485         for (int i = 0; i < numOfResources; i++) {
486             String fhirResourceId = "id/" + i;
487             FhirResource fhirResource =
488                     new FhirResource.Builder(
489                                     FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION,
490                                     fhirResourceId,
491                                     new ImmunizationBuilder().setId(fhirResourceId).toJson())
492                             .build();
493             MedicalResource medicalResource =
494                     new MedicalResource.Builder(
495                                     MEDICAL_RESOURCE_TYPE_VACCINES,
496                                     dataSourceId,
497                                     fhirVersion,
498                                     fhirResource)
499                             .build();
500             medicalResources.add(medicalResource);
501         }
502         return medicalResources;
503     }
504 
505     /**
506      * Creates a number of allergy resources based on the given {@code numOfResources} and {@code
507      * dataSourceId}.
508      */
createAllergyMedicalResources( int numOfResources, String dataSourceId)509     public static List<MedicalResource> createAllergyMedicalResources(
510             int numOfResources, String dataSourceId) {
511         FhirVersion fhirVersion = parseFhirVersion(R4_VERSION_STRING);
512         List<MedicalResource> medicalResources = new ArrayList<>();
513         for (int i = 0; i < numOfResources; i++) {
514             FhirResource fhirResource =
515                     new FhirResource.Builder(
516                                     FHIR_RESOURCE_TYPE_ALLERGY_INTOLERANCE,
517                                     "id/" + i,
518                                     FHIR_DATA_ALLERGY)
519                             .build();
520             MedicalResource medicalResource =
521                     new MedicalResource.Builder(
522                                     MEDICAL_RESOURCE_TYPE_ALLERGIES_INTOLERANCES,
523                                     dataSourceId,
524                                     fhirVersion,
525                                     fhirResource)
526                             .build();
527             medicalResources.add(medicalResource);
528         }
529         return medicalResources;
530     }
531 
532     /**
533      * Given two {@link MedicalDataSource}s, compare whether they are equal or not. This ignores the
534      * {@link MedicalDataSource#getLastDataUpdateTime()}.
535      */
isMedicalDataSourceEqual( MedicalDataSource actual, MedicalDataSource expected)536     public static boolean isMedicalDataSourceEqual(
537             MedicalDataSource actual, MedicalDataSource expected) {
538         return Objects.equals(actual.getId(), expected.getId())
539                 && Objects.equals(actual.getFhirVersion(), expected.getFhirVersion())
540                 && Objects.equals(actual.getFhirBaseUri(), expected.getFhirBaseUri())
541                 && Objects.equals(actual.getPackageName(), expected.getPackageName())
542                 && Objects.equals(actual.getDisplayName(), expected.getDisplayName());
543     }
544 }
545