davinci_crd_test_kit 0.14.0 → 0.14.2
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.
- checksums.yaml +4 -4
- data/config/presets/{inferno_crd_server_suite.json.erb → client_against_inferno_crd_server_suite.json.erb} +0 -33
- data/config/presets/{ri_crd_request_generator.json.erb → client_against_ri_crd_request_generator.json.erb} +1 -14
- data/config/presets/client_v221_against_inferno_crd_server_suite.json.erb +84 -0
- data/config/presets/client_v221_against_ri_br_provider.json.erb +31 -0
- data/config/presets/{ri_crd_request_generator.json_v221.json.erb → client_v221_against_ri_crd_request_generator.json.erb} +1 -1
- data/config/presets/{inferno_crd_client_suite.json.erb → server_against_inferno_crd_client_suite.json.erb} +0 -43
- data/config/presets/{ri_crd_server.json.erb → server_against_ri_crd_server.json.erb} +1 -44
- data/config/presets/{inferno_crd_client_suite_v221.json.erb → server_v221_against_inferno_crd_client_suite.json.erb} +48 -45
- data/config/presets/{inferno_crd_client_suite_prefetch_subset_v221.json.erb → server_v221_against_inferno_crd_client_suite_prefetch_subset.json.erb} +0 -46
- data/config/presets/server_v221_against_inferno_g31_suite.json.erb +49 -0
- data/lib/davinci_crd_test_kit/client/cross_hook_helper.rb +26 -0
- data/lib/davinci_crd_test_kit/client/endpoints/custom_service_response.rb +7 -7
- data/lib/davinci_crd_test_kit/client/endpoints/gather_response_generation_data.rb +4 -1
- data/lib/davinci_crd_test_kit/client/endpoints/hook_request_endpoint.rb +70 -39
- data/lib/davinci_crd_test_kit/client/endpoints/mock_service_response.rb +26 -7
- data/lib/davinci_crd_test_kit/client/endpoints/mocked_card_responses/external_reference.json +1 -1
- data/lib/davinci_crd_test_kit/client/endpoints/mocked_card_responses/launch_smart_app.json +0 -7
- data/lib/davinci_crd_test_kit/client/endpoints/scenario_service_response.rb +54 -0
- data/lib/davinci_crd_test_kit/client/tagged_request_load_helper.rb +8 -27
- data/lib/davinci_crd_test_kit/client/v2.0.1/auth/retrieve_jwks_test.rb +35 -23
- data/lib/davinci_crd_test_kit/client/v2.0.1/auth/token_payload_test.rb +34 -21
- data/lib/davinci_crd_test_kit/client/v2.0.1/cds-services-v201.json +20 -20
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_appointment_book_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_encounter_discharge_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_encounter_start_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_hooks_group.rb +5 -5
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_dispatch_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_select_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.0.1/client_order_sign_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_coverage_information.rb +3 -36
- data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_external_reference.rb +3 -36
- data/lib/davinci_crd_test_kit/client/v2.0.1/must_support/client_card_must_support_instructions.rb +3 -36
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_fetched_data_test.rb +4 -13
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_request/hook_request_prefetch_equals_queried_test.rb +3 -13
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_response/client_display_cards_attest.rb +3 -13
- data/lib/davinci_crd_test_kit/client/v2.0.1/verify_response/inferno_response_validation.rb +3 -13
- data/lib/davinci_crd_test_kit/client/v2.2.1/api/client_coverage_info_update_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/attestation_instructions.rb +10 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_authorized_scope_attestation_test.rb +45 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_coverage_based_invocation_attestation_test.rb +44 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_data_element_expectations_attestation_test.rb +44 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_hook_invocation_logging_attestation_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_must_support_exposure_attestation_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_must_support_use_attestation_test.rb +41 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_order_sign_support_attestation_test.rb +42 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_prefetch_key_omission_attestation_test.rb +42 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_resource_identifiers_attestation_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_response_case_distinction_attestation_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_security_and_privacy_attestation_test.rb +43 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/attestations/client_workflow_integration_attestation_test.rb +44 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/decode_auth_token_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/retrieve_jwks_test.rb +29 -18
- data/lib/davinci_crd_test_kit/client/v2.2.1/auth/token_payload_test.rb +35 -22
- data/lib/davinci_crd_test_kit/client/v2.2.1/cds-services-prefetch-subset-v221.json +47 -47
- data/lib/davinci_crd_test_kit/client/v2.2.1/cds-services-v221.json +50 -50
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_appointment_book_group.rb +24 -15
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_attestations_group.rb +49 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_cross_hook_group.rb +22 -6
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_cross_hook_interaction_group.rb +98 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_discharge_group.rb +19 -11
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_start_group.rb +19 -11
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_fhir_api_group.rb +14 -12
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_hooks_group.rb +25 -5
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_long_running_hook_group.rb +3 -2
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_dispatch_group.rb +23 -15
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_select_group.rb +19 -11
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_sign_group.rb +23 -15
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_registration_group.rb +6 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_scenarios_group.rb +26 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_self_pay_hook_group.rb +36 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_unknown_content_hook_group.rb +34 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/crd_client_suite.rb +24 -3
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_card_must_support_coverage_information_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_fhirpath_collection_as_comma_delimited_string_test.rb +4 -11
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_hook_instances_unique_test.rb +1 -9
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_location_address_propagation_test.rb +6 -2
- data/lib/davinci_crd_test_kit/client/v2.2.1/cross_hook/client_prefetch_complete_and_subset_test.rb +3 -12
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/appointment_book_receive_request_test.rb +8 -89
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/base_hook_invocation_receive_request_test.rb +163 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/cross_hooks_receive_request_test.rb +97 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/encounter_discharge_receive_request_test.rb +8 -90
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/encounter_start_receive_request_test.rb +8 -90
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_dispatch_receive_request_test.rb +8 -98
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_select_receive_request_test.rb +8 -98
- data/lib/davinci_crd_test_kit/client/v2.2.1/invocation/order_sign_receive_request_test.rb +8 -98
- data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_long_running_receive_request_test.rb +10 -2
- data/lib/davinci_crd_test_kit/client/v2.2.1/long_running/client_skip_long_running_attestation_test.rb +11 -6
- data/lib/davinci_crd_test_kit/client/v2.2.1/registration/client_service_registration_attestation_test.rb +2 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/self_pay/client_self_pay_no_request_test.rb +30 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/self_pay/client_self_pay_workflow_test.rb +55 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/unknown_content/client_unknown_content_attestation_test.rb +99 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/unknown_content/client_unknown_content_receive_request_test.rb +62 -0
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_conformance_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_coverage_verification_test.rb +10 -2
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_data_fetch_verification_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_granted_scopes_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_complete_test.rb +7 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_prefetch_profiles_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_requested_version_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_request/hook_request_secured_transport_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/client_display_cards_attest.rb +54 -21
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/hook_response_support_coverage_information_test.rb +1 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/verify_response/inferno_response_validation.rb +6 -3
- data/lib/davinci_crd_test_kit/cross_suite/cards_identification.rb +18 -0
- data/lib/davinci_crd_test_kit/cross_suite/logical_models_override_helper.rb +30 -13
- data/lib/davinci_crd_test_kit/cross_suite/prefetch_completeness_checker.rb +25 -11
- data/lib/davinci_crd_test_kit/cross_suite/prefetch_profile_validation.rb +2 -0
- data/lib/davinci_crd_test_kit/cross_suite/requests_logical_model_validation.rb +28 -28
- data/lib/davinci_crd_test_kit/cross_suite/response_logical_model_validation.rb +211 -43
- data/lib/davinci_crd_test_kit/cross_suite/tags.rb +7 -0
- data/lib/davinci_crd_test_kit/requirements/cds-hooks_3.0.0-ballot_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/requirements/davinci_crd_test_kit_requirements.csv +6 -2
- data/lib/davinci_crd_test_kit/requirements/generated/crd_client_requirements_coverage.csv +107 -107
- data/lib/davinci_crd_test_kit/requirements/generated/crd_client_v221_requirements_coverage.csv +101 -97
- data/lib/davinci_crd_test_kit/requirements/generated/crd_server_requirements_coverage.csv +18 -18
- data/lib/davinci_crd_test_kit/requirements/generated/crd_server_v221_requirements_coverage.csv +43 -39
- data/lib/davinci_crd_test_kit/requirements/hl7.fhir.us.davinci-crd_2.2.1_requirements.xlsx +0 -0
- data/lib/davinci_crd_test_kit/server/jobs/invoke_hook.rb +3 -3
- data/lib/davinci_crd_test_kit/server/jobs/invoke_hook_no_special_requests.rb +17 -0
- data/lib/davinci_crd_test_kit/server/jobs/invoke_hook_with_bad_auth.rb +14 -0
- data/lib/davinci_crd_test_kit/server/server_abstract_invoke_hook_test.rb +24 -6
- data/lib/davinci_crd_test_kit/server/server_hook_helper.rb +2 -2
- data/lib/davinci_crd_test_kit/server/v2.0.1/crd_server_suite.rb +1 -1
- data/lib/davinci_crd_test_kit/server/v2.0.1/server_hooks_group.rb +5 -5
- data/lib/davinci_crd_test_kit/server/v2.2.1/crd_server_suite.rb +3 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/discovery/discovery_standard_prefetch_expressions_test.rb +165 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/interaction/server_invoke_hook_single_request_test.rb +22 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_appointment_book_group.rb +10 -102
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_demonstrate_hook_response_group.rb +3 -2
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_discovery_group.rb +2 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_discharge_group.rb +10 -97
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_encounter_start_group.rb +10 -97
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_hooks_group.rb +5 -5
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_dispatch_group.rb +10 -103
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_select_group.rb +12 -116
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_order_sign_group.rb +12 -121
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_response_group.rb +23 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/coverage_info_reason_test.rb +108 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/coverage_not_found_group.rb +116 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/no_active_coverage_group.rb +115 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/no_member_found_group.rb +117 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/technical_issues_group.rb +121 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/specific_coverage_responses/technical_issues_invoke_test.rb +22 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_context_validation_test.rb +1 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_optional_fields_validation_test.rb +1 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_request/service_request_required_fields_validation_test.rb +1 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/additional_orders_validation_test.rb +78 -35
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_card_absence_test.rb +76 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_validation_test.rb +70 -124
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/create_or_update_coverage_info_response_validation_test.rb +83 -39
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/external_reference_card_validation_test.rb +59 -17
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/form_completion_response_validation_test.rb +80 -45
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/hook_request_resource_resolution.rb +37 -0
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/instructions_card_received_test.rb +57 -6
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/launch_smart_app_card_validation_test.rb +56 -17
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/propose_alternate_request_card_validation_test.rb +52 -25
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/service_response_validation_test.rb +17 -51
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_cds_hooks_elements_test.rb +1 -1
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_configuration_test.rb +1 -1
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/unknown_context_test.rb +1 -1
- data/lib/davinci_crd_test_kit/version.rb +2 -2
- metadata +54 -16
- data/config/presets/inferno_crd_server_suite_v221.json.erb +0 -94
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/card_optional_fields_validation_test.rb +0 -57
- data/lib/davinci_crd_test_kit/server/v2.2.1/verify_response/coverage_information_system_action_received_test.rb +0 -65
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require_relative 'attestation_instructions'
|
|
2
|
+
|
|
3
|
+
module DaVinciCRDTestKit
|
|
4
|
+
module V221
|
|
5
|
+
class SecurityAndPrivacyAttestationTest < Inferno::Test
|
|
6
|
+
id :crd_v221_security_and_privacy_attestation
|
|
7
|
+
ATTESTATION_TITLE = 'Health IT module adheres to security and privacy requirements'.freeze
|
|
8
|
+
title ATTESTATION_TITLE
|
|
9
|
+
description %(
|
|
10
|
+
During this test, the tester will confirm that the Health IT module adheres to the security and privacy
|
|
11
|
+
rules that CRD inherits from the specifications it builds on.
|
|
12
|
+
To see the specifics of the attested requirements, click the "View Specification Requirements" link for this
|
|
13
|
+
test.
|
|
14
|
+
)
|
|
15
|
+
attestation
|
|
16
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@sec-1'
|
|
17
|
+
input_instructions ATTESTATION_INPUT_INSTRUCTIONS
|
|
18
|
+
|
|
19
|
+
input :security_and_privacy_attestation,
|
|
20
|
+
title: ATTESTATION_TITLE,
|
|
21
|
+
description: %(
|
|
22
|
+
I attest that the Health IT module adheres to the security and privacy rules that CRD inherits from
|
|
23
|
+
the specifications it builds on.
|
|
24
|
+
),
|
|
25
|
+
type: 'radio',
|
|
26
|
+
default: 'false',
|
|
27
|
+
options: {
|
|
28
|
+
list_options: [
|
|
29
|
+
{ label: 'Yes', value: 'true' },
|
|
30
|
+
{ label: 'No', value: 'false' }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
input :security_and_privacy_attestation_note,
|
|
34
|
+
title: 'Notes, if applicable:',
|
|
35
|
+
type: 'textarea',
|
|
36
|
+
optional: true
|
|
37
|
+
|
|
38
|
+
run do
|
|
39
|
+
assert security_and_privacy_attestation == 'true'
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require_relative 'attestation_instructions'
|
|
2
|
+
|
|
3
|
+
module DaVinciCRDTestKit
|
|
4
|
+
module V221
|
|
5
|
+
class WorkflowIntegrationAttestationTest < Inferno::Test
|
|
6
|
+
id :crd_v221_workflow_integration_attestation
|
|
7
|
+
ATTESTATION_TITLE = 'Health IT module integrates hook invocations transparently into user workflows'.freeze
|
|
8
|
+
title ATTESTATION_TITLE
|
|
9
|
+
description %(
|
|
10
|
+
During this test, the tester will confirm that the Health IT module integrates CRD hook invocations
|
|
11
|
+
transparently into the regular user workflow.
|
|
12
|
+
To see the specifics of the attested requirements, click the "View Specification Requirements" link for this
|
|
13
|
+
test.
|
|
14
|
+
)
|
|
15
|
+
attestation
|
|
16
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@hook-7', 'hl7.fhir.us.davinci-crd_2.2.1@hook-8',
|
|
17
|
+
'hl7.fhir.us.davinci-crd_2.2.1@resp-48'
|
|
18
|
+
input_instructions ATTESTATION_INPUT_INSTRUCTIONS
|
|
19
|
+
|
|
20
|
+
input :workflow_integration_attestation,
|
|
21
|
+
title: ATTESTATION_TITLE,
|
|
22
|
+
description: %(
|
|
23
|
+
I attest that the Health IT module integrates CRD hook invocations transparently into the regular
|
|
24
|
+
user workflow.
|
|
25
|
+
),
|
|
26
|
+
type: 'radio',
|
|
27
|
+
default: 'false',
|
|
28
|
+
options: {
|
|
29
|
+
list_options: [
|
|
30
|
+
{ label: 'Yes', value: 'true' },
|
|
31
|
+
{ label: 'No', value: 'false' }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
input :workflow_integration_attestation_note,
|
|
35
|
+
title: 'Notes, if applicable:',
|
|
36
|
+
type: 'textarea',
|
|
37
|
+
optional: true
|
|
38
|
+
|
|
39
|
+
run do
|
|
40
|
+
assert workflow_integration_attestation == 'true'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -21,7 +21,7 @@ module DaVinciCRDTestKit
|
|
|
21
21
|
output :auth_tokens, :auth_token_payloads_json, :auth_token_headers_json
|
|
22
22
|
|
|
23
23
|
run do
|
|
24
|
-
|
|
24
|
+
load_interaction_group_requests
|
|
25
25
|
skip_if requests.empty?, "No #{hook_name} requests were made in a previous test as expected."
|
|
26
26
|
auth_tokens = []
|
|
27
27
|
auth_token_payloads_json = []
|
|
@@ -11,7 +11,8 @@ module DaVinciCRDTestKit
|
|
|
11
11
|
During this test, Inferno will verify that for each request the JWKS can be retrieved from the JWKS uri if
|
|
12
12
|
it is present in the `jku` field within the JWT token header. Additionally, keys will be extracted and
|
|
13
13
|
outputted for use in subsequent tests. If the client does not provide a uri in the `jku` field,
|
|
14
|
-
Inferno will
|
|
14
|
+
Inferno will use the JWK Set provided out of band as a part of the "Registration" group, either
|
|
15
|
+
retrieving it from a url or, if provided directly, extracting keys from the raw JWKS JSON.
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
verifies_requirements 'cds-hooks_3.0.0-ballot@183', 'cds-hooks_3.0.0-ballot@185', 'cds-hooks_3.0.0-ballot@197',
|
|
@@ -22,12 +23,10 @@ module DaVinciCRDTestKit
|
|
|
22
23
|
title: 'CRD JSON Web Key Set (JWKS)',
|
|
23
24
|
type: 'textarea',
|
|
24
25
|
description: %(
|
|
25
|
-
The client's registered JWK Set
|
|
26
|
-
only when a request was received with a JWT without the `jku`
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
it would be populated in the `jku` header). Run or re-run the "Registration"
|
|
30
|
-
group to set or change this value.
|
|
26
|
+
The client's registered JWK Set, provided during the "Registration" group, containing
|
|
27
|
+
it's public key. Used only when a request was received with a JWT without the `jku`
|
|
28
|
+
header. May be either a publicly accessible url containing the JWKS, or the raw JWKS
|
|
29
|
+
JSON. Run or re-run the "Registration" group to set or change this value.
|
|
31
30
|
),
|
|
32
31
|
locked: true,
|
|
33
32
|
optional: true
|
|
@@ -49,18 +48,9 @@ module DaVinciCRDTestKit
|
|
|
49
48
|
jku = JSON.parse(token_header)['jku'] # NOTE: pre-verified json
|
|
50
49
|
jwks =
|
|
51
50
|
if jku.present?
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if response[:status] == 200
|
|
55
|
-
parse_json_request_entity(response[:body], 'Fetched jku url response', index)
|
|
56
|
-
else
|
|
57
|
-
add_request_message('error',
|
|
58
|
-
"Unexpected response status: expected 200, but received #{response[:status]}",
|
|
59
|
-
index)
|
|
60
|
-
nil
|
|
61
|
-
end
|
|
51
|
+
fetch_jwks(jku, 'Fetched jku url response', index)
|
|
62
52
|
else
|
|
63
|
-
|
|
53
|
+
parse_or_fetch_jwks(cds_jwk_set, index)
|
|
64
54
|
end
|
|
65
55
|
if jwks.blank?
|
|
66
56
|
crd_jwks_keys_json << nil
|
|
@@ -115,6 +105,27 @@ module DaVinciCRDTestKit
|
|
|
115
105
|
token_header.present? && JSON.parse(token_header)['jku'].blank?
|
|
116
106
|
end
|
|
117
107
|
end
|
|
108
|
+
|
|
109
|
+
def fetch_jwks(uri, entity, index)
|
|
110
|
+
get(uri)
|
|
111
|
+
|
|
112
|
+
if response[:status] == 200
|
|
113
|
+
parse_json_request_entity(response[:body], entity, index)
|
|
114
|
+
else
|
|
115
|
+
add_request_message('error',
|
|
116
|
+
"Unexpected response status: expected 200, but received #{response[:status]}",
|
|
117
|
+
index)
|
|
118
|
+
nil
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def parse_or_fetch_jwks(jwk_set_input, index)
|
|
123
|
+
if jwk_set_input.match?(%r{\Ahttps?://})
|
|
124
|
+
fetch_jwks(jwk_set_input, 'Fetched JWK Set input url response', index)
|
|
125
|
+
else
|
|
126
|
+
parse_json_request_entity(jwk_set_input, 'JWK Set input', index)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
118
129
|
end
|
|
119
130
|
end
|
|
120
131
|
end
|
|
@@ -45,7 +45,7 @@ module DaVinciCRDTestKit
|
|
|
45
45
|
run do
|
|
46
46
|
auth_tokens_list = JSON.parse(auth_tokens)
|
|
47
47
|
auth_tokens_jwk = JSON.parse(auth_tokens_jwk_json)
|
|
48
|
-
requests =
|
|
48
|
+
requests = load_interaction_group_requests
|
|
49
49
|
skip_if auth_tokens_list.compact.empty?, 'No Authorization tokens produced from the previous tests.'
|
|
50
50
|
skip_if auth_tokens_jwk.compact.empty?, 'No Authorization token JWK produced from the previous test.'
|
|
51
51
|
|
|
@@ -56,34 +56,47 @@ module DaVinciCRDTestKit
|
|
|
56
56
|
|
|
57
57
|
begin
|
|
58
58
|
jwk = JSON.parse(auth_token_jwk).deep_symbolize_keys # NOTE: pre-verified json
|
|
59
|
-
|
|
60
|
-
payload, =
|
|
61
|
-
JWT.decode(
|
|
62
|
-
auth_tokens_list[index],
|
|
63
|
-
JWT::JWK.import(jwk).public_key,
|
|
64
|
-
true,
|
|
65
|
-
algorithms: [jwk[:alg]],
|
|
66
|
-
exp_leeway: 60,
|
|
67
|
-
iss: cds_jwt_iss,
|
|
68
|
-
aud: public_hook_url(request),
|
|
69
|
-
verify_not_before: false,
|
|
70
|
-
verify_iat: false,
|
|
71
|
-
verify_jti: true,
|
|
72
|
-
verify_iss: true,
|
|
73
|
-
verify_aud: true
|
|
74
|
-
)
|
|
59
|
+
unverified_payload, jwt_header = JWT.decode(auth_tokens_list[index], nil, false)
|
|
75
60
|
rescue StandardError => e
|
|
76
61
|
add_request_message('error', "Token validation error: #{e.message}", index)
|
|
77
62
|
next
|
|
78
63
|
end
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
missing_claims_string = missing_claims.map { |claim| "`#{claim}`" }.join(', ')
|
|
65
|
+
alg = jwk[:alg] || jwt_header['alg']
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
# Continue checking the rest of the token even when one check fails, so the tester sees every
|
|
68
|
+
# issue at once rather than fixing them one error at a time.
|
|
69
|
+
# CDS Hooks prohibits the `none` algorithm and symmetric (HMAC) algorithms for the authentication JWT.
|
|
70
|
+
if alg.to_s.match?(/\A(none|hs\d+)\z/i)
|
|
71
|
+
add_request_message('error',
|
|
72
|
+
"Token signature algorithm #{alg.inspect} is not permitted; CDS Hooks prohibits " \
|
|
73
|
+
'the `none` algorithm and symmetric (HMAC) algorithms.', index)
|
|
86
74
|
end
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
JWT.decode(
|
|
78
|
+
auth_tokens_list[index],
|
|
79
|
+
JWT::JWK.import(jwk).public_key,
|
|
80
|
+
true,
|
|
81
|
+
algorithms: [alg],
|
|
82
|
+
exp_leeway: 60,
|
|
83
|
+
iss: cds_jwt_iss,
|
|
84
|
+
aud: public_hook_url(request),
|
|
85
|
+
verify_not_before: false,
|
|
86
|
+
verify_iat: false,
|
|
87
|
+
verify_jti: true,
|
|
88
|
+
verify_iss: true,
|
|
89
|
+
verify_aud: true
|
|
90
|
+
)
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
add_request_message('error', "Token validation error: #{e.message}", index)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
missing_claims = required_claims - unverified_payload.keys
|
|
96
|
+
next if missing_claims.empty?
|
|
97
|
+
|
|
98
|
+
missing_claims_string = missing_claims.map { |claim| "`#{claim}`" }.join(', ')
|
|
99
|
+
add_request_message('error', "JWT payload missing required claims: #{missing_claims_string}", index)
|
|
87
100
|
end
|
|
88
101
|
assert_no_error_messages("#{requests_with_errors_prefix}Token payload is missing required claims or " \
|
|
89
102
|
'does not have a valid signature. See Messages for details.')
|
|
@@ -66,18 +66,19 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
|
-
"hook": "
|
|
70
|
-
"title": "
|
|
71
|
-
"description": "A simulated CDS Service requesting a subset of the standard prefetches that
|
|
72
|
-
"id": "
|
|
73
|
-
"prefetch"
|
|
69
|
+
"hook": "order-select",
|
|
70
|
+
"title": "Order Select CDS Service",
|
|
71
|
+
"description": "A simulated CDS Service requesting a subset of the standard prefetches that fires when a clinician selects one or more orders to place for a patient",
|
|
72
|
+
"id": "order-select-subset",
|
|
73
|
+
"prefetch": {
|
|
74
74
|
"pat": "Patient/{{context.patientId}}",
|
|
75
75
|
"enc": "Encounter/{{context.encounterId}}",
|
|
76
|
-
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
76
|
+
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
77
|
+
"devs": "Device?_id={{context.draftOrders.entry.resource.ofType(DeviceRequest).code.resolve().id}}",
|
|
78
|
+
"meds": "Medication?_id={{context.draftOrders.entry.resource.ofType(MedicationRequest).medication.resolve().id}}",
|
|
79
|
+
"roles": "PractitionerRole?_id={{context.draftOrders.entry.resource.sender.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.recipient.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.performer.resolve().ofType(PractitionerRole).id}}",
|
|
80
|
+
"pracs": "Practitioner?_id={{%roles.entry.resource.practitioner.resolve().id|context.draftOrders.entry.resource.sender.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.performer.resolve().ofType(Practitioner).id}}",
|
|
81
|
+
"locs": "Location?_id={{%roles.entry.resource.location.resolve().id|%enc.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}"
|
|
81
82
|
},
|
|
82
83
|
"extension" : {
|
|
83
84
|
"davinci-crd.version" : [
|
|
@@ -96,25 +97,19 @@
|
|
|
96
97
|
}
|
|
97
98
|
},
|
|
98
99
|
{
|
|
99
|
-
"hook": "order-
|
|
100
|
-
"title": "Order
|
|
101
|
-
"description": "A simulated CDS Service requesting a subset of the standard prefetches that fires when a
|
|
102
|
-
"id": "order-
|
|
100
|
+
"hook": "order-sign",
|
|
101
|
+
"title": "Order Sign CDS Service",
|
|
102
|
+
"description": "A simulated CDS Service requesting a subset of the standard prefetches that fires when a clinician is ready to sign one or more orders for a patient",
|
|
103
|
+
"id": "order-sign-subset",
|
|
103
104
|
"prefetch": {
|
|
104
105
|
"pat": "Patient/{{context.patientId}}",
|
|
105
106
|
"enc": "Encounter/{{context.encounterId}}",
|
|
106
|
-
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"visRxs": "VisionPrescription?_id={{context.dispatchedOrders.resolve().ofType(VisionPrescription).id}}",
|
|
113
|
-
"devs": "Device?_id={{%devReqs.entry.resource.code.resolve().id}}",
|
|
114
|
-
"meds": "Medication?_id={{%medReqs.entry.resource.medication.resolve().id}}",
|
|
115
|
-
"roles": "PractitionerRole?_id={{%comReqs.entry.resource.sender.resolve().ofType(PractitionerRole).id|%comReqs.entry.resource.recipient.resolve().ofType(PractitionerRole).id|%devReqs.entry.resource.performer.resolve().ofType(PractitionerRole).id|%medReqs.entry.resource.performer.resolve().ofType(PractitionerRole).id|%servReqs.entry.resource.performer.resolve().ofType(PractitionerRole).id}}",
|
|
116
|
-
"pracs": "Practitioner?_id={{%roles.entry.resource.practitioner.resolve().id|%comReqs.entry.resource.sender.resolve().ofType(Practitioner).id|%comReqs.entry.resource.recipient.resolve().ofType(Practitioner).id|%devReqs.entry.resource.performer.resolve().ofType(Practitioner).id|%medReqs.entry.resource.performer.resolve().ofType(Practitioner).id|%servReqs.entry.resource.performer.resolve().ofType(Practitioner).id}}",
|
|
117
|
-
"locs": "Location?_id={{%roles.entry.resource.location.resolve().id|%enc.location.location.resolve().id|%servReqs.entry.resource.locationReference.resolve().id}}"
|
|
107
|
+
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
108
|
+
"devs": "Device?_id={{context.draftOrders.entry.resource.ofType(DeviceRequest).code.resolve().id}}",
|
|
109
|
+
"meds": "Medication?_id={{context.draftOrders.entry.resource.ofType(MedicationRequest).medication.resolve().id}}",
|
|
110
|
+
"roles": "PractitionerRole?_id={{context.draftOrders.entry.resource.sender.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.recipient.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.performer.resolve().ofType(PractitionerRole).id}}",
|
|
111
|
+
"pracs": "Practitioner?_id={{%roles.entry.resource.practitioner.resolve().id|context.draftOrders.entry.resource.sender.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.performer.resolve().ofType(Practitioner).id}}",
|
|
112
|
+
"locs": "Location?_id={{%roles.entry.resource.location.resolve().id|%enc.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}"
|
|
118
113
|
},
|
|
119
114
|
"extension" : {
|
|
120
115
|
"davinci-crd.version" : [
|
|
@@ -133,19 +128,25 @@
|
|
|
133
128
|
}
|
|
134
129
|
},
|
|
135
130
|
{
|
|
136
|
-
"hook": "order-
|
|
137
|
-
"title": "Order
|
|
138
|
-
"description": "A simulated CDS Service requesting a subset of the standard prefetches that fires when a
|
|
139
|
-
"id": "order-
|
|
131
|
+
"hook": "order-dispatch",
|
|
132
|
+
"title": "Order Dispatch CDS Service Example",
|
|
133
|
+
"description": "A simulated CDS Service requesting a subset of the standard prefetches that fires when a practitioner is selecting a candidate performer for a pre-existing order that was not tied to a specific performer",
|
|
134
|
+
"id": "order-dispatch-subset",
|
|
140
135
|
"prefetch": {
|
|
141
136
|
"pat": "Patient/{{context.patientId}}",
|
|
142
137
|
"enc": "Encounter/{{context.encounterId}}",
|
|
143
|
-
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
138
|
+
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
139
|
+
"comReqs": "CommunicationRequest?_id={{context.dispatchedOrders.resolve().ofType(CommunicationRequest).id}}",
|
|
140
|
+
"devReqs": "DeviceRequest?_id={{context.dispatchedOrders.resolve().ofType(DeviceRequest).id}}",
|
|
141
|
+
"medReqs": "MedicationRequest?_id={{context.dispatchedOrders.resolve().ofType(MedicationRequest).id}}",
|
|
142
|
+
"nutOrds": "NutritionOrder?_id={{context.dispatchedOrders.resolve().ofType(NutritionOrder).id}}",
|
|
143
|
+
"servReqs": "ServiceRequest?_id={{context.dispatchedOrders.resolve().ofType(ServiceRequest).id}}",
|
|
144
|
+
"visRxs": "VisionPrescription?_id={{context.dispatchedOrders.resolve().ofType(VisionPrescription).id}}",
|
|
145
|
+
"devs": "Device?_id={{%devReqs.entry.resource.code.resolve().id}}",
|
|
146
|
+
"meds": "Medication?_id={{%medReqs.entry.resource.medication.resolve().id}}",
|
|
147
|
+
"roles": "PractitionerRole?_id={{%comReqs.entry.resource.sender.resolve().ofType(PractitionerRole).id|%comReqs.entry.resource.recipient.resolve().ofType(PractitionerRole).id|%devReqs.entry.resource.performer.resolve().ofType(PractitionerRole).id|%medReqs.entry.resource.performer.resolve().ofType(PractitionerRole).id|%servReqs.entry.resource.performer.resolve().ofType(PractitionerRole).id}}",
|
|
148
|
+
"pracs": "Practitioner?_id={{%roles.entry.resource.practitioner.resolve().id|%comReqs.entry.resource.sender.resolve().ofType(Practitioner).id|%comReqs.entry.resource.recipient.resolve().ofType(Practitioner).id|%devReqs.entry.resource.performer.resolve().ofType(Practitioner).id|%medReqs.entry.resource.performer.resolve().ofType(Practitioner).id|%servReqs.entry.resource.performer.resolve().ofType(Practitioner).id}}",
|
|
149
|
+
"locs": "Location?_id={{%roles.entry.resource.location.resolve().id|%enc.location.location.resolve().id|%servReqs.entry.resource.locationReference.resolve().id}}"
|
|
149
150
|
},
|
|
150
151
|
"extension" : {
|
|
151
152
|
"davinci-crd.version" : [
|
|
@@ -164,19 +165,18 @@
|
|
|
164
165
|
}
|
|
165
166
|
},
|
|
166
167
|
{
|
|
167
|
-
"hook": "
|
|
168
|
-
"title": "
|
|
169
|
-
"description": "A simulated CDS Service requesting a subset of the standard prefetches that
|
|
170
|
-
"id": "
|
|
171
|
-
"prefetch": {
|
|
168
|
+
"hook": "encounter-discharge",
|
|
169
|
+
"title": "Encounter Disharge CDS Service Example",
|
|
170
|
+
"description": "A simulated CDS Service requesting a subset of the standard prefetches that is invoked when the user is performing the discharge process for an encounter - typically an inpatient encounter.",
|
|
171
|
+
"id": "encounter-discharge-subset",
|
|
172
|
+
"prefetch" : {
|
|
172
173
|
"pat": "Patient/{{context.patientId}}",
|
|
173
174
|
"enc": "Encounter/{{context.encounterId}}",
|
|
174
|
-
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
"locs": "Location?_id={{%roles.entry.resource.location.resolve().id|%enc.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}"
|
|
175
|
+
"cov": "Coverage?patient={{context.patientId}}&status=active",
|
|
176
|
+
"roles" : "PractitionerRole?_id={{%enc.participant.individual.resolve().ofType(PractitionerRole).id}}",
|
|
177
|
+
"pracs" : "Practitioner?_id={{%roles.entry.resource.practitioner.resolve().id|%enc.participant.individual.resolve().ofType(Practitioner).id}}",
|
|
178
|
+
"orgs" : "Organization?_id={{%roles.entry.resource.organization.resolve().id}}",
|
|
179
|
+
"locs" : "Location?_id={{%roles.entry.resource.location.resolve().id}}"
|
|
180
180
|
},
|
|
181
181
|
"extension" : {
|
|
182
182
|
"davinci-crd.version" : [
|
|
@@ -67,18 +67,20 @@
|
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
|
-
"hook": "
|
|
71
|
-
"title": "
|
|
72
|
-
"description": "A simulated CDS Service that
|
|
73
|
-
"id": "
|
|
74
|
-
"prefetch"
|
|
70
|
+
"hook": "order-select",
|
|
71
|
+
"title": "Order Select CDS Service",
|
|
72
|
+
"description": "A simulated CDS Service that fires when a clinician selects one or more orders to place for a patient",
|
|
73
|
+
"id": "order-select-service",
|
|
74
|
+
"prefetch": {
|
|
75
75
|
"patient": "Patient/{{context.patientId}}",
|
|
76
76
|
"encounter": "Encounter/{{context.encounterId}}",
|
|
77
|
-
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
77
|
+
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
78
|
+
"devices": "Device?_id={{context.draftOrders.entry.resource.ofType(DeviceRequest).code.resolve().id}}",
|
|
79
|
+
"medications": "Medication?_id={{context.draftOrders.entry.resource.ofType(MedicationRequest).medication.resolve().id}}",
|
|
80
|
+
"practitionerRoles": "PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.sender.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.recipient.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.requester.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.performer.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.orderer.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.prescriber.resolve().ofType(PractitionerRole).id}}",
|
|
81
|
+
"practitioners": "Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.sender.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.requester.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.performer.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.orderer.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.prescriber.resolve().ofType(Practitioner).id}}",
|
|
82
|
+
"organizations": "Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|context.draftOrders.entry.resource.dispenseRequest.performer.resolve().id|context.draftOrders.entry.resource.sender.resolve().ofType(Organization).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Organization).id|context.draftOrders.entry.resource.performer.resolve().ofType(Organization).id}}",
|
|
83
|
+
"locations": "Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}"
|
|
82
84
|
},
|
|
83
85
|
"extension" : {
|
|
84
86
|
"davinci-crd.version" : [
|
|
@@ -97,26 +99,20 @@
|
|
|
97
99
|
}
|
|
98
100
|
},
|
|
99
101
|
{
|
|
100
|
-
"hook": "order-
|
|
101
|
-
"title": "Order
|
|
102
|
-
"description": "A simulated CDS Service that fires when a
|
|
103
|
-
"id": "order-
|
|
102
|
+
"hook": "order-sign",
|
|
103
|
+
"title": "Order Sign CDS Service",
|
|
104
|
+
"description": "A simulated CDS Service that fires when a clinician is ready to sign one or more orders for a patient",
|
|
105
|
+
"id": "order-sign-service",
|
|
104
106
|
"prefetch": {
|
|
105
107
|
"patient": "Patient/{{context.patientId}}",
|
|
106
108
|
"encounter": "Encounter/{{context.encounterId}}",
|
|
107
|
-
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
|
|
114
|
-
"devices": "Device?_id={{%deviceRequests.entry.resource.code.resolve().id}}",
|
|
115
|
-
"medications": "Medication?_id={{%medicationRequests.entry.resource.medication.resolve().id}}",
|
|
116
|
-
"practitionerRoles": "PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.sender.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.recipient.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%nutritionOrders.entry.resource.orderer.resolve().ofType(PractitionerRole).id|%visionPrescriptions.entry.resource.prescriber.resolve().ofType(PractitionerRole).id}}",
|
|
117
|
-
"practitioners": "Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.sender.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.recipient.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%nutritionOrders.entry.resource.orderer.resolve().ofType(Practitioner).id|%visionPrescriptions.entry.resource.prescriber.resolve().ofType(Practitioner).id}}",
|
|
118
|
-
"organizations": "Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|%communicationRequests.entry.resource.recipient.resolve().ofType(Organization).id|%communicationRequests.entry.resource.sender.resolve().ofType(Organization).id|%medicationRequests.entry.resource.dispenseRequest.performer.resolve().id|%serviceRequests.entry.resource.performer.resolve().ofType(Organization).id}}",
|
|
119
|
-
"locations": "Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|%serviceRequests.entry.resource.locationReference.resolve().id}}"
|
|
109
|
+
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
110
|
+
"devices": "Device?_id={{context.draftOrders.entry.resource.ofType(DeviceRequest).code.resolve().id}}",
|
|
111
|
+
"medications": "Medication?_id={{context.draftOrders.entry.resource.ofType(MedicationRequest).medication.resolve().id}}",
|
|
112
|
+
"practitionerRoles": "PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.sender.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.recipient.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.requester.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.performer.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.orderer.resolve().ofType(PractitionerRole).id|context.draftOrders.entry.resource.prescriber.resolve().ofType(PractitionerRole).id}}",
|
|
113
|
+
"practitioners": "Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.sender.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.requester.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.performer.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.orderer.resolve().ofType(Practitioner).id|context.draftOrders.entry.resource.prescriber.resolve().ofType(Practitioner).id}}",
|
|
114
|
+
"organizations": "Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|context.draftOrders.entry.resource.dispenseRequest.performer.resolve().id|context.draftOrders.entry.resource.sender.resolve().ofType(Organization).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Organization).id|context.draftOrders.entry.resource.performer.resolve().ofType(Organization).id}}",
|
|
115
|
+
"locations": "Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}"
|
|
120
116
|
},
|
|
121
117
|
"extension" : {
|
|
122
118
|
"davinci-crd.version" : [
|
|
@@ -135,20 +131,26 @@
|
|
|
135
131
|
}
|
|
136
132
|
},
|
|
137
133
|
{
|
|
138
|
-
"hook": "order-
|
|
139
|
-
"title": "Order
|
|
140
|
-
"description": "A simulated CDS Service that fires when a
|
|
141
|
-
"id": "order-
|
|
134
|
+
"hook": "order-dispatch",
|
|
135
|
+
"title": "Order Dispatch CDS Service Example",
|
|
136
|
+
"description": "A simulated CDS Service that fires when a practitioner is selecting a candidate performer for a pre-existing order that was not tied to a specific performer",
|
|
137
|
+
"id": "order-dispatch-service",
|
|
142
138
|
"prefetch": {
|
|
143
139
|
"patient": "Patient/{{context.patientId}}",
|
|
144
140
|
"encounter": "Encounter/{{context.encounterId}}",
|
|
145
|
-
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
|
|
141
|
+
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
142
|
+
"communicationRequests": "CommunicationRequest?_id={{context.dispatchedOrders.resolve().ofType(CommunicationRequest).id}}",
|
|
143
|
+
"deviceRequests": "DeviceRequest?_id={{context.dispatchedOrders.resolve().ofType(DeviceRequest).id}}",
|
|
144
|
+
"medicationRequests": "MedicationRequest?_id={{context.dispatchedOrders.resolve().ofType(MedicationRequest).id}}",
|
|
145
|
+
"nutritionOrders": "NutritionOrder?_id={{context.dispatchedOrders.resolve().ofType(NutritionOrder).id}}",
|
|
146
|
+
"serviceRequests": "ServiceRequest?_id={{context.dispatchedOrders.resolve().ofType(ServiceRequest).id}}",
|
|
147
|
+
"visionPrescriptions": "VisionPrescription?_id={{context.dispatchedOrders.resolve().ofType(VisionPrescription).id}}",
|
|
148
|
+
"devices": "Device?_id={{%deviceRequests.entry.resource.code.resolve().id}}",
|
|
149
|
+
"medications": "Medication?_id={{%medicationRequests.entry.resource.medication.resolve().id}}",
|
|
150
|
+
"practitionerRoles": "PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.sender.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.recipient.resolve().ofType(PractitionerRole).id|%communicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%deviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%medicationRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.performer.resolve().ofType(PractitionerRole).id|%serviceRequests.entry.resource.requester.resolve().ofType(PractitionerRole).id|%nutritionOrders.entry.resource.orderer.resolve().ofType(PractitionerRole).id|%visionPrescriptions.entry.resource.prescriber.resolve().ofType(PractitionerRole).id}}",
|
|
151
|
+
"practitioners": "Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.sender.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.recipient.resolve().ofType(Practitioner).id|%communicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%deviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%medicationRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.performer.resolve().ofType(Practitioner).id|%serviceRequests.entry.resource.requester.resolve().ofType(Practitioner).id|%nutritionOrders.entry.resource.orderer.resolve().ofType(Practitioner).id|%visionPrescriptions.entry.resource.prescriber.resolve().ofType(Practitioner).id}}",
|
|
152
|
+
"organizations": "Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|%communicationRequests.entry.resource.recipient.resolve().ofType(Organization).id|%communicationRequests.entry.resource.sender.resolve().ofType(Organization).id|%medicationRequests.entry.resource.dispenseRequest.performer.resolve().id|%serviceRequests.entry.resource.performer.resolve().ofType(Organization).id}}",
|
|
153
|
+
"locations": "Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|%serviceRequests.entry.resource.locationReference.resolve().id}}"
|
|
152
154
|
},
|
|
153
155
|
"extension" : {
|
|
154
156
|
"davinci-crd.version" : [
|
|
@@ -167,20 +169,18 @@
|
|
|
167
169
|
}
|
|
168
170
|
},
|
|
169
171
|
{
|
|
170
|
-
"hook": "
|
|
171
|
-
"title": "
|
|
172
|
-
"description": "A simulated CDS Service that
|
|
173
|
-
"id": "
|
|
174
|
-
"prefetch": {
|
|
172
|
+
"hook": "encounter-discharge",
|
|
173
|
+
"title": "Encounter Disharge CDS Service Example",
|
|
174
|
+
"description": "A simulated CDS Service that is invoked when the user is performing the discharge process for an encounter - typically an inpatient encounter.",
|
|
175
|
+
"id": "encounter-discharge-service",
|
|
176
|
+
"prefetch" : {
|
|
175
177
|
"patient": "Patient/{{context.patientId}}",
|
|
176
178
|
"encounter": "Encounter/{{context.encounterId}}",
|
|
177
|
-
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
178
|
-
"
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"organizations": "Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id|context.draftOrders.entry.resource.dispenseRequest.performer.resolve().id|context.draftOrders.entry.resource.sender.resolve().ofType(Organization).id|context.draftOrders.entry.resource.recipient.resolve().ofType(Organization).id|context.draftOrders.entry.resource.performer.resolve().ofType(Organization).id}}",
|
|
183
|
-
"locations": "Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id|context.draftOrders.entry.resource.locationReference.resolve().id}}"
|
|
179
|
+
"coverage": "Coverage?patient={{context.patientId}}&status=active",
|
|
180
|
+
"practitionerRoles" : "PractitionerRole?_id={{%encounter.participant.individual.resolve().ofType(PractitionerRole).id}}",
|
|
181
|
+
"practitioners" : "Practitioner?_id={{%practitionerRoles.entry.resource.practitioner.resolve().id|%encounter.participant.individual.resolve().ofType(Practitioner).id}}",
|
|
182
|
+
"organizations" : "Organization?_id={{%practitionerRoles.entry.resource.organization.resolve().id|%encounter.serviceProvider.resolve().ofType(Organization).id}}",
|
|
183
|
+
"locations" : "Location?_id={{%practitionerRoles.entry.resource.location.resolve().id|%encounter.location.location.resolve().id}}"
|
|
184
184
|
},
|
|
185
185
|
"extension" : {
|
|
186
186
|
"davinci-crd.version" : [
|