ipa_test_kit 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/ipa_test_kit/custom_groups/capability_statement/conformance_support_test.rb +41 -0
  4. data/lib/ipa_test_kit/custom_groups/capability_statement/fhir_version_test.rb +15 -0
  5. data/lib/ipa_test_kit/custom_groups/capability_statement/json_support_test.rb +39 -0
  6. data/lib/ipa_test_kit/custom_groups/capability_statement/profile_support_test.rb +76 -0
  7. data/lib/ipa_test_kit/custom_groups/capability_statement_group.rb +64 -0
  8. data/lib/ipa_test_kit/date_search_validation.rb +101 -0
  9. data/lib/ipa_test_kit/ext/fhir_models.rb +59 -0
  10. data/lib/ipa_test_kit/fhir_resource_navigation.rb +63 -0
  11. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_must_support_test.rb +40 -0
  12. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_patient_clinical_status_search_test.rb +46 -0
  13. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_patient_search_test.rb +60 -0
  14. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_provenance_revinclude_search_test.rb +49 -0
  15. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_read_test.rb +24 -0
  16. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_reference_resolution_test.rb +28 -0
  17. data/lib/ipa_test_kit/generated/allergy_intolerance/allergy_intolerance_validation_test.rb +34 -0
  18. data/lib/ipa_test_kit/generated/allergy_intolerance/metadata.yml +165 -0
  19. data/lib/ipa_test_kit/generated/allergy_intolerance_group.rb +85 -0
  20. data/lib/ipa_test_kit/generated/bmi/bmi_must_support_test.rb +49 -0
  21. data/lib/ipa_test_kit/generated/bmi/bmi_patient_category_date_search_test.rb +46 -0
  22. data/lib/ipa_test_kit/generated/bmi/bmi_patient_category_search_test.rb +45 -0
  23. data/lib/ipa_test_kit/generated/bmi/bmi_patient_category_status_search_test.rb +47 -0
  24. data/lib/ipa_test_kit/generated/bmi/bmi_patient_code_date_search_test.rb +48 -0
  25. data/lib/ipa_test_kit/generated/bmi/bmi_patient_code_search_test.rb +62 -0
  26. data/lib/ipa_test_kit/generated/bmi/bmi_provenance_revinclude_search_test.rb +51 -0
  27. data/lib/ipa_test_kit/generated/bmi/bmi_read_test.rb +24 -0
  28. data/lib/ipa_test_kit/generated/bmi/bmi_reference_resolution_test.rb +28 -0
  29. data/lib/ipa_test_kit/generated/bmi/bmi_validation_test.rb +34 -0
  30. data/lib/ipa_test_kit/generated/bmi/metadata.yml +322 -0
  31. data/lib/ipa_test_kit/generated/bmi_group.rb +93 -0
  32. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_must_support_test.rb +49 -0
  33. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_patient_category_date_search_test.rb +46 -0
  34. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_patient_category_search_test.rb +45 -0
  35. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_patient_category_status_search_test.rb +47 -0
  36. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_patient_code_date_search_test.rb +48 -0
  37. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_patient_code_search_test.rb +62 -0
  38. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_provenance_revinclude_search_test.rb +51 -0
  39. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_read_test.rb +24 -0
  40. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_reference_resolution_test.rb +28 -0
  41. data/lib/ipa_test_kit/generated/bodyheight/bodyheight_validation_test.rb +34 -0
  42. data/lib/ipa_test_kit/generated/bodyheight/metadata.yml +328 -0
  43. data/lib/ipa_test_kit/generated/bodyheight_group.rb +93 -0
  44. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_must_support_test.rb +49 -0
  45. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_patient_category_date_search_test.rb +46 -0
  46. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_patient_category_search_test.rb +45 -0
  47. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_patient_category_status_search_test.rb +47 -0
  48. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_patient_code_date_search_test.rb +48 -0
  49. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_patient_code_search_test.rb +62 -0
  50. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_provenance_revinclude_search_test.rb +51 -0
  51. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_read_test.rb +24 -0
  52. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_reference_resolution_test.rb +28 -0
  53. data/lib/ipa_test_kit/generated/bodytemp/bodytemp_validation_test.rb +34 -0
  54. data/lib/ipa_test_kit/generated/bodytemp/metadata.yml +328 -0
  55. data/lib/ipa_test_kit/generated/bodytemp_group.rb +93 -0
  56. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_must_support_test.rb +49 -0
  57. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_patient_category_date_search_test.rb +46 -0
  58. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_patient_category_search_test.rb +45 -0
  59. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_patient_category_status_search_test.rb +47 -0
  60. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_patient_code_date_search_test.rb +48 -0
  61. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_patient_code_search_test.rb +62 -0
  62. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_provenance_revinclude_search_test.rb +51 -0
  63. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_read_test.rb +24 -0
  64. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_reference_resolution_test.rb +28 -0
  65. data/lib/ipa_test_kit/generated/bodyweight/bodyweight_validation_test.rb +34 -0
  66. data/lib/ipa_test_kit/generated/bodyweight/metadata.yml +328 -0
  67. data/lib/ipa_test_kit/generated/bodyweight_group.rb +93 -0
  68. data/lib/ipa_test_kit/generated/bp/bp_must_support_test.rb +52 -0
  69. data/lib/ipa_test_kit/generated/bp/bp_patient_category_date_search_test.rb +46 -0
  70. data/lib/ipa_test_kit/generated/bp/bp_patient_category_search_test.rb +45 -0
  71. data/lib/ipa_test_kit/generated/bp/bp_patient_category_status_search_test.rb +47 -0
  72. data/lib/ipa_test_kit/generated/bp/bp_patient_code_date_search_test.rb +48 -0
  73. data/lib/ipa_test_kit/generated/bp/bp_patient_code_search_test.rb +62 -0
  74. data/lib/ipa_test_kit/generated/bp/bp_provenance_revinclude_search_test.rb +51 -0
  75. data/lib/ipa_test_kit/generated/bp/bp_read_test.rb +24 -0
  76. data/lib/ipa_test_kit/generated/bp/bp_reference_resolution_test.rb +28 -0
  77. data/lib/ipa_test_kit/generated/bp/bp_validation_test.rb +34 -0
  78. data/lib/ipa_test_kit/generated/bp/metadata.yml +379 -0
  79. data/lib/ipa_test_kit/generated/bp_group.rb +93 -0
  80. data/lib/ipa_test_kit/generated/condition/condition_must_support_test.rb +39 -0
  81. data/lib/ipa_test_kit/generated/condition/condition_patient_category_search_test.rb +47 -0
  82. data/lib/ipa_test_kit/generated/condition/condition_patient_clinical_status_search_test.rb +46 -0
  83. data/lib/ipa_test_kit/generated/condition/condition_patient_code_search_test.rb +47 -0
  84. data/lib/ipa_test_kit/generated/condition/condition_patient_onset_date_search_test.rb +47 -0
  85. data/lib/ipa_test_kit/generated/condition/condition_patient_search_test.rb +60 -0
  86. data/lib/ipa_test_kit/generated/condition/condition_provenance_revinclude_search_test.rb +49 -0
  87. data/lib/ipa_test_kit/generated/condition/condition_read_test.rb +24 -0
  88. data/lib/ipa_test_kit/generated/condition/condition_reference_resolution_test.rb +28 -0
  89. data/lib/ipa_test_kit/generated/condition/condition_validation_test.rb +34 -0
  90. data/lib/ipa_test_kit/generated/condition/metadata.yml +231 -0
  91. data/lib/ipa_test_kit/generated/condition_group.rb +91 -0
  92. data/lib/ipa_test_kit/generated/document_reference/document_reference_id_search_test.rb +40 -0
  93. data/lib/ipa_test_kit/generated/document_reference/document_reference_must_support_test.rb +49 -0
  94. data/lib/ipa_test_kit/generated/document_reference/document_reference_patient_category_date_search_test.rb +46 -0
  95. data/lib/ipa_test_kit/generated/document_reference/document_reference_patient_category_search_test.rb +45 -0
  96. data/lib/ipa_test_kit/generated/document_reference/document_reference_patient_search_test.rb +61 -0
  97. data/lib/ipa_test_kit/generated/document_reference/document_reference_patient_status_search_test.rb +46 -0
  98. data/lib/ipa_test_kit/generated/document_reference/document_reference_patient_type_period_search_test.rb +48 -0
  99. data/lib/ipa_test_kit/generated/document_reference/document_reference_patient_type_search_test.rb +45 -0
  100. data/lib/ipa_test_kit/generated/document_reference/document_reference_provenance_revinclude_search_test.rb +50 -0
  101. data/lib/ipa_test_kit/generated/document_reference/document_reference_read_test.rb +24 -0
  102. data/lib/ipa_test_kit/generated/document_reference/document_reference_reference_resolution_test.rb +28 -0
  103. data/lib/ipa_test_kit/generated/document_reference/document_reference_validation_test.rb +34 -0
  104. data/lib/ipa_test_kit/generated/document_reference/metadata.yml +356 -0
  105. data/lib/ipa_test_kit/generated/document_reference_group.rb +99 -0
  106. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_must_support_test.rb +49 -0
  107. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_patient_category_date_search_test.rb +46 -0
  108. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_patient_category_search_test.rb +45 -0
  109. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_patient_category_status_search_test.rb +47 -0
  110. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_patient_code_date_search_test.rb +48 -0
  111. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_patient_code_search_test.rb +62 -0
  112. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_provenance_revinclude_search_test.rb +51 -0
  113. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_read_test.rb +24 -0
  114. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_reference_resolution_test.rb +28 -0
  115. data/lib/ipa_test_kit/generated/head_circumference/head_circumference_validation_test.rb +34 -0
  116. data/lib/ipa_test_kit/generated/head_circumference/metadata.yml +324 -0
  117. data/lib/ipa_test_kit/generated/head_circumference_group.rb +93 -0
  118. data/lib/ipa_test_kit/generated/heartrate/heartrate_must_support_test.rb +49 -0
  119. data/lib/ipa_test_kit/generated/heartrate/heartrate_patient_category_date_search_test.rb +46 -0
  120. data/lib/ipa_test_kit/generated/heartrate/heartrate_patient_category_search_test.rb +45 -0
  121. data/lib/ipa_test_kit/generated/heartrate/heartrate_patient_category_status_search_test.rb +47 -0
  122. data/lib/ipa_test_kit/generated/heartrate/heartrate_patient_code_date_search_test.rb +48 -0
  123. data/lib/ipa_test_kit/generated/heartrate/heartrate_patient_code_search_test.rb +62 -0
  124. data/lib/ipa_test_kit/generated/heartrate/heartrate_provenance_revinclude_search_test.rb +51 -0
  125. data/lib/ipa_test_kit/generated/heartrate/heartrate_read_test.rb +24 -0
  126. data/lib/ipa_test_kit/generated/heartrate/heartrate_reference_resolution_test.rb +28 -0
  127. data/lib/ipa_test_kit/generated/heartrate/heartrate_validation_test.rb +34 -0
  128. data/lib/ipa_test_kit/generated/heartrate/metadata.yml +325 -0
  129. data/lib/ipa_test_kit/generated/heartrate_group.rb +93 -0
  130. data/lib/ipa_test_kit/generated/immunization/immunization_must_support_test.rb +40 -0
  131. data/lib/ipa_test_kit/generated/immunization/immunization_patient_date_search_test.rb +47 -0
  132. data/lib/ipa_test_kit/generated/immunization/immunization_patient_search_test.rb +60 -0
  133. data/lib/ipa_test_kit/generated/immunization/immunization_patient_status_search_test.rb +45 -0
  134. data/lib/ipa_test_kit/generated/immunization/immunization_provenance_revinclude_search_test.rb +50 -0
  135. data/lib/ipa_test_kit/generated/immunization/immunization_read_test.rb +24 -0
  136. data/lib/ipa_test_kit/generated/immunization/immunization_reference_resolution_test.rb +28 -0
  137. data/lib/ipa_test_kit/generated/immunization/immunization_validation_test.rb +34 -0
  138. data/lib/ipa_test_kit/generated/immunization/metadata.yml +210 -0
  139. data/lib/ipa_test_kit/generated/immunization_group.rb +87 -0
  140. data/lib/ipa_test_kit/generated/ipa_test_suite.rb +88 -0
  141. data/lib/ipa_test_kit/generated/medication_request/medication_request_must_support_test.rb +44 -0
  142. data/lib/ipa_test_kit/generated/medication_request/medication_request_patient_intent_authoredon_search_test.rb +53 -0
  143. data/lib/ipa_test_kit/generated/medication_request/medication_request_patient_intent_encounter_search_test.rb +52 -0
  144. data/lib/ipa_test_kit/generated/medication_request/medication_request_patient_intent_search_test.rb +68 -0
  145. data/lib/ipa_test_kit/generated/medication_request/medication_request_patient_intent_status_search_test.rb +49 -0
  146. data/lib/ipa_test_kit/generated/medication_request/medication_request_provenance_revinclude_search_test.rb +51 -0
  147. data/lib/ipa_test_kit/generated/medication_request/medication_request_read_test.rb +24 -0
  148. data/lib/ipa_test_kit/generated/medication_request/medication_request_reference_resolution_test.rb +28 -0
  149. data/lib/ipa_test_kit/generated/medication_request/medication_request_validation_test.rb +34 -0
  150. data/lib/ipa_test_kit/generated/medication_request/medication_validation_test.rb +34 -0
  151. data/lib/ipa_test_kit/generated/medication_request/metadata.yml +307 -0
  152. data/lib/ipa_test_kit/generated/medication_request_group.rb +92 -0
  153. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_must_support_test.rb +43 -0
  154. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_patient_context_search_test.rb +51 -0
  155. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_patient_effective_search_test.rb +52 -0
  156. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_patient_search_test.rb +68 -0
  157. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_patient_status_search_test.rb +49 -0
  158. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_provenance_revinclude_search_test.rb +51 -0
  159. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_read_test.rb +24 -0
  160. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_reference_resolution_test.rb +28 -0
  161. data/lib/ipa_test_kit/generated/medication_statement/medication_statement_validation_test.rb +34 -0
  162. data/lib/ipa_test_kit/generated/medication_statement/medication_validation_test.rb +34 -0
  163. data/lib/ipa_test_kit/generated/medication_statement/metadata.yml +230 -0
  164. data/lib/ipa_test_kit/generated/medication_statement_group.rb +92 -0
  165. data/lib/ipa_test_kit/generated/metadata.yml +6230 -0
  166. data/lib/ipa_test_kit/generated/observation/metadata.yml +1280 -0
  167. data/lib/ipa_test_kit/generated/observation/observation_must_support_test.rb +41 -0
  168. data/lib/ipa_test_kit/generated/observation/observation_patient_category_date_search_test.rb +46 -0
  169. data/lib/ipa_test_kit/generated/observation/observation_patient_category_search_test.rb +62 -0
  170. data/lib/ipa_test_kit/generated/observation/observation_patient_category_status_search_test.rb +47 -0
  171. data/lib/ipa_test_kit/generated/observation/observation_patient_code_date_search_test.rb +48 -0
  172. data/lib/ipa_test_kit/generated/observation/observation_patient_code_search_test.rb +45 -0
  173. data/lib/ipa_test_kit/generated/observation/observation_provenance_revinclude_search_test.rb +51 -0
  174. data/lib/ipa_test_kit/generated/observation/observation_read_test.rb +24 -0
  175. data/lib/ipa_test_kit/generated/observation/observation_reference_resolution_test.rb +28 -0
  176. data/lib/ipa_test_kit/generated/observation/observation_validation_test.rb +34 -0
  177. data/lib/ipa_test_kit/generated/observation_group.rb +93 -0
  178. data/lib/ipa_test_kit/generated/oxygensat/metadata.yml +366 -0
  179. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_must_support_test.rb +65 -0
  180. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_patient_category_date_search_test.rb +46 -0
  181. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_patient_category_search_test.rb +45 -0
  182. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_patient_category_status_search_test.rb +47 -0
  183. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_patient_code_date_search_test.rb +48 -0
  184. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_patient_code_search_test.rb +62 -0
  185. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_provenance_revinclude_search_test.rb +51 -0
  186. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_read_test.rb +24 -0
  187. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_reference_resolution_test.rb +28 -0
  188. data/lib/ipa_test_kit/generated/oxygensat/oxygensat_validation_test.rb +34 -0
  189. data/lib/ipa_test_kit/generated/oxygensat_group.rb +93 -0
  190. data/lib/ipa_test_kit/generated/patient/metadata.yml +320 -0
  191. data/lib/ipa_test_kit/generated/patient/patient_birthdate_family_search_test.rb +41 -0
  192. data/lib/ipa_test_kit/generated/patient/patient_birthdate_name_search_test.rb +39 -0
  193. data/lib/ipa_test_kit/generated/patient/patient_family_gender_search_test.rb +41 -0
  194. data/lib/ipa_test_kit/generated/patient/patient_gender_name_search_test.rb +39 -0
  195. data/lib/ipa_test_kit/generated/patient/patient_id_search_test.rb +53 -0
  196. data/lib/ipa_test_kit/generated/patient/patient_identifier_search_test.rb +40 -0
  197. data/lib/ipa_test_kit/generated/patient/patient_must_support_test.rb +57 -0
  198. data/lib/ipa_test_kit/generated/patient/patient_name_search_test.rb +39 -0
  199. data/lib/ipa_test_kit/generated/patient/patient_provenance_revinclude_search_test.rb +49 -0
  200. data/lib/ipa_test_kit/generated/patient/patient_read_test.rb +24 -0
  201. data/lib/ipa_test_kit/generated/patient/patient_reference_resolution_test.rb +28 -0
  202. data/lib/ipa_test_kit/generated/patient/patient_validation_test.rb +34 -0
  203. data/lib/ipa_test_kit/generated/patient_group.rb +99 -0
  204. data/lib/ipa_test_kit/generated/practitioner/metadata.yml +124 -0
  205. data/lib/ipa_test_kit/generated/practitioner/practitioner_identifier_search_test.rb +40 -0
  206. data/lib/ipa_test_kit/generated/practitioner/practitioner_must_support_test.rb +40 -0
  207. data/lib/ipa_test_kit/generated/practitioner/practitioner_name_search_test.rb +49 -0
  208. data/lib/ipa_test_kit/generated/practitioner/practitioner_read_test.rb +24 -0
  209. data/lib/ipa_test_kit/generated/practitioner/practitioner_reference_resolution_test.rb +28 -0
  210. data/lib/ipa_test_kit/generated/practitioner/practitioner_validation_test.rb +34 -0
  211. data/lib/ipa_test_kit/generated/practitioner_group.rb +84 -0
  212. data/lib/ipa_test_kit/generated/practitioner_role/metadata.yml +141 -0
  213. data/lib/ipa_test_kit/generated/practitioner_role/practitioner_role_identifier_search_test.rb +40 -0
  214. data/lib/ipa_test_kit/generated/practitioner_role/practitioner_role_must_support_test.rb +40 -0
  215. data/lib/ipa_test_kit/generated/practitioner_role/practitioner_role_practitioner_search_test.rb +51 -0
  216. data/lib/ipa_test_kit/generated/practitioner_role/practitioner_role_read_test.rb +24 -0
  217. data/lib/ipa_test_kit/generated/practitioner_role/practitioner_role_reference_resolution_test.rb +28 -0
  218. data/lib/ipa_test_kit/generated/practitioner_role/practitioner_role_validation_test.rb +34 -0
  219. data/lib/ipa_test_kit/generated/practitioner_role_group.rb +84 -0
  220. data/lib/ipa_test_kit/generated/provenance/metadata.yml +114 -0
  221. data/lib/ipa_test_kit/generated/provenance/provenance_read_test.rb +24 -0
  222. data/lib/ipa_test_kit/generated/provenance/provenance_reference_resolution_test.rb +28 -0
  223. data/lib/ipa_test_kit/generated/provenance/provenance_validation_test.rb +34 -0
  224. data/lib/ipa_test_kit/generated/provenance_group.rb +53 -0
  225. data/lib/ipa_test_kit/generated/resource_list.rb +18 -0
  226. data/lib/ipa_test_kit/generated/resprate/metadata.yml +325 -0
  227. data/lib/ipa_test_kit/generated/resprate/resprate_must_support_test.rb +49 -0
  228. data/lib/ipa_test_kit/generated/resprate/resprate_patient_category_date_search_test.rb +46 -0
  229. data/lib/ipa_test_kit/generated/resprate/resprate_patient_category_search_test.rb +45 -0
  230. data/lib/ipa_test_kit/generated/resprate/resprate_patient_category_status_search_test.rb +47 -0
  231. data/lib/ipa_test_kit/generated/resprate/resprate_patient_code_date_search_test.rb +48 -0
  232. data/lib/ipa_test_kit/generated/resprate/resprate_patient_code_search_test.rb +62 -0
  233. data/lib/ipa_test_kit/generated/resprate/resprate_provenance_revinclude_search_test.rb +51 -0
  234. data/lib/ipa_test_kit/generated/resprate/resprate_read_test.rb +24 -0
  235. data/lib/ipa_test_kit/generated/resprate/resprate_reference_resolution_test.rb +28 -0
  236. data/lib/ipa_test_kit/generated/resprate/resprate_validation_test.rb +34 -0
  237. data/lib/ipa_test_kit/generated/resprate_group.rb +93 -0
  238. data/lib/ipa_test_kit/generator/group_generator.rb +171 -0
  239. data/lib/ipa_test_kit/generator/group_metadata.rb +98 -0
  240. data/lib/ipa_test_kit/generator/group_metadata_extractor.rb +278 -0
  241. data/lib/ipa_test_kit/generator/ig_loader.rb +63 -0
  242. data/lib/ipa_test_kit/generator/ig_metadata.rb +49 -0
  243. data/lib/ipa_test_kit/generator/ig_metadata_extractor.rb +56 -0
  244. data/lib/ipa_test_kit/generator/ig_resources.rb +48 -0
  245. data/lib/ipa_test_kit/generator/must_support_metadata_extractor.rb +221 -0
  246. data/lib/ipa_test_kit/generator/must_support_test_generator.rb +86 -0
  247. data/lib/ipa_test_kit/generator/naming.rb +46 -0
  248. data/lib/ipa_test_kit/generator/provenance_revinclude_search_test_generator.rb +186 -0
  249. data/lib/ipa_test_kit/generator/read_test_generator.rb +89 -0
  250. data/lib/ipa_test_kit/generator/reference_resolution_test_generator.rb +72 -0
  251. data/lib/ipa_test_kit/generator/resource_list_generator.rb +46 -0
  252. data/lib/ipa_test_kit/generator/search_definition_metadata_extractor.rb +181 -0
  253. data/lib/ipa_test_kit/generator/search_metadata_extractor.rb +74 -0
  254. data/lib/ipa_test_kit/generator/search_test_generator.rb +295 -0
  255. data/lib/ipa_test_kit/generator/special_cases.rb +16 -0
  256. data/lib/ipa_test_kit/generator/suite_generator.rb +67 -0
  257. data/lib/ipa_test_kit/generator/terminology_binding_metadata_extractor.rb +105 -0
  258. data/lib/ipa_test_kit/generator/validation_test_generator.rb +124 -0
  259. data/lib/ipa_test_kit/generator.rb +86 -0
  260. data/lib/ipa_test_kit/igs/package.tgz +0 -0
  261. data/lib/ipa_test_kit/must_support_test.rb +146 -0
  262. data/lib/ipa_test_kit/read_test.rb +62 -0
  263. data/lib/ipa_test_kit/reference_resolution_test.rb +111 -0
  264. data/lib/ipa_test_kit/request_logger.rb +46 -0
  265. data/lib/ipa_test_kit/search_test.rb +728 -0
  266. data/lib/ipa_test_kit/search_test_properties.rb +61 -0
  267. data/lib/ipa_test_kit/validation_test.rb +48 -0
  268. data/lib/ipa_test_kit/version.rb +3 -0
  269. data/lib/ipa_test_kit.rb +2 -0
  270. metadata +397 -0
