ipa_test_kit 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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