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
@@ -1,12 +1,12 @@
1
+ require_relative 'requests_logical_model_validation'
2
+ require_relative '../server/server_base_urls'
3
+ require_relative 'profiles_and_resource_types'
4
+
1
5
  module DaVinciCRDTestKit
2
6
  module HookRequestFieldValidation
3
- def request_number
4
- if @request_number.blank?
5
- ''
6
- else
7
- "Request #{@request_number}: "
8
- end
9
- end
7
+ include RequestsLogicalModelValidation
8
+ include ServerBaseURLs
9
+ include ProfilesAndResourceTypes
10
10
 
11
11
  def json_parse(json)
12
12
  JSON.parse(json)
@@ -15,6 +15,115 @@ module DaVinciCRDTestKit
15
15
  false
16
16
  end
17
17
 
18
+ def hook_request_required_fields_check(request_body, hook_name, ig_version: 'v201') # rubocop:disable Lint/UnusedMethodArgument
19
+ hook_request_required_fields_check_presence_and_types(request_body)
20
+
21
+ if request_body['hook'] != hook_name
22
+ add_message('error',
23
+ "#{request_number}The `hook` field should be #{hook_name}, but was #{request_body['hook']}")
24
+ end
25
+
26
+ if request_body['fhirAuthorization'].present? && request_body['fhirServer'].blank?
27
+ add_message('error', %(
28
+ #{request_number}Missing `fhirServer` field: If `fhirAuthorization` is provided, this field is
29
+ #REQUIRED.))
30
+ end
31
+
32
+ return unless request_body['hookInstance'].present? && !guid?(request_body['hookInstance'])
33
+
34
+ add_message('error', "#{request_number}`hookInstance` field must be a globally unique UUID.")
35
+ end
36
+
37
+ def hook_request_optional_fields_check(request_body, ig_version: 'v201') # rubocop:disable Lint/UnusedMethodArgument
38
+ hook_request_optional_fields_check_type(request_body)
39
+
40
+ defined_fields = hook_required_fields.keys + hook_optional_fields.keys
41
+ request_body.keys.reject { |field| defined_fields.include?(field) }.each do |field|
42
+ if request_body[field].blank?
43
+ add_message('error', "#{request_number}Hook request field `#{field}` cannot be defined but blank.")
44
+ end
45
+ end
46
+
47
+ hook_request_fhir_auth_check(request_body)
48
+ end
49
+
50
+ def hook_request_context_check(context, hook_name, ig_version: 'v201')
51
+ required_fields =
52
+ if hook_name == 'order-dispatch'
53
+ context_required_order_dispatch_fields[ig_version]
54
+ else
55
+ context_required_fields_by_hook[hook_name]
56
+ end
57
+ required_fields.each do |field, type|
58
+ validate_presence_and_type(context, field, type,
59
+ "#{request_number}#{hook_name} request context")
60
+ end
61
+ context_validate_optional_fields(context, hook_name, ig_version:)
62
+ hook_specific_context_check(context, hook_name, ig_version:)
63
+ end
64
+
65
+ def hook_request_prefetch_check(advertised_prefetch_fields, received_prefetch, received_context, ig_version: 'v201')
66
+ received_prefetch.each do |received_prefetch_key, received_prefetch_hash|
67
+ unless advertised_prefetch_fields.key?(received_prefetch_key)
68
+ add_message('error', "#{request_number}Client sent non-requested Prefetch field `#{received_prefetch_key}`.")
69
+ next
70
+ end
71
+
72
+ unless received_prefetch_hash.is_a?(Hash)
73
+ add_message('error', "#{request_number}Prefetch field `#{received_prefetch_key}` is not of type `Hash`.")
74
+ next
75
+ end
76
+
77
+ received_prefetch_resource = FHIR.from_contents(received_prefetch[received_prefetch_key].to_json)
78
+ advertised_prefetch_template = advertised_prefetch_fields[received_prefetch_key]
79
+ if advertised_prefetch_template.include?('?')
80
+ advertised_prefetch_fhir_search = advertised_prefetch_template.gsub(/{|}/, '').split('?')
81
+ advertised_prefetch_resource_type = advertised_prefetch_fhir_search.first
82
+
83
+ if advertised_prefetch_resource_type == 'Coverage'
84
+ advertised_coverage_query_params = Rack::Utils.parse_nested_query(advertised_prefetch_fhir_search.last)
85
+
86
+ advertised_patient_token = advertised_coverage_query_params['patient']
87
+ advertised_context_patient_id_key = advertised_patient_token.split('.').last
88
+ received_context_patient_id = received_context[advertised_context_patient_id_key]
89
+
90
+ advertised_status_param = advertised_coverage_query_params['status']
91
+
92
+ validate_prefetch_coverages(received_prefetch_resource, received_prefetch_key, received_context_patient_id,
93
+ advertised_status_param, ig_version:)
94
+ end
95
+ else
96
+ advertised_prefetch_token = advertised_prefetch_template.gsub(/{|}/, '').split('/')
97
+ advertised_context_id = advertised_prefetch_token.last.split('.').last
98
+
99
+ if advertised_prefetch_token.length == 1
100
+ received_context_reference = FHIR::Reference.new(reference: received_context[advertised_context_id])
101
+ received_context_resource_type = received_context_reference.resource_type
102
+ received_context_id = received_context_reference.reference_id
103
+ else
104
+ received_context_id = received_context[advertised_context_id]
105
+ received_context_resource_type = advertised_prefetch_token.first
106
+ end
107
+ validate_prefetch_resource(received_prefetch_resource, received_prefetch_key,
108
+ received_context_resource_type, received_context_id, ig_version:)
109
+ end
110
+ end
111
+ end
112
+
113
+ def no_error_validation(message)
114
+ assert messages.none? { |msg| msg[:type] == 'error' }, message
115
+ end
116
+
117
+ private
118
+
119
+ def request_number
120
+ if @request_number.blank?
121
+ ''
122
+ else
123
+ "Request #{@request_number}: "
124
+ end
125
+ end
126
+
18
127
  def hook_required_fields
19
128
  {
20
129
  'hook' => String,
@@ -51,16 +160,28 @@ module DaVinciCRDTestKit
51
160
  'encounter-start' => common_context_fields.merge('encounterId' => String),
52
161
  'encounter-discharge' => common_context_fields.merge('encounterId' => String),
53
162
  'order-select' => common_context_fields.merge('selections' => Array, 'draftOrders' => Hash),
54
- 'order-dispatch' => { 'patientId' => String, 'order' => String, 'performer' => String },
55
163
  'order-sign' => common_context_fields.merge('draftOrders' => Hash)
56
164
  }.freeze
57
165
  end
58
166
 
167
+ def context_required_order_dispatch_fields
168
+ {
169
+ 'v201' => { 'patientId' => String, 'order' => String, 'performer' => String },
170
+ 'v221' => { 'patientId' => String, 'dispatchedOrders' => Array, 'performer' => String }
171
+ }
172
+ end
173
+
174
+ def context_optional_order_dispatch_fields
175
+ {
176
+ 'v201' => { 'task' => Hash },
177
+ 'v221' => { 'fulfillmentTasks' => Array }
178
+ }
179
+ end
180
+
59
181
  def context_optional_fields_by_hook
60
182
  {
61
183
  'appointment-book' => { 'encounterId' => String },
62
184
  'order-select' => { 'encounterId' => String },
63
- 'order-dispatch' => { 'task' => Hash },
64
185
  'order-sign' => { 'encounterId' => String }
65
186
  }.freeze
66
187
  end
@@ -82,29 +203,7 @@ module DaVinciCRDTestKit
82
203
  }.freeze
83
204
  end
84
205
 
85
- def structure_definition_map
86
- {
87
- 'Practitioner' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-practitioner',
88
- 'PractitionerRole' => 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitionerrole',
89
- 'Patient' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-patient',
90
- 'Encounter' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-encounter',
91
- 'Appointment' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-appointment',
92
- 'DeviceRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-devicerequest',
93
- 'MedicationRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-medicationrequest',
94
- 'NutritionOrder' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-nutritionorder',
95
- 'ServiceRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-servicerequest',
96
- 'VisionPrescription' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-visionprescription',
97
- 'Medication' => 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medication',
98
- 'Device' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-device',
99
- 'CommunicationRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-communicationrequest',
100
- 'Task' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-taskquestionnaire',
101
- 'Coverage' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-coverage',
102
- 'Location' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-location',
103
- 'Organization' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-organization'
104
- }.freeze
105
- end
106
-
107
- def hook_request_required_fields_check(request_body, hook_name)
206
+ def hook_request_required_fields_check_presence_and_types(request_body)
108
207
  hook_required_fields.each do |field, type|
109
208
  if request_body[field].blank?
110
209
  add_message('error', "#{request_number}Hook request did not contain required field: `#{field}`")
@@ -112,22 +211,10 @@ module DaVinciCRDTestKit
112
211
  end
113
212
 
114
213
  unless request_body[field].is_a?(type)
115
- add_message('error', "#{request_number}Hook request field #{field} is not of type #{type}")
214
+ add_message('error', "#{request_number}Hook request field `#{field}` is not of type #{type}")
116
215
  next
117
216
  end
118
217
  end
119
-
120
- if request_body['hook'] != hook_name
121
- add_message('error',
122
- "#{request_number}The `hook` field should be #{hook_name}, but was #{request_body['hook']}")
123
- return
124
- end
125
-
126
- return unless request_body['fhirAuthorization'].present? && request_body['fhirServer'].blank?
127
-
128
- add_message('error', %(
129
- #{request_number}Missing `fhirServer` field: If `fhirAuthorization` is provided, this field is
130
- #REQUIRED.))
131
218
  end
132
219
 
133
220
  def fhir_auth_fields_valid?(fhir_authorization_required_fields, fhir_authorization)
@@ -138,10 +225,18 @@ module DaVinciCRDTestKit
138
225
  fhir_auth_valid = false
139
226
  end
140
227
  unless fhir_authorization[field].is_a?(type)
141
- add_message('error', "#{request_number}`fhirAuthorization` field #{field} is not of type #{type}")
228
+ add_message('error', "#{request_number}`fhirAuthorization` field `#{field}` is not of type #{type}")
229
+ fhir_auth_valid = false
230
+ end
231
+ end
232
+
233
+ fhir_authorization.keys.reject { |field| fhir_authorization_required_fields.key?(field) }.each do |field|
234
+ if fhir_authorization[field].blank?
235
+ add_message('error', "#{request_number}`fhirAuthorization` field `#{field}` cannot be defined but blank.")
142
236
  fhir_auth_valid = false
143
237
  end
144
238
  end
239
+
145
240
  fhir_auth_valid
146
241
  end
147
242
 
@@ -174,20 +269,25 @@ module DaVinciCRDTestKit
174
269
  { fhir_server_uri: request_body['fhirServer'], fhir_access_token: access_token }
175
270
  end
176
271
 
177
- def hook_request_optional_fields_check(request_body)
272
+ def hook_request_optional_fields_check_type(request_body)
178
273
  hook_optional_fields.each do |field, type|
179
- info "#{request_number}Hook request did not contain optional field: `#{field}`" if request_body[field].blank?
274
+ if request_body[field].blank?
275
+ if request_body.key?(field)
276
+ add_message('error', "#{request_number}Hook request field `#{field}` cannot be defined but blank.")
277
+ else
278
+ info "#{request_number}Hook request did not contain optional field: `#{field}`"
279
+ end
280
+ end
180
281
 
181
282
  if request_body[field] && !request_body[field].is_a?(type)
182
- add_message('error', "#{request_number}Hook request field #{field} is not of type #{type}")
283
+ add_message('error', "#{request_number}Hook request field `#{field}` is not of type #{type}")
183
284
  end
184
285
  end
185
- hook_request_fhir_auth_check(request_body)
186
286
  end
187
287
 
188
- def validate_presence_and_type(object, field_name, type, description = '')
288
+ def validate_presence_and_type(object, field_name, type, description = '', required: true)
189
289
  value = object[field_name]
190
- unless value
290
+ if required && !value
191
291
  error_msg = "#{description} does not contain required field `#{field_name}`: #{description} `#{object}`."
192
292
  add_message('error', error_msg)
193
293
  return
@@ -197,44 +297,33 @@ module DaVinciCRDTestKit
197
297
  unless is_valid_type
198
298
  error_msg = type == 'URL' ? 'is not a valid URL' : "is not of type `#{type}`"
199
299
  add_message('error', "#{description} field `#{field_name}` #{error_msg}: #{description} `#{object}`.")
200
- return
201
300
  end
202
301
 
203
- return unless value.blank?
302
+ return if value.present?
204
303
 
205
304
  error_msg = "#{description} field `#{field_name}` should not be an empty #{type}: #{description} `#{object}`."
206
305
  add_message('error', error_msg)
207
306
  end
208
307
 
209
- def hook_request_context_check(context, hook_name)
210
- required_fields = context_required_fields_by_hook[hook_name]
211
- required_fields.each do |field, type|
212
- validate_presence_and_type(context, field, type,
213
- "#{request_number}#{hook_name} request context")
214
- end
215
- context_validate_optional_fields(context, hook_name)
216
- hook_specific_context_check(context, hook_name)
217
- end
218
-
219
- def hook_specific_context_check(context, hook_name)
308
+ def hook_specific_context_check(context, hook_name, ig_version: 'v201')
220
309
  case hook_name
221
310
  when 'appointment-book'
222
- appointment_book_context_check(context)
311
+ appointment_book_context_check(context, ig_version:)
223
312
  when 'encounter-start', 'encounter-discharge'
224
- encounter_start_or_discharge_context_check(context, hook_name)
313
+ encounter_start_or_discharge_context_check(context, hook_name, ig_version:)
225
314
  when 'order-select', 'order-sign'
226
- order_select_or_sign_context_check(context, hook_name)
315
+ order_select_or_sign_context_check(context, hook_name, ig_version:)
227
316
  when 'order-dispatch'
228
- order_dispatch_context_check(context)
317
+ order_dispatch_context_check(context, ig_version:)
229
318
  end
230
319
  end
231
320
 
232
- def hook_user_type_check(context, hook_name)
321
+ def hook_user_type_check(context, hook_name, ig_version: 'v201')
233
322
  supported_resource_types = context_user_types_by_hook[hook_name]
234
- resource_reference_check(context['userId'], 'userId', supported_resource_types:)
323
+ resource_reference_check(context['userId'], 'userId', supported_resource_types:, ig_version:)
235
324
  end
236
325
 
237
- def resource_reference_check(reference, field_name, supported_resource_types: nil)
326
+ def resource_reference_check(reference, field_name, supported_resource_types: nil, ig_version: 'v201')
238
327
  return unless reference.is_a?(String) && valid_reference_format?(reference, field_name)
239
328
 
240
329
  resource_type, resource_id = reference.split('/')
@@ -248,7 +337,9 @@ module DaVinciCRDTestKit
248
337
  return
249
338
  end
250
339
 
251
- query_and_validate_id_field(resource_type, resource_id) if client_test? && !field_name.include?('selections')
340
+ return unless client_test? && !field_name.include?('selections')
341
+
342
+ query_and_validate_id_field(resource_type, resource_id, ig_version:)
252
343
  end
253
344
 
254
345
  def valid_reference_format?(reference, field_name)
@@ -262,14 +353,14 @@ module DaVinciCRDTestKit
262
353
  false
263
354
  end
264
355
 
265
- def id_only_fields_check(hook_name, context, id_fields)
356
+ def id_only_fields_check(hook_name, context, id_fields, ig_version: 'v201')
266
357
  id_fields.each do |field|
267
358
  resource_id = context[field]
268
359
  next unless resource_id.is_a?(String) && valid_id_format?(field, hook_name, resource_id)
269
360
 
270
361
  if client_test?
271
362
  resource_type = field.split(/(?=[A-Z])/).first.capitalize
272
- query_and_validate_id_field(resource_type, resource_id)
363
+ query_and_validate_id_field(resource_type, resource_id, ig_version:)
273
364
  end
274
365
  end
275
366
  end
@@ -285,7 +376,7 @@ module DaVinciCRDTestKit
285
376
  true
286
377
  end
287
378
 
288
- def bundle_entries_check(context, context_field_name, bundle, resource_types, status = nil)
379
+ def bundle_entries_check(context, context_field_name, bundle, resource_types, status = nil, ig_version: 'v201') # rubocop:disable Metrics/CyclomaticComplexity
289
380
  bundle.entry.each do |entry|
290
381
  resource_id = entry.resource.id
291
382
  next unless resource_id.blank?
@@ -305,7 +396,11 @@ module DaVinciCRDTestKit
305
396
  status_check(context, context_field_name, status, target_resources)
306
397
 
307
398
  target_resources.each do |resource|
308
- resource_is_valid?(resource:, profile_url: structure_definition_map[resource.resourceType])
399
+ if resource.resourceType == 'Appointment' && ig_version == 'v221'
400
+ check_appointment_conformance(resource, { 'context' => context, 'fhirServer' => fhir_url }, '', '2.2.1')
401
+ else
402
+ resource_is_valid?(resource:, profile_url: structure_definition_map(ig_version)[resource.resourceType])
403
+ end
309
404
  end
310
405
  end
311
406
 
@@ -334,11 +429,12 @@ module DaVinciCRDTestKit
334
429
  nil
335
430
  end
336
431
 
337
- def context_selections_check(context, selections, order_refs, expected_resource_types)
432
+ def context_selections_check(context, selections, order_refs, expected_resource_types, ig_version: 'v201')
338
433
  return unless selections.is_a?(Array)
339
434
 
340
435
  selections.each do |reference|
341
- resource_reference_check(reference, 'selections item', supported_resource_types: expected_resource_types)
436
+ resource_reference_check(reference, 'selections item', supported_resource_types: expected_resource_types,
437
+ ig_version:)
342
438
  next if order_refs.include?(reference)
343
439
 
344
440
  error_msg = "#{request_number}`selections` field must reference FHIR resources in `draftOrders`. " \
@@ -347,25 +443,26 @@ module DaVinciCRDTestKit
347
443
  end
348
444
  end
349
445
 
350
- def appointment_book_context_check(context)
351
- hook_user_type_check(context, 'appointment-book')
352
- id_only_fields_check('appointment-book', context, ['patientId'])
446
+ def appointment_book_context_check(context, ig_version: 'v201')
447
+ hook_user_type_check(context, 'appointment-book', ig_version:)
448
+ id_only_fields_check('appointment-book', context, ['patientId'], ig_version:)
353
449
 
354
450
  appointment_bundle = parse_fhir_bundle_from_context('appointments', context)
355
451
  return if appointment_bundle.blank?
356
452
 
357
453
  expected_resource_types = ['Appointment']
358
- bundle_entries_check(context, 'appointments', appointment_bundle, expected_resource_types, 'proposed')
454
+ bundle_entries_check(context, 'appointments', appointment_bundle, expected_resource_types, 'proposed',
455
+ ig_version:)
359
456
  end
360
457
 
361
- def encounter_start_or_discharge_context_check(context, hook_name)
362
- hook_user_type_check(context, hook_name)
363
- id_only_fields_check(hook_name, context, ['patientId', 'encounterId'])
458
+ def encounter_start_or_discharge_context_check(context, hook_name, ig_version: 'v201')
459
+ hook_user_type_check(context, hook_name, ig_version:)
460
+ id_only_fields_check(hook_name, context, ['patientId', 'encounterId'], ig_version:)
364
461
  end
365
462
 
366
- def order_select_or_sign_context_check(context, hook_name)
367
- hook_user_type_check(context, hook_name)
368
- id_only_fields_check(hook_name, context, ['patientId'])
463
+ def order_select_or_sign_context_check(context, hook_name, ig_version: 'v201')
464
+ hook_user_type_check(context, hook_name, ig_version:)
465
+ id_only_fields_check(hook_name, context, ['patientId'], ig_version:)
369
466
 
370
467
  draft_orders_bundle = parse_fhir_bundle_from_context('draftOrders', context)
371
468
  return if draft_orders_bundle.blank?
@@ -375,28 +472,25 @@ module DaVinciCRDTestKit
375
472
  'ServiceRequest', 'VisionPrescription'
376
473
  ]
377
474
 
378
- bundle_entries_check(context, 'draftOrders', draft_orders_bundle, expected_resource_types)
475
+ bundle_entries_check(context, 'draftOrders', draft_orders_bundle, expected_resource_types, ig_version:)
379
476
 
380
477
  return unless hook_name == 'order-select'
381
478
 
382
479
  order_refs = draft_orders_bundle.entry.map(&:resource).map do |resource|
383
480
  "#{resource.resourceType}/#{resource.id}"
384
481
  end
385
- context_selections_check(context, context['selections'], order_refs, expected_resource_types)
482
+ context_selections_check(context, context['selections'], order_refs, expected_resource_types, ig_version:)
386
483
  end
387
484
 
388
- def order_dispatch_context_check(context)
389
- id_only_fields_check('order-dispatch', context, ['patientId'])
485
+ def order_dispatch_context_check(context, ig_version: 'v201')
486
+ id_only_fields_check('order-dispatch', context, ['patientId'], ig_version:)
390
487
  order_supported_resource_type = [
391
488
  'DeviceRequest', 'MedicationRequest', 'NutritionOrder',
392
489
  'ServiceRequest', 'VisionPrescription'
393
490
  ]
394
- resource_reference_check(context['order'], 'order', supported_resource_types: order_supported_resource_type)
395
- resource_reference_check(context['performer'], 'performer')
396
- end
397
-
398
- def no_error_validation(message)
399
- assert messages.none? { |msg| msg[:type] == 'error' }, message
491
+ resource_reference_check(context['order'], 'order', supported_resource_types: order_supported_resource_type,
492
+ ig_version:)
493
+ resource_reference_check(context['performer'], 'performer', ig_version:)
400
494
  end
401
495
 
402
496
  def valid_url?(url)
@@ -406,42 +500,55 @@ module DaVinciCRDTestKit
406
500
  false
407
501
  end
408
502
 
409
- def query_and_validate_id_field(resource_type, resource_id)
503
+ def guid?(string)
504
+ uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
505
+ uuid_regex.match?(string.to_s)
506
+ end
507
+
508
+ def query_and_validate_id_field(resource_type, resource_id, ig_version: 'v201')
410
509
  fhir_read(resource_type, resource_id)
411
510
  status = request.response[:status]
412
511
  unless status == 200
413
- add_message('error', "#{request_number}Unexpected response status: expected 200, but received #{status}")
512
+ add_message('error',
513
+ "#{request_number}Unexpected response status when reading '#{resource_type}/#{resource_id}': " \
514
+ "expected 200, but received #{status}")
414
515
  return
415
516
  end
416
517
  unless resource.resourceType == resource_type
417
- add_message('error', %(
418
- #{request_number}Unexpected resource type: Expected `#{resource_type}`. Got
419
- `#{resource.resourceType}`.
420
- ))
518
+ add_message('error',
519
+ "#{request_number}Unexpected resource type when reading '#{resource_type}/#{resource_id}': " \
520
+ "#Expected `#{resource_type}`. Got `#{resource.resourceType}`.")
421
521
  return
422
522
  end
423
523
  unless resource.id == resource_id
424
- add_message('error', %(
425
- #{request_number}Requested resource with id #{resource_id}, received resource with id #{resource.id}
426
- ))
524
+ add_message('error',
525
+ "#{request_number}Unexpected resource id when reading '#{resource_type}/#{resource_id}': " \
526
+ "Expected `#{resource_id}`. Got `#{resource.id}`.")
427
527
  return
428
528
  end
429
529
 
430
- profile_url = hook_name == 'order-dispatch' ? nil : structure_definition_map[resource_type]
530
+ profile_url = hook_name == 'order-dispatch' ? nil : structure_definition_map(ig_version)[resource_type]
431
531
  resource_is_valid?(profile_url:)
432
532
  end
433
533
 
434
- def context_validate_optional_fields(hook_context, hook_name)
435
- hook_optional_context_fields = context_optional_fields_by_hook[hook_name]
534
+ def context_validate_optional_fields(hook_context, hook_name, ig_version: 'v201')
535
+ hook_optional_context_fields =
536
+ if hook_name == 'order-dispatch'
537
+ context_optional_order_dispatch_fields[ig_version]
538
+ else
539
+ context_optional_fields_by_hook[hook_name]
540
+ end
436
541
  return if hook_optional_context_fields.blank?
437
542
 
438
543
  hook_optional_context_fields.each do |field, type|
439
- validate_presence_and_type(hook_context, field, type, "#{hook_name} request context") if hook_context[field]
544
+ if hook_context.key?(field)
545
+ validate_presence_and_type(hook_context, field, type, "#{hook_name} request context", required: false)
546
+ end
440
547
  end
441
548
 
442
549
  optional_field_keys = hook_optional_context_fields.keys
443
550
  if optional_field_keys.include?('encounterId') && hook_context['encounterId'].present?
444
- id_only_fields_check(hook_name, hook_context, ['encounterId'])
551
+ id_only_fields_check(hook_name, hook_context, ['encounterId'], ig_version:)
445
552
  end
446
553
 
447
554
  validate_hash_fields(hook_context, optional_field_keys)
@@ -473,58 +580,11 @@ module DaVinciCRDTestKit
473
580
  end
474
581
  end
475
582
 
476
- def hook_request_prefetch_check(advertised_prefetch_fields, received_prefetch, received_context)
477
- advertised_prefetch_fields.each do |advertised_prefetch_key, advertised_prefetch_template|
478
- next if received_prefetch[advertised_prefetch_key].blank?
479
-
480
- unless received_prefetch[advertised_prefetch_key].is_a?(Hash)
481
- add_message('error', "#{request_number}Prefetch field `#{advertised_prefetch_key}` is not of type `Hash`.")
482
- next
483
- end
484
-
485
- received_prefetch_resource = FHIR.from_contents(received_prefetch[advertised_prefetch_key].to_json)
486
-
487
- if advertised_prefetch_template.include?('?')
488
- advertised_prefetch_fhir_search = advertised_prefetch_template.gsub(/{|}/, '').split('?')
489
- advertised_prefetch_resource_type = advertised_prefetch_fhir_search.first
490
-
491
- if advertised_prefetch_resource_type == 'Coverage'
492
- advertised_coverage_query_params = Rack::Utils.parse_nested_query(advertised_prefetch_fhir_search.last)
493
-
494
- advertised_patient_token = advertised_coverage_query_params['patient']
495
- advertised_context_patient_id_key = advertised_patient_token.split('.').last
496
- received_context_patient_id = received_context[advertised_context_patient_id_key]
497
-
498
- advertised_status_param = advertised_coverage_query_params['status']
499
-
500
- validate_prefetch_coverage(received_prefetch_resource, advertised_prefetch_key, received_context_patient_id,
501
- advertised_status_param)
502
- end
503
- else
504
- advertised_prefetch_token = advertised_prefetch_template.gsub(/{|}/, '').split('/')
505
- advertised_context_id = advertised_prefetch_token.last.split('.').last
506
-
507
- if advertised_prefetch_token.length == 1
508
- received_context_reference = FHIR::Reference.new(reference: received_context[advertised_context_id])
509
- received_context_resource_type = received_context_reference.resource_type
510
- received_context_id = received_context_reference.reference_id
511
- else
512
- received_context_id = received_context[advertised_context_id]
513
- received_context_resource_type = advertised_prefetch_token.first
514
- end
515
- validate_prefetch_resource(received_prefetch_resource, advertised_prefetch_key,
516
- received_context_resource_type, received_context_id)
517
- end
518
- end
519
- end
520
-
521
- def validate_prefetch_coverage(received_resource, advertised_prefetch_key,
522
- received_context_patient_id, advertised_status)
583
+ def validate_prefetch_coverages(received_resource, advertised_prefetch_key,
584
+ received_context_patient_id, advertised_status, ig_version: 'v201')
523
585
  unless received_resource.resourceType == 'Bundle'
524
- add_message('error', %(
525
- #{request_number}Unexpected resource type: Expected `Bundle`. Got
526
- `#{received_resource.resourceType}`.
527
- ))
586
+ add_message('error', "#{request_number}Unexpected resource type: Expected `Bundle`. Got " \
587
+ "`#{received_resource.resourceType}`.")
528
588
  return
529
589
  end
530
590
 
@@ -533,76 +593,95 @@ module DaVinciCRDTestKit
533
593
  return
534
594
  end
535
595
 
536
- coverage_resource = received_resource.entry.first.resource
596
+ if received_context_patient_id.blank?
597
+ add_message('error',
598
+ "#{request_number}Cannot verify `coverage` patient id because no id provided in the context.")
599
+ end
600
+
601
+ received_resource.entry.each_with_index do |entry, index|
602
+ validate_prefetch_coverage(entry&.resource, advertised_prefetch_key,
603
+ received_context_patient_id, advertised_status, index, ig_version:)
604
+ end
605
+ end
606
+
607
+ def validate_prefetch_coverage(coverage_resource, advertised_prefetch_key,
608
+ received_context_patient_id, advertised_status, entry_index, ig_version: 'v201')
609
+ unless coverage_resource.present?
610
+ add_message('error', "#{request_number}Coverage Bundle entry #{entry_index + 1} had no resource")
611
+ return
612
+ end
613
+
537
614
  unless coverage_resource.resourceType == 'Coverage'
538
- add_message('error', %(
539
- #{request_number}Unexpected resource type: Expected `Coverage`. Got
540
- `#{coverage_resource.resourceType}`.
541
- ))
615
+ add_message('error', "#{request_number}Coverage Bundle entry #{entry_index + 1} - Unexpected resource type: " \
616
+ "Expected `Coverage`. Got `#{coverage_resource.resourceType}`.")
542
617
  return
543
618
  end
544
619
 
545
620
  resource_is_valid?(resource: coverage_resource,
546
- profile_url: structure_definition_map['Coverage'])
621
+ profile_url: structure_definition_map(ig_version)['Coverage'])
547
622
 
548
623
  coverage_beneficiary_reference = coverage_resource.beneficiary
549
624
  coverage_beneficiary_patient_id = coverage_beneficiary_reference.reference_id
550
625
  if coverage_beneficiary_patient_id.blank?
551
- add_message('error', %(
552
- #{request_number}Could not get beneficiary reference id from `#{advertised_prefetch_key}` field's Coverage
553
- resource
554
- ))
626
+ add_message('error', "#{request_number}Coverage Bundle entry #{entry_index + 1} - Could not get beneficiary " \
627
+ "reference id from `#{advertised_prefetch_key}` field's Coverage resource")
555
628
  return
556
629
  end
557
630
 
558
- if coverage_beneficiary_patient_id != received_context_patient_id
559
- add_message('error', %(
560
- #{request_number}Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `beneficiary`
561
- reference id of '#{received_context_patient_id}', instead was '#{coverage_beneficiary_patient_id}'
562
- ))
631
+ if received_context_patient_id.present? && coverage_beneficiary_patient_id != received_context_patient_id
632
+ add_message('error', "#{request_number}Coverage Bundle entry #{entry_index + 1} - " \
633
+ "Expected `#{advertised_prefetch_key}` field's Coverage resource to have a " \
634
+ "`beneficiary` reference id of '#{received_context_patient_id}', " \
635
+ "instead was '#{coverage_beneficiary_patient_id}`")
563
636
  return
564
637
  end
565
638
 
566
639
  coverage_status = coverage_resource.status
567
640
  return unless coverage_status != advertised_status
568
641
 
569
- add_message('error', %(
570
- #{request_number}Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `status` of
571
- '#{advertised_status}', instead was '#{coverage_status}'
572
- ))
642
+ add_message('error', "#{request_number}Coverage Bundle entry #{entry_index + 1} - " \
643
+ "Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `status` of " \
644
+ "'#{advertised_status}', instead was '#{coverage_status}'")
573
645
  end
574
646
 
575
647
  def validate_prefetch_resource(received_resource, advertised_prefetch_key, context_field_resource_type,
576
- context_field_id)
577
- unless received_resource.resourceType == context_field_resource_type
578
- add_message('error', %(
579
- #{request_number}Unexpected resource type: Expected `#{context_field_resource_type}`. Got
580
- `#{received_resource.resourceType}`.
581
- ))
582
- return
583
- end
648
+ context_field_id, ig_version: 'v201')
649
+ return unless prefetch_resource_type_correct?(received_resource, context_field_resource_type,
650
+ "#{request_number}`#{advertised_prefetch_key}` - ")
584
651
 
