davinci_crd_test_kit 0.12.2 → 0.14.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 (341) hide show
  1. checksums.yaml +4 -4
  2. data/config/presets/inferno_crd_client_suite.json.erb +20 -14
  3. data/config/presets/inferno_crd_client_suite_prefetch_subset_v221.json.erb +125 -0
  4. data/config/presets/inferno_crd_client_suite_v221.json.erb +124 -0
  5. data/config/presets/inferno_crd_server_suite.json.erb +59 -2
  6. data/config/presets/inferno_crd_server_suite_v221.json.erb +94 -0
  7. data/config/presets/ri_crd_request_generator.json_v221.json.erb +13 -0
  8. data/config/presets/ri_crd_server.json.erb +19 -19
  9. data/lib/davinci_crd_test_kit/client/client_base_urls.rb +80 -0
  10. data/lib/davinci_crd_test_kit/{client_hook_request_validation.rb → client/client_hook_request_validation.rb} +1 -1
  11. data/lib/davinci_crd_test_kit/client/crd_client_options.rb +30 -0
  12. data/lib/davinci_crd_test_kit/client/endpoints/cds_services_discovery_handler.rb +34 -0
  13. data/lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb +342 -0
  14. data/lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb +410 -0
  15. data/lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb +233 -0
  16. data/lib/davinci_crd_test_kit/{mock_service_response.rb → client/endpoints/mock_service_response.rb} +165 -59
  17. data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/companions_prerequisites.json +1 -0
  18. data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/create_update_coverage_information.json +3 -2
  19. data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/launch_smart_app.json +8 -1
  20. data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/propose_alternate_request.json +1 -0
  21. data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/request_form_completion.json +17 -16
  22. data/lib/davinci_crd_test_kit/client/multi_request_message_helper.rb +35 -0
  23. data/lib/davinci_crd_test_kit/client/tagged_request_load_helper.rb +38 -0
  24. data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_create_test.rb +43 -0
  25. data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_read_test.rb +43 -0
  26. data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_search_test.rb +234 -0
  27. data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_update_test.rb +43 -0
  28. data/lib/davinci_crd_test_kit/client/v2.0.1/api/client_fhir_api_validation_test.rb +63 -0
  29. data/lib/davinci_crd_test_kit/client/v2.0.1/auth/decode_auth_token_test.rb +65 -0
  30. data/lib/davinci_crd_test_kit/client/v2.0.1/auth/retrieve_jwks_test.rb +109 -0
  31. data/lib/davinci_crd_test_kit/client/v2.0.1/auth/token_header_test.rb +70 -0
  32. data/lib/davinci_crd_test_kit/client/v2.0.1/auth/token_payload_test.rb +85 -0
  33. data/lib/davinci_crd_test_kit/{routes/cds-services.json → client/v2.0.1/cds-services-v201.json} +1 -1
  34. data/lib/davinci_crd_test_kit/client/v2.0.1/client_appointment_book_group.rb +108 -0
  35. data/lib/davinci_crd_test_kit/client/v2.0.1/client_card_must_support_group.rb +31 -0
  36. data/lib/davinci_crd_test_kit/client/v2.0.1/client_encounter_discharge_group.rb +105 -0
  37. data/lib/davinci_crd_test_kit/client/v2.0.1/client_encounter_start_group.rb +105 -0
  38. data/lib/davinci_crd_test_kit/client/v2.0.1/client_fhir_api_group.rb +790 -0
  39. data/lib/davinci_crd_test_kit/client/v2.0.1/client_hooks_group.rb +74 -0
  40. data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_dispatch_group.rb +111 -0
  41. data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_select_group.rb +116 -0
  42. data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_sign_group.rb +113 -0
  43. data/lib/davinci_crd_test_kit/{client_registration_group.rb → client/v2.0.1/client_registration_group.rb} +12 -8
  44. data/lib/davinci_crd_test_kit/client/v2.0.1/client_urls.rb +13 -0
  45. data/lib/davinci_crd_test_kit/client/v2.0.1/crd_client_suite.rb +134 -0
  46. data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/appointment_book_receive_request_test.rb +129 -0
  47. data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/encounter_discharge_receive_request_test.rb +126 -0
  48. data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/encounter_start_receive_request_test.rb +126 -0
  49. data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/order_dispatch_receive_request_test.rb +138 -0
  50. data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/order_select_receive_request_test.rb +134 -0
  51. data/lib/davinci_crd_test_kit/client/v2.0.1/invocation/order_sign_receive_request_test.rb +136 -0
  52. data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_coverage_information.rb +93 -0
  53. data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_external_reference.rb +62 -0
  54. data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_instructions.rb +62 -0
  55. data/lib/davinci_crd_test_kit/client/v2.0.1/registration/client_registration_verification_test.rb +94 -0
  56. data/lib/davinci_crd_test_kit/client/v2.0.1/registration/client_service_registration_attestation_test.rb +40 -0
  57. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_fetched_data_test.rb +86 -0
  58. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_optional_fields_test.rb +63 -0
  59. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_prefetch_equals_queried_test.rb +96 -0
  60. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_required_fields_test.rb +55 -0
  61. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_valid_context_test.rb +70 -0
  62. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_valid_prefetch_test.rb +62 -0
  63. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_response/client_display_cards_attest.rb +83 -0
  64. data/lib/davinci_crd_test_kit/client/v2.0.1/verify_response/inferno_response_validation.rb +79 -0
  65. data/lib/davinci_crd_test_kit/client/v2.2.1/api/client_coverage_info_update_test.rb +212 -0
  66. data/lib/davinci_crd_test_kit/client/v2.2.1/api/client_crd_update_verification_group.rb +18 -0
  67. data/lib/davinci_crd_test_kit/client/v2.2.1/auth/decode_auth_token_test.rb +69 -0
  68. data/lib/davinci_crd_test_kit/client/v2.2.1/auth/retrieve_jwks_test.rb +120 -0
  69. data/lib/davinci_crd_test_kit/client/v2.2.1/auth/token_header_test.rb +92 -0
  70. data/lib/davinci_crd_test_kit/client/v2.2.1/auth/token_payload_test.rb +93 -0
  71. data/lib/davinci_crd_test_kit/client/v2.2.1/cds-services-prefetch-subset-v221.json +198 -0
  72. data/lib/davinci_crd_test_kit/client/v2.2.1/cds-services-v221.json +202 -0
  73. data/lib/davinci_crd_test_kit/client/v2.2.1/client_appointment_book_group.rb +102 -0
  74. data/lib/davinci_crd_test_kit/client/v2.2.1/client_cross_hook_group.rb +28 -0
  75. data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_discharge_group.rb +96 -0
  76. data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_start_group.rb +95 -0
  77. data/lib/davinci_crd_test_kit/client/v2.2.1/client_fhir_api_group.rb +88 -0
  78. data/lib/davinci_crd_test_kit/client/v2.2.1/client_hooks_group.rb +64 -0
  79. data/lib/davinci_crd_test_kit/client/v2.2.1/client_long_running_hook_group.rb +32 -0
  80. data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_dispatch_group.rb +101 -0
  81. data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_select_group.rb +102 -0
  82. data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_sign_group.rb +107 -0
  83. data/lib/davinci_crd_test_kit/client/v2.2.1/client_registration_group.rb +27 -0
  84. data/lib/davinci_crd_test_kit/client/v2.2.1/client_urls.rb +27 -0
  85. data/lib/davinci_crd_test_kit/client/v2.2.1/crd_client_suite.rb +229 -0
  86. data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_card_must_support_coverage_information_test.rb +63 -0
  87. data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_fhirpath_collection_as_comma_delimited_string_test.rb +60 -0
  88. data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_hook_instances_unique_test.rb +45 -0
  89. data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_location_address_propagation_test.rb +135 -0
  90. data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_prefetch_complete_and_subset_test.rb +103 -0
  91. data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/appointment_book_receive_request_test.rb +156 -0
  92. data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/encounter_discharge_receive_request_test.rb +157 -0
  93. data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/encounter_start_receive_request_test.rb +157 -0
  94. data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_dispatch_receive_request_test.rb +165 -0
  95. data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_select_receive_request_test.rb +165 -0
  96. data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_sign_receive_request_test.rb +165 -0
  97. data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_long_running_receive_request_test.rb +64 -0
  98. data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_skip_long_running_attestation_test.rb +49 -0
  99. data/lib/davinci_crd_test_kit/client/v2.2.1/registration/client_registration_verification_test.rb +161 -0
  100. data/lib/davinci_crd_test_kit/client/v2.2.1/registration/client_service_registration_attestation_test.rb +107 -0
  101. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_conformance_test.rb +47 -0
  102. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_coverage_verification_test.rb +152 -0
  103. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_data_fetch_verification_test.rb +55 -0
  104. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_granted_scopes_test.rb +123 -0
  105. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_complete_test.rb +127 -0
  106. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_profiles_test.rb +55 -0
  107. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_requested_version_test.rb +54 -0
  108. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_secured_transport_test.rb +48 -0
  109. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/client_display_cards_attest.rb +74 -0
  110. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/hook_response_support_coverage_information_test.rb +30 -0
  111. data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/inferno_response_validation.rb +77 -0
  112. data/lib/davinci_crd_test_kit/cross_suite/base_urls.rb +20 -0
  113. data/lib/davinci_crd_test_kit/cross_suite/cards_identification.rb +312 -0
  114. data/lib/davinci_crd_test_kit/{cards_validation.rb → cross_suite/cards_validation.rb} +104 -47
  115. data/lib/davinci_crd_test_kit/cross_suite/coverage-information_stu201_metadata.yml +27 -0
  116. data/lib/davinci_crd_test_kit/cross_suite/coverage-information_stu221_metadata.yml +60 -0
  117. data/lib/davinci_crd_test_kit/cross_suite/fhirpath_on_cds_request.rb +177 -0
  118. data/lib/davinci_crd_test_kit/{hook_request_field_validation.rb → cross_suite/hook_request_field_validation.rb} +282 -203
  119. data/lib/davinci_crd_test_kit/cross_suite/logical_models_override_helper.rb +220 -0
  120. data/lib/davinci_crd_test_kit/cross_suite/prefetch_completeness_checker.rb +462 -0
  121. data/lib/davinci_crd_test_kit/cross_suite/prefetch_contents_validation.rb +81 -0
  122. data/lib/davinci_crd_test_kit/cross_suite/prefetch_profile_validation.rb +48 -0
  123. data/lib/davinci_crd_test_kit/cross_suite/profiles_and_resource_types.rb +63 -0
  124. data/lib/davinci_crd_test_kit/cross_suite/replace_tokens.rb +38 -0
  125. data/lib/davinci_crd_test_kit/cross_suite/requests_logical_model_validation.rb +202 -0
  126. data/lib/davinci_crd_test_kit/cross_suite/response_logical_model_validation.rb +274 -0
  127. data/lib/davinci_crd_test_kit/{suggestion_actions_validation.rb → cross_suite/suggestion_actions_validation.rb} +70 -50
  128. data/lib/davinci_crd_test_kit/cross_suite/tags.rb +42 -0
  129. data/lib/davinci_crd_test_kit/metadata.rb +10 -44
  130. data/lib/davinci_crd_test_kit/requirements/cds-hooks-library_1.0.1_requirements.xlsx +0 -0
  131. data/lib/davinci_crd_test_kit/requirements/cds-hooks_2.0_requirements.xlsx +0 -0
  132. data/lib/davinci_crd_test_kit/requirements/cds-hooks_3.0.0-ballot_requirements.xlsx +0 -0
  133. data/lib/davinci_crd_test_kit/requirements/davinci_crd_test_kit_requirements.csv +742 -65
  134. data/lib/davinci_crd_test_kit/requirements/generated/crd_client_requirements_coverage.csv +279 -54
  135. data/lib/davinci_crd_test_kit/requirements/generated/crd_client_v221_requirements_coverage.csv +1430 -0
  136. data/lib/davinci_crd_test_kit/requirements/generated/crd_server_requirements_coverage.csv +36 -45
  137. data/lib/davinci_crd_test_kit/requirements/generated/crd_server_v221_requirements_coverage.csv +143 -0
  138. data/lib/davinci_crd_test_kit/requirements/hl7.fhir.us.davinci-crd_2.0.1_requirements.xlsx +0 -0
  139. data/lib/davinci_crd_test_kit/requirements/hl7.fhir.us.davinci-crd_2.2.1_requirements.xlsx +0 -0
  140. data/lib/davinci_crd_test_kit/server/endpoints/jwk_set_endpoint_handler.rb +13 -0
  141. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_create_endpoint.rb +23 -0
  142. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_delete_endpoint.rb +30 -0
  143. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_metadata_endpoint.rb +112 -0
  144. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_read_endpoint.rb +21 -0
  145. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_request_handler.rb +261 -0
  146. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_search_endpoint.rb +561 -0
  147. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/fhir_update_endpoint.rb +24 -0
  148. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr/stress-test-Bundle.json +54687 -0
  149. data/lib/davinci_crd_test_kit/server/endpoints/mock_ehr_endpoints.rb +95 -0
  150. data/lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb +225 -0
  151. data/lib/davinci_crd_test_kit/{jwt_helper.rb → server/jwt_helper.rb} +1 -12
  152. data/lib/davinci_crd_test_kit/server/resource_extractor.rb +68 -0
  153. data/lib/davinci_crd_test_kit/server/server_abstract_invoke_hook_test.rb +165 -0
  154. data/lib/davinci_crd_test_kit/server/server_base_urls.rb +30 -0
  155. data/lib/davinci_crd_test_kit/{server_hook_helper.rb → server/server_hook_helper.rb} +1 -1
  156. data/lib/davinci_crd_test_kit/{server_hook_request_validation.rb → server/server_hook_request_validation.rb} +1 -1
  157. data/lib/davinci_crd_test_kit/{test_helper.rb → server/server_test_helper.rb} +7 -3
  158. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Appointment.yml +5 -0
  159. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/ClaimResponse.yml +5 -0
  160. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/CommunicationRequest.yml +5 -0
  161. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Coverage.yml +21 -0
  162. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Device.yml +5 -0
  163. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/DeviceRequest.yml +5 -0
  164. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Encounter.yml +7 -0
  165. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Location.yml +5 -0
  166. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/MedicationRequest.yml +5 -0
  167. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/NutritionOrder.yml +5 -0
  168. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Organization.yml +5 -0
  169. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Patient.yml +5 -0
  170. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Practitioner.yml +5 -0
  171. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/PractitionerRole.yml +40 -0
  172. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/ServiceRequest.yml +5 -0
  173. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/Task.yml +5 -0
  174. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_metadata/VisionPrescription.yml +5 -0
  175. data/lib/davinci_crd_test_kit/server/v2.0.1/crd_server_suite.rb +99 -0
  176. data/lib/davinci_crd_test_kit/server/v2.0.1/discovery/discovery_endpoint_test.rb +90 -0
  177. data/lib/davinci_crd_test_kit/server/v2.0.1/discovery/discovery_services_validation_test.rb +67 -0
  178. data/lib/davinci_crd_test_kit/server/v2.0.1/interaction/server_invoke_hook_test.rb +12 -0
  179. data/lib/davinci_crd_test_kit/server/v2.0.1/must_support/coverage_information_system_action_across_hooks_validation_test.rb +34 -0
  180. data/lib/davinci_crd_test_kit/server/v2.0.1/must_support/external_reference_card_across_hooks_validation_test.rb +30 -0
  181. data/lib/davinci_crd_test_kit/server/v2.0.1/must_support/instructions_card_received_across_hooks_test.rb +27 -0
  182. data/lib/davinci_crd_test_kit/server/v2.0.1/server_appointment_book_group.rb +191 -0
  183. data/lib/davinci_crd_test_kit/server/v2.0.1/server_demonstrate_hook_response_group.rb +93 -0
  184. data/lib/davinci_crd_test_kit/server/v2.0.1/server_discovery_group.rb +62 -0
  185. data/lib/davinci_crd_test_kit/server/v2.0.1/server_encounter_discharge_group.rb +186 -0
  186. data/lib/davinci_crd_test_kit/server/v2.0.1/server_encounter_start_group.rb +186 -0
  187. data/lib/davinci_crd_test_kit/server/v2.0.1/server_hooks_group.rb +73 -0
  188. data/lib/davinci_crd_test_kit/server/v2.0.1/server_order_dispatch_group.rb +191 -0
  189. data/lib/davinci_crd_test_kit/server/v2.0.1/server_order_select_group.rb +211 -0
  190. data/lib/davinci_crd_test_kit/server/v2.0.1/server_order_sign_group.rb +216 -0
  191. data/lib/davinci_crd_test_kit/server/v2.0.1/server_required_card_response_validation_group.rb +28 -0
  192. data/lib/davinci_crd_test_kit/server/v2.0.1/server_urls.rb +13 -0
  193. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_request/service_request_context_validation_test.rb +30 -0
  194. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_request/service_request_optional_fields_validation_test.rb +39 -0
  195. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_request/service_request_required_fields_validation_test.rb +40 -0
  196. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/additional_orders_validation_test.rb +59 -0
  197. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/card_optional_fields_validation_test.rb +51 -0
  198. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/coverage_information_system_action_received_test.rb +65 -0
  199. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/coverage_information_system_action_validation_test.rb +120 -0
  200. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/create_or_update_coverage_info_response_validation_test.rb +70 -0
  201. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/external_reference_card_validation_test.rb +37 -0
  202. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/form_completion_response_validation_test.rb +67 -0
  203. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/instructions_card_received_test.rb +30 -0
  204. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/launch_smart_app_card_validation_test.rb +39 -0
  205. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/propose_alternate_request_card_validation_test.rb +46 -0
  206. data/lib/davinci_crd_test_kit/server/v2.0.1/verify_response/service_response_validation_test.rb +83 -0
  207. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Appointment_withorder.yml +5 -0
  208. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Appointment_withoutorder.yml +5 -0
  209. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/CommunicationRequest.yml +5 -0
  210. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Coverage.yml +5 -0
  211. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Device.yml +5 -0
  212. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/DeviceRequest.yml +5 -0
  213. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Encounter.yml +5 -0
  214. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Location.yml +5 -0
  215. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/MedicationRequest.yml +5 -0
  216. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/NutritionOrder.yml +5 -0
  217. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Organization.yml +5 -0
  218. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/Patient.yml +5 -0
  219. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/PractitionerRole.yml +5 -0
  220. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/ServiceRequest.yml +5 -0
  221. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_metadata/VisionPrescription.yml +5 -0
  222. data/lib/davinci_crd_test_kit/server/v2.2.1/crd_server_suite.rb +115 -0
  223. data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_configuration_test.rb +159 -0
  224. data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_endpoint_test.rb +90 -0
  225. data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_prefetch_support_test.rb +43 -0
  226. data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_services_validation_test.rb +121 -0
  227. data/lib/davinci_crd_test_kit/server/v2.2.1/interaction/server_invoke_hook_test.rb +17 -0
  228. data/lib/davinci_crd_test_kit/server/v2.2.1/must_support/coverage_information_must_support_test.rb +71 -0
  229. data/lib/davinci_crd_test_kit/server/v2.2.1/must_support/coverage_information_system_action_across_hooks_validation_test.rb +36 -0
  230. data/lib/davinci_crd_test_kit/server/v2.2.1/must_support/supported_us_core_versions_test.rb +118 -0
  231. data/lib/davinci_crd_test_kit/server/v2.2.1/server_appointment_book_group.rb +213 -0
  232. data/lib/davinci_crd_test_kit/server/v2.2.1/server_demonstrate_hook_response_group.rb +93 -0
  233. data/lib/davinci_crd_test_kit/server/v2.2.1/server_discovery_group.rb +69 -0
  234. data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_discharge_group.rb +194 -0
  235. data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_start_group.rb +194 -0
  236. data/lib/davinci_crd_test_kit/server/v2.2.1/server_hooks_group.rb +73 -0
  237. data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_dispatch_group.rb +214 -0
  238. data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_select_group.rb +219 -0
  239. data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_sign_group.rb +241 -0
  240. data/lib/davinci_crd_test_kit/server/v2.2.1/server_required_card_response_validation_group.rb +30 -0
  241. data/lib/davinci_crd_test_kit/server/v2.2.1/server_urls.rb +13 -0
  242. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_context_validation_test.rb +30 -0
  243. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_no_custom_extensions_test.rb +120 -0
  244. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_optional_fields_validation_test.rb +39 -0
  245. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_required_fields_validation_test.rb +40 -0
  246. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/additional_orders_validation_test.rb +66 -0
  247. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/all_responses_include_coverage_information_test.rb +123 -0
  248. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/card_optional_fields_validation_test.rb +57 -0
  249. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_info_configuration_test.rb +83 -0
  250. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_received_test.rb +65 -0
  251. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_validation_test.rb +184 -0
  252. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/create_or_update_coverage_info_response_validation_test.rb +75 -0
  253. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/external_reference_card_validation_test.rb +47 -0
  254. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/form_completion_response_validation_test.rb +91 -0
  255. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/hook_request_resource_resolution.rb +137 -0
  256. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/instructions_card_received_test.rb +32 -0
  257. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/launch_smart_app_card_validation_test.rb +49 -0
  258. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/order_dispatch_coverage_information_test.rb +38 -0
  259. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/propose_alternate_request_card_validation_test.rb +54 -0
  260. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/service_response_validation_test.rb +97 -0
  261. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_cds_hooks_elements_test.rb +78 -0
  262. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_configuration_test.rb +78 -0
  263. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_context_test.rb +78 -0
  264. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/verify_response_without_billing_options_test.rb +43 -0
  265. data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/verify_response_without_configuration_test.rb +44 -0
  266. data/lib/davinci_crd_test_kit/version.rb +2 -2
  267. data/lib/davinci_crd_test_kit.rb +4 -2
  268. metadata +308 -101
  269. data/lib/davinci_crd_test_kit/client_fhir_api_group.rb +0 -785
  270. data/lib/davinci_crd_test_kit/client_hooks_group.rb +0 -75
  271. data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +0 -93
  272. data/lib/davinci_crd_test_kit/client_tests/client_appointment_book_group.rb +0 -75
  273. data/lib/davinci_crd_test_kit/client_tests/client_display_cards_attest.rb +0 -48
  274. data/lib/davinci_crd_test_kit/client_tests/client_encounter_discharge_group.rb +0 -73
  275. data/lib/davinci_crd_test_kit/client_tests/client_encounter_start_group.rb +0 -73
  276. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_create_test.rb +0 -41
  277. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_read_test.rb +0 -39
  278. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_search_test.rb +0 -232
  279. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_update_test.rb +0 -41
  280. data/lib/davinci_crd_test_kit/client_tests/client_fhir_api_validation_test.rb +0 -60
  281. data/lib/davinci_crd_test_kit/client_tests/client_order_dispatch_group.rb +0 -79
  282. data/lib/davinci_crd_test_kit/client_tests/client_order_select_group.rb +0 -82
  283. data/lib/davinci_crd_test_kit/client_tests/client_order_sign_group.rb +0 -81
  284. data/lib/davinci_crd_test_kit/client_tests/client_registration_verification_test.rb +0 -88
  285. data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +0 -60
  286. data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +0 -90
  287. data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +0 -90
  288. data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +0 -57
  289. data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +0 -49
  290. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +0 -68
  291. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +0 -69
  292. data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +0 -102
  293. data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +0 -98
  294. data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +0 -101
  295. data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +0 -105
  296. data/lib/davinci_crd_test_kit/client_tests/submitted_response_validation.rb +0 -44
  297. data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +0 -65
  298. data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +0 -78
  299. data/lib/davinci_crd_test_kit/crd_client_suite.rb +0 -185
  300. data/lib/davinci_crd_test_kit/crd_options.rb +0 -9
  301. data/lib/davinci_crd_test_kit/crd_server_suite.rb +0 -125
  302. data/lib/davinci_crd_test_kit/igs/davinci-crd-2.0.1.tgz +0 -0
  303. data/lib/davinci_crd_test_kit/routes/cds_services_discovery_handler.rb +0 -18
  304. data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +0 -77
  305. data/lib/davinci_crd_test_kit/routes/jwk_set_endpoint_handler.rb +0 -15
  306. data/lib/davinci_crd_test_kit/server_appointment_book_group.rb +0 -176
  307. data/lib/davinci_crd_test_kit/server_demonstrate_hook_response_group.rb +0 -77
  308. data/lib/davinci_crd_test_kit/server_discovery_group.rb +0 -60
  309. data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +0 -170
  310. data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +0 -170
  311. data/lib/davinci_crd_test_kit/server_hooks_group.rb +0 -71
  312. data/lib/davinci_crd_test_kit/server_order_dispatch_group.rb +0 -176
  313. data/lib/davinci_crd_test_kit/server_order_select_group.rb +0 -195
  314. data/lib/davinci_crd_test_kit/server_order_sign_group.rb +0 -201
  315. data/lib/davinci_crd_test_kit/server_required_card_response_validation_group.rb +0 -26
  316. data/lib/davinci_crd_test_kit/server_tests/additional_orders_validation_test.rb +0 -68
  317. data/lib/davinci_crd_test_kit/server_tests/card_optional_fields_validation_test.rb +0 -47
  318. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_across_hooks_validation_test.rb +0 -32
  319. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +0 -63
  320. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_validation_test.rb +0 -118
  321. data/lib/davinci_crd_test_kit/server_tests/create_or_update_coverage_info_response_validation_test.rb +0 -71
  322. data/lib/davinci_crd_test_kit/server_tests/discovery_endpoint_test.rb +0 -88
  323. data/lib/davinci_crd_test_kit/server_tests/discovery_services_validation_test.rb +0 -65
  324. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_across_hooks_validation_test.rb +0 -28
  325. data/lib/davinci_crd_test_kit/server_tests/external_reference_card_validation_test.rb +0 -36
  326. data/lib/davinci_crd_test_kit/server_tests/form_completion_response_validation_test.rb +0 -78
  327. data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_across_hooks_test.rb +0 -25
  328. data/lib/davinci_crd_test_kit/server_tests/instructions_card_received_test.rb +0 -26
  329. data/lib/davinci_crd_test_kit/server_tests/launch_smart_app_card_validation_test.rb +0 -38
  330. data/lib/davinci_crd_test_kit/server_tests/propose_alternate_request_card_validation_test.rb +0 -63
  331. data/lib/davinci_crd_test_kit/server_tests/service_call_test.rb +0 -101
  332. data/lib/davinci_crd_test_kit/server_tests/service_request_context_validation_test.rb +0 -28
  333. data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +0 -37
  334. data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +0 -38
  335. data/lib/davinci_crd_test_kit/server_tests/service_response_validation_test.rb +0 -81
  336. data/lib/davinci_crd_test_kit/tags.rb +0 -10
  337. data/lib/davinci_crd_test_kit/urls.rb +0 -52
  338. /data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/external_reference.json +0 -0
  339. /data/lib/davinci_crd_test_kit/{card_responses → client/endpoints/mocked_card_responses}/instructions.json +0 -0
  340. /data/lib/davinci_crd_test_kit/{crd_jwks.json → server/endpoints/crd_jwks.json} +0 -0
  341. /data/lib/davinci_crd_test_kit/{jwks.rb → server/endpoints/jwks.rb} +0 -0