@@ -0,0 +1,728 @@
1
+ require_relative 'date_search_validation'
2
+ require_relative 'fhir_resource_navigation'
3
+ require_relative 'search_test_properties'
4
+
5
+ module IpaTestKit
6
+ module SearchTest
7
+ extend Forwardable
8
+ include DateSearchValidation
9
+ include FHIRResourceNavigation
10
+
11
+ def_delegators 'self.class', :metadata, :provenance_metadata, :properties
12
+ def_delegators 'properties',
13
+ :resource_type,
14
+ :search_param_names,
15
+ :saves_delayed_references?,
16
+ :first_search?,
17
+ :fixed_value_search?,
18
+ :possible_status_search?,
19
+ :test_medication_inclusion_mr?,
20
+ :test_medication_inclusion_ms?,
21
+ :test_post_search?,
22
+ :token_search_params,
23
+ :test_reference_variants?,
24
+ :params_with_comparators,
25
+ :multiple_or_search_params
26
+
27
+ def all_search_params
28
+ @all_search_params ||=
29
+ patient_id_list.each_with_object({}) do |patient_id, params|
30
+ params[patient_id] ||= []
31
+ new_params =
32
+ if fixed_value_search?
33
+ fixed_value_search_param_values.map { |value| fixed_value_search_params(value, patient_id) }
34
+ else
35
+ [search_params_with_values(patient_id)]
36
+ end
37
+ new_params.reject! do |params|
38
+ params.any? { |_key, value| value.blank? }
39
+ end
40
+
41
+ params[patient_id].concat(new_params)
42
+ end
43
+ end
44
+
45
+ def all_provenance_revinclude_search_params
46
+ @all_provenance_revinclude_search_params ||=
47
+ all_search_params.transform_values! do |params_list|
48
+ params_list.map { |params| params.merge(_revinclude: 'Provenance:target') }
49
+ end
50
+ end
51
+
52
+ def any_valid_search_params?(search_params)
53
+ search_params.any? { |_patient_id, params| params.present? }
54
+ end
55
+
56
+ def run_provenance_revinclude_search_test
57
+ # TODO: skip if not supported?
58
+ skip_if !any_valid_search_params?(all_provenance_revinclude_search_params), unable_to_resolve_params_message
59
+
60
+ provenance_resources =
61
+ all_provenance_revinclude_search_params.flat_map do |_patient_id, params_list|
62
+ params_list.flat_map do |params|
63
+ fhir_search resource_type, params: params
64
+
65
+ perform_search_with_status(params, patient_id) if response[:status] == 400 && possible_status_search?
66
+
67
+ check_search_response
68
+
69
+ fetch_all_bundled_resources(additional_resource_types: ['Provenance'])
70
+ .select { |resource| resource.resourceType == 'Provenance' }
71
+ end
72
+ end
73
+
74
+ scratch_provenance_resources[:all] ||= []
75
+ scratch_provenance_resources[:all].concat(provenance_resources)
76
+
77
+ save_delayed_references(provenance_resources, 'Provenance')
78
+
79
+ skip_if provenance_resources.empty?, no_resources_skip_message('Provenance')
80
+ end
81
+
82
+ def run_search_test
83
+ # TODO: skip if not supported?
84
+ skip_if !any_valid_search_params?(all_search_params), unable_to_resolve_params_message
85
+
86
+ resources_returned =
87
+ all_search_params.flat_map do |patient_id, params_list|
88
+ params_list.flat_map { |params| perform_search(params, patient_id) }
89
+ end
90
+
91
+ skip_if resources_returned.empty?, no_resources_skip_message
92
+
93
+ perform_multiple_or_search_test if multiple_or_search_params.present?
94
+ end
95
+
96
+ def perform_search(params, patient_id)
97
+ fhir_search resource_type, params: params
98
+
99
+ perform_search_with_status(params, patient_id) if response[:status] == 400 && possible_status_search?
100
+
101
+ check_search_response
102
+
103
+ resources_returned =
104
+ fetch_all_bundled_resources.select { |resource| resource.resourceType == resource_type }
105
+
106
+ return [] if resources_returned.blank?
107
+
108
+ perform_comparator_searches(params, patient_id) if params_with_comparators.present?
109
+
110
+ filter_devices(resources_returned) if resource_type == 'Device'
111
+
112
+ if first_search?
113
+ all_scratch_resources.concat(resources_returned).uniq!
114
+ scratch_resources_for_patient(patient_id).concat(resources_returned).uniq!
115
+ end
116
+
117
+ resources_returned.each do |resource|
118
+ check_resource_against_params(resource, params)
119
+ end
120
+
121
+ save_delayed_references(resources_returned) if saves_delayed_references?
122
+
123
+ return resources_returned if all_search_variants_tested?
124
+
125
+ perform_post_search(resources_returned, params) if test_post_search?
126
+ test_medication_inclusion_mr(resources_returned, params, patient_id) if test_medication_inclusion_mr?
127
+ test_medication_inclusion_ms(resources_returned, params, patient_id) if test_medication_inclusion_ms?
128
+ perform_reference_with_type_search(params, resources_returned.count) if test_reference_variants?
129
+ perform_search_with_system(params, patient_id) if token_search_params.present?
130
+
131
+ resources_returned
132
+ end
133
+
134
+ def perform_post_search(get_search_resources, params)
135
+ fhir_search resource_type, params: params, search_method: :post
136
+
137
+ check_search_response
138
+
139
+ post_search_resources = fetch_all_bundled_resources.select { |resource| resource.resourceType == resource_type }
140
+ get_resource_count = get_search_resources.length
141
+ post_resource_count = post_search_resources.length
142
+
143
+ search_variant_test_records[:post_variant] = true
144
+
145
+ assert get_resource_count == post_resource_count,
146
+ "Expected search by POST to return the same results as search by GET, " \
147
+ "but GET search returned #{get_resource_count} resources, and POST search " \
148
+ "returned #{post_resource_count} resources."
149
+ end
150
+
151
+ def filter_devices(resources)
152
+ codes_to_include = implantable_device_codes&.split(',')&.map(&:strip)
153
+ return resources if codes_to_include.blank?
154
+
155
+ resources.select! do |resource|
156
+ resource&.type&.coding&.any? { |coding| codes_to_include.include?(coding.code) }
157
+ end
158
+ end
159
+
160
+ def search_and_check_response(params, resource_type = self.resource_type)
161
+ fhir_search resource_type, params: params
162
+
163
+ check_search_response
164
+ end
165
+
166
+ def check_search_response
167
+ assert_response_status(200)
168
+ assert_resource_type(:bundle)
169
+ # NOTE: how do we want to handle validating Bundles?
170
+ end
171
+
172
+ def search_variant_test_records
173
+ @search_variant_test_records ||= initial_search_variant_test_records
174
+ end
175
+
176
+ def initial_search_variant_test_records
177
+ {}.tap do |records|
178
+ records[:post_variant] = false if test_post_search?
179
+ records[:medication_inclusion] = false if (test_medication_inclusion_mr? && test_medication_inclusion_ms?)
180
+ records[:reference_variants] = false if test_reference_variants?
181
+ records[:token_variants] = false if token_search_params.present?
182
+ records[:comparator_searches] = Set.new if params_with_comparators.present?
183
+ end
184
+ end
185
+
186
+ def all_search_variants_tested?
187
+ search_variant_test_records.all? { |_variant, tested| tested.present? } &&
188
+ all_comparator_searches_tested?
189
+ end
190
+
191
+ def all_comparator_searches_tested?
192
+ return true if params_with_comparators.blank?
193
+
194
+ Set.new(params_with_comparators) == search_variant_test_records[:comparator_searches]
195
+ end
196
+
197
+ def date_comparator_value(comparator, date)
198
+ date = date.start || date.end if date.is_a? FHIR::Period
199
+ case comparator
200
+ when 'lt', 'le'
201
+ comparator + (DateTime.xmlschema(date) + 1).xmlschema
202
+ when 'gt', 'ge'
203
+ comparator + (DateTime.xmlschema(date) - 1).xmlschema
204
+ else
205
+ # ''
206
+ raise "Unsupported comparator '#{comparator}'"
207
+ end
208
+ end
209
+
210
+ def required_comparators(name)
211
+ metadata
212
+ .search_definitions
213
+ .dig(name.to_sym, :comparators)
214
+ .select { |_comparator, expectation| expectation == 'SHALL' }
215
+ .keys
216
+ .map(&:to_s)
217
+ end
218
+
219
+ def perform_comparator_searches(params, patient_id)
220
+ params_with_comparators.each do |name|
221
+ next if search_variant_test_records[:comparator_searches].include? name
222
+
223
+ required_comparators(name).each do |comparator|
224
+ path = search_param_path(name)
225
+ date_element = find_a_value_at(scratch_resources_for_patient(patient_id), path)
226
+ params_with_comparator = params.merge(name => date_comparator_value(comparator, date_element))
227
+
228
+ search_and_check_response(params_with_comparator)
229
+
230
+ fetch_all_bundled_resources
231
+ .each { |resource| check_resource_against_params(resource, params_with_comparator) }
232
+ end
233
+
234
+ search_variant_test_records[:comparator_searches] << name
235
+ end
236
+ end
237
+
238
+ def perform_reference_with_type_search(params, resource_count)
239
+ return if resource_count == 0
240
+ return if search_variant_test_records[:reference_variants]
241
+
242
+ new_search_params = params.merge('patient' => "Patient/#{params['patient']}")
243
+ search_and_check_response(new_search_params)
244
+
245
+ new_resource_count =
246
+ fetch_all_bundled_resources
247
+ .select { |resource| resource.resourceType == resource_type }
248
+ .count
249
+
250
+ assert new_resource_count == resource_count,
251
+ "Expected search by `#{params['patient']}` to to return the same results as searching " \
252
+ "by `#{new_search_params['patient']}`, but found #{resource_count} resources with " \
253
+ "`#{params['patient']}` and #{new_resource_count} with `#{new_search_params['patient']}`"
254
+
255
+ search_variant_test_records[:reference_variants] = true
256
+ end
257
+
258
+ def perform_search_with_system(params, patient_id)
259
+ return if search_variant_test_records[:token_variants]
260
+
261
+ new_search_params = token_search_params.each_with_object({}) do |name, search_params|
262
+ search_params[name] = search_param_value(name, patient_id, include_system: true)
263
+ end
264
+ return if new_search_params.any? { |_name, value| value.blank? }
265
+
266
+ search_params = params.merge(new_search_params)
267
+ search_and_check_response(search_params)
268
+
269
+ resources_returned =
270
+ fetch_all_bundled_resources
271
+ .select { |resource| resource.resourceType == resource_type }
272
+
273
+ assert resources_returned.present?, "No resources were returned when searching by `system|code`"
274
+
275
+ search_variant_test_records[:token_variants] = true
276
+ end
277
+
278
+ def perform_search_with_status(
279
+ original_params,
280
+ patient_id,
281
+ status_search_values: self.status_search_values,
282
+ resource_type: self.resource_type
283
+ )
284
+ assert resource.is_a?(FHIR::OperationOutcome), "Server returned a status of 400 without an OperationOutcome"
285
+ # TODO: warn about documenting status requirements
286
+ status_search_values.flat_map do |status_value|
287
+ search_params = original_params.merge("#{status_search_param_name}": status_value)
288
+
289
+ search_and_check_response(search_params)
290
+
291
+ entries = resource.entry.select { |entry| entry.resource.resourceType == resource_type }
292
+
293
+ if entries.present?
294
+ original_params.merge!("#{status_search_param_name}": status_value)
295
+ break
296
+ end
297
+ end
298
+ end
299
+
300
+ def status_search_param_name
301
+ @status_search_param_name ||=
302
+ metadata.search_definitions.keys.find { |key| key.to_s.include? 'status' }
303
+ end
304
+
305
+ def status_search_values
306
+ default_search_values(status_search_param_name)
307
+ end
308
+
309
+ def default_search_values(param_name)
310
+ definition = metadata.search_definitions[param_name]
311
+ return [] if definition.blank?
312
+
313
+ definition[:multiple_or] == 'SHALL' ? [definition[:values].join(',')] : [definition[:values]]
314
+ end
315
+
316
+
317
+ def perform_multiple_or_search_test
318
+ resolved_one = false
319
+
320
+ all_search_params.each do |patient_id, params_list|
321
+ next unless params_list.present?
322
+
323
+ search_params = params_list.first
324
+ existing_values = {}
325
+ missing_values = {}
326
+
327
+ multiple_or_search_params.each do |param_name|
328
+ search_value = default_search_values(param_name.to_sym)
329
+ search_params = search_params.merge("#{param_name}" => search_value)
330
+ existing_values[param_name.to_sym] = scratch_resources_for_patient(patient_id).map(&param_name.to_sym).compact.uniq
331
+ end
332
+
333
+ # skip patient without multiple-or values
334
+ next if existing_values.values.any?(&:empty?)
335
+
336
+ resolved_one = true
337
+
338
+ search_and_check_response(search_params)
339
+
340
+ resources_returned =
341
+ fetch_all_bundled_resources
342
+ .select { |resource| resource.resourceType == resource_type }
343
+
344
+ multiple_or_search_params.each do |param_name|
345
+ missing_values[param_name.to_sym] = existing_values[param_name.to_sym] - resources_returned.map(&param_name.to_sym)
346
+ end
347
+
348
+ missing_value_message = missing_values
349
+ .reject { |_param_name, missing_value| missing_value.empty? }
350
+ .map { |param_name, missing_value| "#{missing_value.join(',')} values from #{param_name}" }
351
+ .join(' and ')
352
+
353
+ assert missing_value_message.blank?, "Could not find #{missing_value_message} in any of the resources returned for Patient/#{patient_id}"
354
+
355
+ break if resolved_one
356
+ end
357
+ end
358
+
359
+ def test_medication_inclusion_mr(medication_requests, params, patient_id)
360
+ return if search_variant_test_records[:medication_inclusion]
361
+
362
+ scratch[:medication_resources] ||= {}
363
+ scratch[:medication_resources][:all] ||= []
364
+ scratch[:medication_resources][patient_id] ||= []
365
+ scratch[:medication_resources][:contained] ||= []
366
+
367
+ requests_with_external_references =
368
+ medication_requests
369
+ .select { |request| request&.medicationReference&.present? }
370
+ .reject { |request| request&.medicationReference&.reference&.start_with? '#' }
371
+
372
+ contained_medications =
373
+ medication_requests
374
+ .select { |request| request&.medicationReference&.reference&.start_with? '#' }
375
+ .flat_map(&:contained)
376
+ .select { |resource| resource.resourceType == 'Medication' }
377
+
378
+ scratch[:medication_resources][:all] += contained_medications
379
+ scratch[:medication_resources][patient_id] += contained_medications
380
+ scratch[:medication_resources][:contained] += contained_medications
381
+
382
+ return if requests_with_external_references.blank?
383
+
384
+ search_params = params.merge(_include: 'MedicationRequest:medication')
385
+
386
+ search_and_check_response(search_params)
387
+
388
+ medications = fetch_all_bundled_resources.select { |resource| resource.resourceType == 'Medication' }
389
+ assert medications.present?, 'No Medications were included in the search results'
390
+
391
+ medications.uniq!(&:id)
392
+
393
+ scratch[:medication_resources][:all] += medications
394
+ scratch[:medication_resources][patient_id] += medications
395
+
396
+ search_variant_test_records[:medication_inclusion] = true
397
+ end
398
+
399
+ def test_medication_inclusion_ms(medication_requests, params, patient_id)
400
+ return if search_variant_test_records[:medication_inclusion]
401
+
402
+ scratch[:medication_resources] ||= {}
403
+ scratch[:medication_resources][:all] ||= []
404
+ scratch[:medication_resources][patient_id] ||= []
405
+ scratch[:medication_resources][:contained] ||= []
406
+
407
+ requests_with_external_references =
408
+ medication_requests
409
+ .select { |request| request&.medicationReference&.present? }
410
+ .reject { |request| request&.medicationReference&.reference&.start_with? '#' }
411
+
412
+ contained_medications =
413
+ medication_requests
414
+ .select { |request| request&.medicationReference&.reference&.start_with? '#' }
415
+ .flat_map(&:contained)
416
+ .select { |resource| resource.resourceType == 'Medication' }
417
+
418
+ scratch[:medication_resources][:all] += contained_medications
419
+ scratch[:medication_resources][patient_id] += contained_medications
420
+ scratch[:medication_resources][:contained] += contained_medications
421
+
422
+ return if requests_with_external_references.blank?
423
+
424
+ search_params = params.merge(_include: 'MedicationStatement:medication')
425
+
426
+ search_and_check_response(search_params)
427
+
428
+ medications = fetch_all_bundled_resources.select { |resource| resource.resourceType == 'Medication' }
429
+ assert medications.present?, 'No Medications were included in the search results'
430
+
431
+ medications.uniq!(&:id)
432
+
433
+ scratch[:medication_resources][:all] += medications
434
+ scratch[:medication_resources][patient_id] += medications
435
+
436
+ search_variant_test_records[:medication_inclusion] = true
437
+ end
438
+
439
+ def all_scratch_resources
440
+ scratch_resources[:all] ||= []
441
+ end
442
+
443
+ def scratch_resources_for_patient(patient_id)
444
+ return all_scratch_resources if patient_id.nil?
445
+
446
+ scratch_resources[patient_id] ||= []
447
+ end
448
+
449
+ def references_to_save(resource_type = nil)
450
+ reference_metadata = resource_type == 'Provenance' ? provenance_metadata : metadata
451
+ reference_metadata.delayed_references
452
+ end
453
+
454
+ def fixed_value_search_param_name
455
+ (search_param_names - ['patient']).first
456
+ end
457
+
458
+ def fixed_value_search_param_values
459
+ metadata.search_definitions[fixed_value_search_param_name.to_sym][:values]
460
+ end
461
+
462
+ def fixed_value_search_params(value, patient_id)
463
+ search_param_names.each_with_object({}) do |name, params|
464
+ patient_id_param?(name) ? params[name] = patient_id : params[name] = value
465
+ end
466
+ end
467
+
468
+ def search_params_with_values(patient_id)
469
+ search_param_names.each_with_object({}) do |name, params|
470
+ value = patient_id_param?(name) ? patient_id : search_param_value(name, patient_id)
471
+ params[name] = value
472
+ end
473
+ end
474
+
475
+ def patient_id_list
476
+ return [nil] unless respond_to? :patient_ids
477
+
478
+ patient_ids.split(',').map(&:strip)
479
+ end
480
+
481
+ def patient_search?
482
+ search_param_names.any? { |name| patient_id_param? name }
483
+ end
484
+
485
+ def patient_id_param?(name)
486
+ name == 'patient' || (name == '_id' && resource_type == 'Patient')
487
+ end
488
+
489
+ def search_param_path(name)
490
+ path = metadata.search_definitions[name.to_sym][:path]
491
+ path == 'class' ? 'local_class' : path
492
+ end
493
+
494
+ def all_search_params_present?(params)
495
+ params.all? { |_name, value| value.present? }
496
+ end
497
+
498
+ def array_of_codes(array)
499
+ array.map { |name| "`#{name}`" }.join(', ')
500
+ end
501
+
502
+ def unable_to_resolve_params_message
503
+ "Could not find values for all search params #{array_of_codes(search_param_names)}"
504
+ end
505
+
506
+ def empty_search_params_message(empty_search_params)
507
+ "Could not find values for the search parameters #{array_of_codes(empty_search_params.keys)}"
508
+ end
509
+
510
+ def no_resources_skip_message(resource_type = self.resource_type)
511
+ "No #{resource_type} resources appear to be available. " \
512
+ "Please use patients with more information"
513
+ end
514
+
515
+ def fetch_all_bundled_resources(
516
+ reply_handler: nil,
517
+ max_pages: 20,
518
+ additional_resource_types: [],
519
+ resource_type: self.resource_type
520
+ )
521
+ page_count = 1
522
+ resources = []
523
+ bundle = resource
524
+
525
+ until bundle.nil? || page_count == max_pages
526
+ resources += bundle&.entry&.map { |entry| entry&.resource }
527
+ next_bundle_link = bundle&.link&.find { |link| link.relation == 'next' }&.url
528
+ reply_handler&.call(response)
529
+
530
+ break if next_bundle_link.blank?
531
+
532
+ reply = fhir_client.raw_read_url(next_bundle_link)
533
+
534
+ store_request('outgoing') { reply }
535
+ error_message = cant_resolve_next_bundle_message(next_bundle_link)
536
+
537
+ assert_response_status(200)
538
+ assert_valid_json(reply.body, error_message)
539
+
540
+ bundle = fhir_client.parse_reply(FHIR::Bundle, fhir_client.default_format, reply)
541
+
542
+ page_count += 1
543
+ end
544
+
545
+ valid_resource_types = [resource_type, 'OperationOutcome'].concat(additional_resource_types)
546
+ valid_resource_types << 'Medication' if (resource_type == 'MedicationRequest' || resource_type == 'MedicationStatement')
547
+
548
+ all_valid_resource_types =
549
+ resources.all? { |entry| valid_resource_types.include? entry.resourceType }
550
+
551
+ assert all_valid_resource_types,
552
+ "All resources returned must be of the type: #{valid_resource_types.join(', ')}"
553
+
554
+ resources
555
+ end
556
+
557
+ def cant_resolve_next_bundle_message(link)
558
+ "Could not resolve next bundle: #{link}"
559
+ end
560
+
561
+ def search_param_value(name, patient_id, include_system: false)
562
+ path = search_param_path(name)
563
+ element = find_a_value_at(scratch_resources_for_patient(patient_id), path)
564
+ search_value =
565
+ case element
566
+ when FHIR::Period
567
+ if element.start.present?
568
+ 'gt' + (DateTime.xmlschema(element.start) - 1).xmlschema
569
+ else
570
+ end_datetime = get_fhir_datetime_range(element.end)[:end]
571
+ 'lt' + (end_datetime + 1).xmlschema
572
+ end
573
+ when FHIR::Reference
574
+ element.reference
575
+ when FHIR::CodeableConcept
576
+ if include_system
577
+ coding =
578
+ find_a_value_at(element, 'coding') { |coding| coding.code.present? && coding.system.present? }
579
+ coding.present? ? "#{coding.system}|#{coding.code}" : nil
580
+ else
581
+ find_a_value_at(element, 'coding.code')
582
+ end
583
+ when FHIR::Identifier
584
+ if include_system
585
+ identifier = find_a_value_at(scratch_resources_for_patient(patient_id), path) do |identifier|
586
+ identifier.value.present? && identifier.system.present?
587
+ end
588
+ identifier.present? ? "#{identifier.system}|#{identifier.value}" : nil
589
+ else
590
+ element.value
591
+ end
592
+ when FHIR::Coding
593
+ if include_system
594
+ coding = find_a_value_at(scratch_resources_for_patient(patient_id), path) do |coding|
595
+ coding.code.present? && coding.system.present?
596
+ end
597
+ coding.present? ? "#{coding.system}|#{coding.code}" : nil
598
+ else
599
+ element.code
600
+ end
601
+ when FHIR::HumanName
602
+ element.family || element.given&.first || element.text
603
+ when FHIR::Address
604
+ element.text || element.city || element.state || element.postalCode || element.country
605
+ else
606
+ element
607
+ end
608
+ escaped_value = search_value&.gsub(',', '\\,')
609
+ escaped_value
610
+ end
611
+
612
+ def save_resource_reference(resource_type, reference)
613
+ scratch[:references] ||= {}
614
+ scratch[:references][resource_type] ||= Set.new
615
+ scratch[:references][resource_type] << reference
616
+ end
617
+
618
+ def save_delayed_references(resources, containing_resource_type = self.resource_type)
619
+ resources.each do |resource|
620
+ references_to_save(containing_resource_type).each do |reference_to_save|
621
+ resolve_path(resource, reference_to_save[:path])
622
+ .select { |reference| reference.is_a?(FHIR::Reference) && !reference.contained? }
623
+ .each do |reference|
624
+ resource_type = reference.resource_class.name.demodulize
625
+ need_to_save = reference_to_save[:resources].include?(resource_type)
626
+ next unless need_to_save
627
+
628
+ save_resource_reference(resource_type, reference)
629
+ end
630
+ end
631
+ end
632
+ end
633
+
634
+ #### RESULT CHECKING ####
635
+
636
+ def check_resource_against_params(resource, params)
637
+ params.each do |name, search_value|
638
+ path = search_param_path(name)
639
+ type = metadata.search_definitions[name.to_sym][:type]
640
+ values_found =
641
+ resolve_path(resource, path)
642
+ .map do |value|
643
+ if value.is_a? FHIR::Reference
644
+ value.reference
645
+ else
646
+ value
647
+ end
648
+ end
649
+
650
+ match_found =
651
+ case type
652
+ when 'Period', 'date', 'instant', 'dateTime'
653
+ values_found.any? { |date| validate_date_search(search_value, date) }
654
+ when 'HumanName'
655
+ # When a string search parameter refers to the types HumanName and Address,
656
+ # the search covers the elements of type string, and does not cover elements such as use and period
657
+ # https://www.hl7.org/fhir/search.html#string
658
+ search_value_downcase = search_value.downcase
659
+ values_found.any? do |name|
660
+ name&.text&.downcase&.start_with?(search_value_downcase) ||
661
+ name&.family&.downcase&.start_with?(search_value_downcase) ||
662
+ name&.given&.any? { |given| given.downcase.start_with?(search_value_downcase) } ||
663
+ name&.prefix&.any? { |prefix| prefix.downcase.start_with?(search_value_downcase) } ||
664
+ name&.suffix&.any? { |suffix| suffix.downcase.start_with?(search_value_downcase) }
665
+ end
666
+ when 'Address'
667
+ search_value_downcase = search_value.downcase
668
+ values_found.any? do |address|
669
+ address&.text&.downcase&.start_with?(search_value_downcase) ||
670
+ address&.city&.downcase&.start_with?(search_value_downcase) ||
671
+ address&.state&.downcase&.start_with?(search_value_downcase) ||
672
+ address&.postalCode&.downcase&.start_with?(search_value_downcase) ||
673
+ address&.country&.downcase&.start_with?(search_value_downcase)
674
+ end
675
+ when 'CodeableConcept'
676
+ # FHIR token search (https://www.hl7.org/fhir/search.html#token): "When in doubt, servers SHOULD
677
+ # treat tokens in a case-insensitive manner, on the grounds that including undesired data has
678
+ # less safety implications than excluding desired behavior".
679
+ codings = values_found.flat_map(&:coding)
680
+ if search_value.include? '|'
681
+ system = search_value.split('|').first
682
+ code = search_value.split('|').last
683
+ codings&.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
684
+ else
685
+ codings&.any? { |coding| coding.code&.casecmp?(search_value) }
686
+ end
687
+ when 'Coding'
688
+ if search_value.include? '|'
689
+ system = search_value.split('|').first
690
+ code = search_value.split('|').last
691
+ values_found.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
692
+ else
693
+ values_found.any? { |coding| coding.code&.casecmp?(search_value) }
694
+ end
695
+ when 'Identifier'
696
+ if search_value.include? '|'
697
+ values_found.any? { |identifier| "#{identifier.system}|#{identifier.value}" == search_value }
698
+ else
699
+ values_found.any? { |identifier| identifier.value == search_value }
700
+ end
701
+ when 'string'
702
+ searched_values = search_value.downcase.split(/(?<!\\\\),/).map{ |string| string.gsub('\\,', ',') }
703
+ values_found.any? do |value_found|
704
+ searched_values.any? { |searched_value| value_found.downcase.starts_with? searched_value }
705
+ end
706
+ else
707
+ # searching by patient requires special case because we are searching by a resource identifier
708
+ # references can also be URL's, so we made need to resolve those url's
709
+ if ['subject', 'patient'].include? name.to_s
710
+ id = search_value.split('Patient/').last
711
+ possible_values = [id, 'Patient/' + id, "#{url}/Patient/\#{id}"]
712
+ values_found.any? do |reference|
713
+ possible_values.include? reference
714
+ end
715
+ else
716
+ search_values = search_value.split(/(?<!\\\\),/).map { |string| string.gsub('\\,', ',') }
717
+ values_found.any? { |value_found| search_values.include? value_found }
718
+ end
719
+ end
720
+
721
+ assert match_found,
722
+ "#{resource_type}/#{resource.id} did not match the search parameters:\n" \
723
+ "* Expected: #{search_value}\n" \
724
+ "* Found: #{values_found.map(&:inspect).join(', ')}"
725
+ end
726
+ end
727
+ end
728
+ end