585
652
  if hook_name == 'order-dispatch'
586
653
  resource_is_valid?(resource: received_resource)
587
654
  else
588
655
  resource_is_valid?(resource: received_resource,
589
- profile_url: structure_definition_map[context_field_resource_type])
656
+ profile_url: structure_definition_map(ig_version)[context_field_resource_type])
590
657
  end
591
658
 
592
- received_prefetch_resource_id = received_resource.id
593
- if received_prefetch_resource_id.blank?
594
- add_message('error', %(
595
- #{request_number}#{advertised_prefetch_key}` field's FHIR resource does not contain the `id` field
596
- ))
597
- return
659
+ prefetch_resource_id_correct?(received_resource, context_field_id,
660
+ "#{request_number}`#{advertised_prefetch_key}` - ")
661
+ end
662
+
663
+ def prefetch_resource_type_correct?(resource, expected_type, error_prefix)
664
+ if expected_type.present?
665
+ return true if resource.resourceType == expected_type
666
+
667
+ add_message(:error, "#{error_prefix}Prefetched resource has the wrong resource type. " \
668
+ "Expected `#{expected_type}`, got `#{resource.resourceType}`.")
669
+ else
670
+ add_message(:error, "#{error_prefix}No resource type provided to verify prefetched resource against.")
598
671
  end
672
+ false
673
+ end
599
674
 
600
- return unless received_prefetch_resource_id != context_field_id
675
+ def prefetch_resource_id_correct?(resource, expected_id, error_prefix)
676
+ if expected_id.present?
677
+ return true if resource.id == expected_id
601
678
 
602
- add_message('error', %(
603
- #{request_number}Expected `#{advertised_prefetch_key}` field's FHIR resource to have an `id` of
604
- '#{context_field_id}', instead was '#{received_prefetch_resource_id}'
605
- ))
679
+ add_message(:error, "#{error_prefix}Prefetched resource has the wrong resource id. " \
680
+ "Expected `#{expected_id}`, got `#{resource.id}`.")
681
+ else
682
+ add_message(:error, "#{error_prefix}No resource id provided to verify prefetched resource against.")
683
+ end
684
+ false
606
685
  end
607
686
  end
608
687
  end