@@ -0,0 +1,561 @@
1
+ require_relative 'fhir_request_handler'
2
+ require 'us_core_test_kit'
3
+
4
+ module DaVinciCRDTestKit
5
+ module MockEHR
6
+ class FHIRSearchEndpoint < Inferno::DSL::SuiteEndpoint
7
+ include Inferno::DSL::FHIRResourceNavigation
8
+ include FHIRRequestHandler
9
+
10
+ def make_response
11
+ prepare_response
12
+ return unless mock_ehr_bundle_present?
13
+ return unless resource_type_present?
14
+
15
+ return_response_bundle
16
+ rescue StandardError => e
17
+ return_unhandled_error(e)
18
+ end
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Search Response
22
+ # ---------------------------------------------------------------------------
23
+
24
+ def return_response_bundle
25
+ response.status = 200
26
+ response.body = response_bundle.to_json
27
+ end
28
+
29
+ def response_bundle
30
+ bundle = FHIR::Bundle.new({ type: 'searchset' })
31
+ bundle.link << FHIR::Bundle::Link.new({ relation: 'self', url: self_link })
32
+
33
+ matching_entry_list.each do |entry|
34
+ bundle.entry << FHIR::Bundle::Entry.new({ resource: entry.resource,
35
+ search: FHIR::Bundle::Entry::Search.new({ mode: 'match' }) })
36
+ end
37
+ included_entry_list.each do |entry|
38
+ bundle.entry << FHIR::Bundle::Entry.new({ resource: entry.resource,
39
+ search: FHIR::Bundle::Entry::Search.new({ mode: 'include' }) })
40
+ end
41
+ revincluded_entry_list.each do |entry|
42
+ bundle.entry << FHIR::Bundle::Entry.new({ resource: entry.resource,
43
+ search: FHIR::Bundle::Entry::Search.new({ mode: 'include' }) })
44
+ end
45
+
46
+ bundle
47
+ end
48
+
49
+ # ---------------------------------------------------------------------------
50
+ # Search Metadata
51
+ # ---------------------------------------------------------------------------
52
+
53
+ def self.metadata_files
54
+ nil
55
+ end
56
+
57
+ def self.metadata_by_resource_type
58
+ @metadata_by_resource_type ||=
59
+ metadata_files&.each_with_object(Hash.new { |h, k| h[k] = [] }) do |path, by_type|
60
+ metadata = USCoreTestKit::Generator::GroupMetadata.new(YAML.load_file(path, aliases: true))
61
+ by_type[metadata.resource] << metadata
62
+ end
63
+ end
64
+
65
+ def metadata_directory
66
+ File.join(Gem::Specification.find_by_name('us_core_test_kit').gem_dir,
67
+ 'lib', 'us_core_test_kit', 'generated', 'v6.1.0')
68
+ end
69
+
70
+ def metadata_list
71
+ @metadata_list ||= load_metadata_list
72
+ end
73
+
74
+ def load_metadata_list
75
+ return self.class.metadata_by_resource_type&.dig(resource_type) || [] if self.class.metadata_files
76
+
77
+ Dir.glob(File.join(metadata_directory, "#{resource_type.underscore}*", 'metadata.yml')).filter_map do |path|
78
+ m = USCoreTestKit::Generator::GroupMetadata.new(YAML.load_file(path, aliases: true))
79
+ m if m.resource == resource_type
80
+ end
81
+ end
82
+
83
+ def merged_search_definitions
84
+ @merged_search_definitions ||= metadata_list.each_with_object({}) do |m, defs|
85
+ m.search_definitions&.each do |name, definition|
86
+ defs[name] ||= definition
87
+ end
88
+ end
89
+ end
90
+
91
+ def all_include_params
92
+ @all_include_params ||= metadata_list.flat_map { |m| m.include_params || [] }.uniq
93
+ end
94
+
95
+ def all_references
96
+ @all_references ||= metadata_list.flat_map { |m| m.references || [] }.uniq
97
+ end
98
+
99
+ def all_must_support_elements
100
+ @all_must_support_elements ||= metadata_list.flat_map { |m| m.must_supports&.dig(:elements) || [] }.uniq
101
+ end
102
+
103
+ # ---------------------------------------------------------------------------
104
+ # Self Link search parameters identification
105
+ # ---------------------------------------------------------------------------
106
+
107
+ def self_link
108
+ host_and_path = request.url.split('?').first
109
+ param_list =
110
+ (supported_search_param_names + supported_include_param_paths.keys).map do |name|
111
+ "#{name}=#{request_params[name]}"
112
+ end
113
+ param_list << '_revinclude=Provenance:target' if revinclude_provenance_target?
114
+ query_string = param_list.present? ? "?#{param_list.join('&')}" : ''
115
+
116
+ "#{host_and_path}#{query_string}"
117
+ end
118
+
119
+ def supported_search_param_names
120
+ @supported_search_param_names ||=
121
+ request_params.keys.select { |name| merged_search_definitions.key?(name.to_sym) }
122
+ end
123
+
124
+ def revinclude_provenance_target?
125
+ request_params.any? { |key, value| key == '_revinclude' && value == 'Provenance:target' }
126
+ end
127
+
128
+ # map from include name to a list of paths at which to find references to include
129
+ def supported_include_param_paths
130
+ @supported_include_param_paths ||= calculate_supported_includes
131
+ end
132
+
133
+ def calculate_supported_includes
134
+ request_params.keys.each_with_object({}) do |name, includes|
135
+ next unless name == '_include'
136
+
137
+ target = request_params[name]
138
+ next unless target.present? && all_include_params.include?(target)
139
+
140
+ paths = paths_to_include(target)
141
+ next unless paths.present?
142
+
143
+ includes[name] = paths
144
+ end
145
+ end
146
+
147
+ def paths_to_include(target)
148
+ paths = paths_from_search_definition(target)
149
+ paths = paths_from_reference_list(target) unless paths.present?
150
+ paths = paths_from_must_support_element(target) unless paths.present?
151
+ paths
152
+ end
153
+
154
+ def paths_from_search_definition(target)
155
+ search_parameter = target.split(':').last
156
+ definition = merged_search_definitions[search_parameter.to_sym]
157
+ return unless definition.present? && definition[:type] == 'Reference'
158
+
159
+ definition[:full_paths]
160
+ end
161
+
162
+ def paths_from_reference_list(target)
163
+ path_with_resource_type = target.gsub(':', '.')
164
+ return unless all_references.any? { |ref| ref[:path] == path_with_resource_type }
165
+
166
+ [path_with_resource_type]
167
+ end
168
+
169
+ def paths_from_must_support_element(target)
170
+ target_element = target.split(':').last
171
+ return unless all_must_support_elements.any? { |elt| element_matches_reference_target?(elt, target_element) }
172
+
173
+ [target.gsub(':', '.')]
174
+ end
175
+
176
+ def element_matches_reference_target?(element, target_element)
177
+ element[:types]&.include?('Reference') &&
178
+ [target_element, "#{target_element}[x]"].include?(element[:path])
179
+ end
180
+
181
+ # ---------------------------------------------------------------------------
182
+ # Search Logic
183
+ # ---------------------------------------------------------------------------
184
+
185
+ def matching_entry_list
186
+ @matching_entry_list ||= mock_ehr_bundle.entry&.select do |entry|
187
+ entry.resource.present? &&
188
+ entry.resource.resourceType == resource_type &&
189
+ include_resource_in_search_results?(entry.resource)
190
+ end
191
+ end
192
+
193
+ def matching_entry_set
194
+ @matching_entry_set ||= matching_entry_list.each_with_object(Set.new) do |entry, set|
195
+ set << [entry.resource.resourceType, entry.resource.id]
196
+ end
197
+ end
198
+
199
+ def request_params
200
+ @request_params ||= request.params.to_h.except(:resource_type).stringify_keys
201
+ end
202
+
203
+ def include_resource_in_search_results?(resource)
204
+ supported_search_param_names.reduce(true) do |matches_so_far, name|
205
+ return false unless matches_so_far
206
+
207
+ escaped_search_value = request_params[name]
208
+ values_found = []
209
+ resource_matches_param?(resource, name, escaped_search_value, values_found)
210
+ end
211
+ end
212
+
213
+ def unescape_search_value(value)
214
+ value&.gsub('\\,', ',')
215
+ end
216
+
217
+ def search_param_paths(name)
218
+ paths = merged_search_definitions.dig(name.to_sym, :paths)
219
+ paths[0] = 'local_class' if paths.first == 'class'
220
+
221
+ paths
222
+ end
223
+
224
+ def resource_matches_param?(resource, search_param_name, escaped_search_value, values_found = []) # rubocop:disable Metrics/CyclomaticComplexity
225
+ search_value = unescape_search_value(escaped_search_value)
226
+ paths = search_param_paths(search_param_name)
227
+
228
+ match_found = false
229
+
230
+ paths.each do |path|
231
+ type = merged_search_definitions.dig(search_param_name.to_sym, :type)
232
+
233
+ resolve_path(resource, path).each do |value|
234
+ values_found <<
235
+ if value.is_a? FHIR::Reference
236
+ value.reference
237
+ elsif value.is_a? Inferno::DSL::PrimitiveType
238
+ value.value
239
+ else
240
+ value
241
+ end
242
+ end
243
+
244
+ values_found.compact!
245
+ match_found =
246
+ case type
247
+ when 'Period', 'date', 'instant', 'dateTime'
248
+ values_found.any? { |date| validate_date_search(search_value, date) }
249
+ when 'HumanName'
250
+ # When a string search parameter refers to the types HumanName and Address,
251
+ # the search covers the elements of type string, and does not cover elements such as use and period
252
+ # https://www.hl7.org/fhir/search.html#string
253
+ search_value_downcase = search_value.downcase
254
+ values_found.any? do |name|
255
+ name&.text&.downcase&.start_with?(search_value_downcase) ||
256
+ name&.family&.downcase&.start_with?(search_value_downcase) ||
257
+ name&.given&.any? { |given| given.downcase.start_with?(search_value_downcase) } ||
258
+ name&.prefix&.any? { |prefix| prefix.downcase.start_with?(search_value_downcase) } ||
259
+ name&.suffix&.any? { |suffix| suffix.downcase.start_with?(search_value_downcase) }
260
+ end
261
+ when 'Address'
262
+ search_value_downcase = search_value.downcase
263
+ values_found.any? do |address|
264
+ address&.text&.downcase&.start_with?(search_value_downcase) ||
265
+ address&.city&.downcase&.start_with?(search_value_downcase) ||
266
+ address&.state&.downcase&.start_with?(search_value_downcase) ||
267
+ address&.postalCode&.downcase&.start_with?(search_value_downcase) ||
268
+ address&.country&.downcase&.start_with?(search_value_downcase)
269
+ end
270
+ when 'CodeableConcept'
271
+ # FHIR token search (https://www.hl7.org/fhir/search.html#token): "When in doubt, servers SHOULD
272
+ # treat tokens in a case-insensitive manner, on the grounds that including undesired data has
273
+ # less safety implications than excluding desired behavior".
274
+ codings = values_found.flat_map(&:coding)
275
+ if search_value.include? '|'
276
+ system = search_value.split('|').first
277
+ code = search_value.split('|').last
278
+ codings&.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
279
+ else
280
+ codings&.any? { |coding| coding.code&.casecmp?(search_value) }
281
+ end
282
+ when 'Coding'
283
+ if search_value.include? '|'
284
+ system = search_value.split('|').first
285
+ code = search_value.split('|').last
286
+ values_found.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
287
+ else
288
+ values_found.any? { |coding| coding.code&.casecmp?(search_value) }
289
+ end
290
+ when 'Identifier'
291
+ if search_value.include? '|'
292
+ values_found.any? { |identifier| "#{identifier.system}|#{identifier.value}" == search_value }
293
+ else
294
+ values_found.any? { |identifier| identifier.value == search_value }
295
+ end
296
+ when 'string'
297
+ searched_values = search_value.downcase.split(/(?<!\\\\),/).map { |string| string.gsub('\\,', ',') }
298
+ values_found.any? do |value_found|
299
+ searched_values.any? { |searched_value| value_found.downcase.starts_with? searched_value }
300
+ end
301
+ else
302
+ # searching by patient requires special case because we are searching by a resource identifier
303
+ # references can also be URLs, so we may need to resolve those URLs
304
+ if ['subject', 'patient'].include? search_param_name.to_s
305
+ id = search_value.split('Patient/').last
306
+ possible_values = [id, "Patient/#{id}"] # TODO: "#{url}/Patient/#{id}"] - allow absolute references?
307
+ values_found.any? do |reference|
308
+ possible_values.include? reference
309
+ end
310
+ else
311
+ search_values = search_value.split(/(?<!\\\\),/).map { |string| string.gsub('\\,', ',') }
312
+ values_found.any? { |value_found| search_values.include? value_found }
313
+ end
314
+ end
315
+
316
+ break if match_found
317
+ end
318
+
319
+ match_found
320
+ end
321
+
322
+ def get_fhir_datetime_range(datetime)
323
+ range = { start: DateTime.xmlschema(datetime), end: nil }
324
+ range[:end] =
325
+ case datetime
326
+ when /^\d{4}$/ # YYYY
327
+ range[:start].next_year - 1.seconds
328
+ when /^\d{4}-\d{2}$/ # YYYY-MM
329
+ range[:start].next_month - 1.seconds
330
+ when /^\d{4}-\d{2}-\d{2}$/ # YYYY-MM-DD
331
+ range[:start].next_day - 1.seconds
332
+ else # YYYY-MM-DDThh:mm:ss+zz:zz
333
+ range[:start]
334
+ end
335
+ range
336
+ end
337
+
338
+ def get_fhir_period_range(period)
339
+ range = { start: nil, end: nil }
340
+ range[:start] = DateTime.xmlschema(period.start) unless period.start.nil?
341
+ return range if period.end.nil?
342
+
343
+ period_end_beginning = DateTime.xmlschema(period.end)
344
+ range[:end] =
345
+ case period.end
346
+ when /^\d{4}$/ # YYYY
347
+ period_end_beginning.next_year - 1.seconds
348
+ when /^\d{4}-\d{2}$/ # YYYY-MM
349
+ period_end_beginning.next_month - 1.seconds
350
+ when /^\d{4}-\d{2}-\d{2}$/ # YYYY-MM-DD
351
+ period_end_beginning.next_day - 1.seconds
352
+ else # YYYY-MM-DDThh:mm:ss+zz:zz
353
+ period_end_beginning
354
+ end
355
+ range
356
+ end
357
+
358
+ # NOTE: this is different from the US Core implementation for le and ge
359
+ # See https://jira.hl7.org/browse/FHIR-51068
360
+ def fhir_date_comparer(search_range, target_range, comparator, extend_start: false, extend_end: false) # rubocop:disable Metrics/CyclomaticComplexity
361
+ # Implicitly, a missing lower boundary is "less than" any actual date.
362
+ # A missing upper boundary is "greater than" any actual date.
363
+ case comparator
364
+ when 'eq' # the range of the search value fully contains the range of the target value
365
+ !target_range[:start].nil? && !target_range[:end].nil? && search_range[:start] <= target_range[:start] &&
366
+ search_range[:end] >= target_range[:end]
367
+ when 'ne' # the range of the search value does not fully contain the range of the target value
368
+ target_range[:start].nil? ||
369
+ target_range[:end].nil? ||
370
+ search_range[:start] > target_range[:start] ||
371
+ search_range[:end] < target_range[:end]
372
+ when 'gt' # the range above the search value intersects (i.e. overlaps) with the range of the target value
373
+ target_range[:end].nil? ||
374
+ search_range[:end] < target_range[:end] ||
375
+ (search_range[:end] < (target_range[:end] + 1) && extend_end)
376
+ when 'lt' # the range below the search value intersects (i.e. overlaps) with the range of the target value
377
+ target_range[:start].nil? ||
378
+ search_range[:start] > target_range[:start] ||
379
+ (search_range[:start] > (target_range[:start] - 1) && extend_start)
380
+ when 'ge' # the target end is at or after the search range start
381
+ target_range[:end].nil? || target_range[:end] >= search_range[:start]
382
+ when 'le' # the target start is at or before the search range end
383
+ target_range[:start].nil? || target_range[:start] <= search_range[:end]
384
+ when 'sa' # the range above the search value contains the range of the target value
385
+ !target_range[:start].nil? && search_range[:end] < target_range[:start]
386
+ when 'eb' # the range below the search value contains the range of the target value
387
+ !target_range[:end].nil? && search_range[:start] > target_range[:end]
388
+ when 'ap' # the range of the search value overlaps with the range of the target value
389
+ if target_range[:start].nil? || target_range[:end].nil?
390
+ (target_range[:start].nil? && search_range[:start] < target_range[:end]) ||
391
+ (target_range[:end].nil? && search_range[:end] > target_range[:start])
392
+ else
393
+ search_range[:start].between?(target_range[:start], target_range[:end]) ||
394
+ search_range[:end].between?(target_range[:start], target_range[:end])
395
+ end
396
+ end
397
+ end
398
+
399
+ def validate_date_search(search_value, target_value)
400
+ if target_value.instance_of? FHIR::Period
401
+ validate_period_search(search_value, target_value)
402
+ else
403
+ validate_datetime_search(search_value, target_value)
404
+ end
405
+ end
406
+
407
+ def validate_datetime_search(search_value, target_value)
408
+ comparator = search_value[0..1]
409
+ if ['eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa', 'eb', 'ap'].include? comparator
410
+ search_value = search_value[2..]
411
+ else
412
+ comparator = 'eq'
413
+ end
414
+ search_is_date = date?(search_value)
415
+ target_is_date = date?(target_value)
416
+ search_range = get_fhir_datetime_range(search_value)
417
+ target_range = get_fhir_datetime_range(target_value)
418
+ fhir_date_comparer(search_range, target_range, comparator, extend_start: !search_is_date && target_is_date,
419
+ extend_end: !search_is_date && target_is_date)
420
+ end
421
+
422
+ def validate_period_search(search_value, target_value)
423
+ comparator = search_value[0..1]
424
+ if ['eq', 'ge', 'gt', 'le', 'lt', 'ne', 'sa', 'eb', 'ap'].include? comparator
425
+ search_value = search_value[2..]
426
+ else
427
+ comparator = 'eq'
428
+ end
429
+ search_is_date = date?(search_value)
430
+ search_range = get_fhir_datetime_range(search_value)
431
+ target_range = get_fhir_period_range(target_value)
432
+ fhir_date_comparer(search_range, target_range, comparator,
433
+ extend_start: !search_is_date && date?(target_value.start),
434
+ extend_end: !search_is_date && date?(target_value.end))
435
+ end
436
+
437
+ def date?(value)
438
+ /^\d{4}(-\d{2})?(-\d{2})?$/.match?(value) # YYYY or YYYY-MM or YYYY-MM-DD
439
+ end
440
+
441
+ # ---------------------------------------------------------------------------
442
+ # _include logic
443
+ # ---------------------------------------------------------------------------
444
+
445
+ def included_entry_list
446
+ @included_entry_list ||= begin
447
+ entries = []
448
+ included_set = Set.new
449
+ matching_entry_list.each do |entry|
450
+ resource = entry.resource
451
+ next unless include_paths_by_resource_type.key?(resource.resourceType)
452
+
453
+ include_paths_by_resource_type[resource.resourceType].each do |target_include_path|
454
+ resolve_path(resource, target_include_path).each do |reference|
455
+ evaluate_included_reference(reference, entries, included_set)
456
+ end
457
+ end
458
+ end
459
+ @included_entry_set = included_set
460
+ entries
461
+ end
462
+ end
463
+
464
+ def included_entry_set
465
+ included_entry_list # ensure built, which populates @included_entry_set
466
+ @included_entry_set ||= Set.new
467
+ end
468
+
469
+ def evaluate_included_reference(reference, included_entries, included_set)
470
+ return unless local_reference?(reference)
471
+
472
+ resource_type, resource_id = reference.reference.split('/')
473
+ key = [resource_type, resource_id]
474
+ return if included_set.include?(key) || matching_entry_set.include?(key)
475
+
476
+ included_entry = resolve_reference_to_entry_in_bundle(resource_type, resource_id)
477
+ return unless included_entry.present?
478
+
479
+ included_entries << included_entry
480
+ included_set << key
481
+ end
482
+
483
+ def include_paths_by_resource_type
484
+ @include_paths_by_resource_type ||=
485
+ supported_include_param_paths.values.each_with_object({}) do |include_paths, by_resource_type|
486
+ include_paths.each do |path_with_resource_type|
487
+ resource_type, path = path_with_resource_type.split('.', 2)
488
+ (by_resource_type[resource_type] ||= []) << path
489
+ end
490
+ end
491
+ end
492
+
493
+ def local_reference?(reference)
494
+ reference.is_a?(FHIR::Reference) &&
495
+ reference.reference.split('/').length == 2 # local reference
496
+ end
497
+
498
+ def bundle_entry_index
499
+ @bundle_entry_index ||= mock_ehr_bundle.entry.each_with_object({}) do |entry, index|
500
+ next unless entry.resource.present?
501
+
502
+ index[[entry.resource.resourceType, entry.resource.id]] = entry
503
+ end
504
+ end
505
+
506
+ def resolve_reference_to_entry_in_bundle(resource_type, resource_id)
507
+ bundle_entry_index[[resource_type, resource_id]]
508
+ end
509
+
510
+ # ---------------------------------------------------------------------------
511
+ # _revinclude logic
512
+ # ---------------------------------------------------------------------------
513
+ def revincluded_entry_list
514
+ @revincluded_entry_list ||= revinclude_provenance_target? ? build_revincluded_entry_list : []
515
+ end
516
+
517
+ def revincluded_entry_set
518
+ revincluded_entry_list # ensure built, which populates @revincluded_entry_set
519
+ @revincluded_entry_set ||= Set.new
520
+ end
521
+
522
+ def build_revincluded_entry_list
523
+ entries = []
524
+ revincluded_set = Set.new
525
+ mock_ehr_bundle.entry.each do |entry|
526
+ next unless candidate_provenance_for_revinclude?(entry, revincluded_set)
527
+
528
+ resolve_path(entry.resource, 'target').each do |reference|
529
+ evaluate_revincluded_reference(entry, reference, entries, revincluded_set)
530
+ end
531
+ end
532
+ @revincluded_entry_set = revincluded_set
533
+ entries
534
+ end
535
+
536
+ def candidate_provenance_for_revinclude?(entry, revincluded_set)
537
+ return false unless entry.resource.present?
538
+ return false unless entry.resource.resourceType == 'Provenance'
539
+
540
+ key = [entry.resource.resourceType, entry.resource.id]
541
+ !revincluded_set.include?(key) && !included_entry_set.include?(key) &&
542
+ !matching_entry_set.include?(key)
543
+ end
544
+
545
+ def evaluate_revincluded_reference(referencing_entry, reference, revincluded_entries, revincluded_set)
546
+ return unless local_reference?(reference)
547
+
548
+ resource_type, resource_id = reference.reference.split('/')
549
+ return unless matching_entry_set.include?([resource_type, resource_id]) ||
550
+ included_entry_set.include?([resource_type, resource_id])
551
+
552
+ provenance_key = [referencing_entry.resource.resourceType, referencing_entry.resource.id]
553
+ return if revincluded_set.include?(provenance_key)
554
+
555
+ # referenced instance is matched or included — add the referencing Provenance
556
+ revincluded_entries << referencing_entry
557
+ revincluded_set << provenance_key
558
+ end
559
+ end
560
+ end
561
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'fhir_request_handler'
2
+
3
+ module DaVinciCRDTestKit
4
+ module MockEHR
5
+ class FHIRUpdateEndpoint < Inferno::DSL::SuiteEndpoint
6
+ include FHIRRequestHandler
7
+
8
+ def make_response
9
+ prepare_response
10
+ return unless mock_ehr_bundle_present?
11
+ return unless resource_type_present?
12
+ return unless resource_id_present?
13
+ return unless provided_resource_valid?
14
+
15
+ assign_id_to_provided_resource(target_id: resource_id)
16
+ update_target_resource_in_mock_ehr_bundle
17
+
18
+ return_provided_resource(status: nil) # status set in update_target_resource_in_mock_ehr_bundle
19
+ rescue StandardError => e
20
+ return_unhandled_error(e)
21
+ end
22
+ end
23
+ end
24
+ end