davinci_crd_test_kit 0.14.1 → 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} +0 -61
- 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 +63 -38
- data/lib/davinci_crd_test_kit/client/endpoints/mock_service_response.rb +26 -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 +16 -14
- 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 +12 -10
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_encounter_start_group.rb +12 -10
- 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 +9 -9
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_long_running_hook_group.rb +2 -1
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_dispatch_group.rb +16 -14
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_select_group.rb +12 -10
- data/lib/davinci_crd_test_kit/client/v2.2.1/client_order_sign_group.rb +16 -14
- 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 +19 -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 +1 -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 +1 -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 +52 -20
- 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 +9 -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_profile_validation.rb +2 -0
- data/lib/davinci_crd_test_kit/cross_suite/requests_logical_model_validation.rb +26 -27
- data/lib/davinci_crd_test_kit/cross_suite/response_logical_model_validation.rb +48 -41
- data/lib/davinci_crd_test_kit/cross_suite/tags.rb +3 -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 +36 -32
- 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/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/discovery/discovery_standard_prefetch_expressions_test.rb +1 -2
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_demonstrate_hook_response_group.rb +2 -2
- data/lib/davinci_crd_test_kit/server/v2.2.1/server_hooks_group.rb +5 -5
- data/lib/davinci_crd_test_kit/version.rb +2 -2
- metadata +40 -12
- data/config/presets/inferno_crd_server_suite_v221.json.erb +0 -94
|
@@ -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" : [
|
|
@@ -60,7 +60,9 @@ module DaVinciCRDTestKit
|
|
|
60
60
|
crd_jwks_keys_json: { name: :appointment_book_crd_jwks_keys_json }
|
|
61
61
|
},
|
|
62
62
|
options: {
|
|
63
|
-
hook_name: 'appointment-book'
|
|
63
|
+
hook_name: 'appointment-book',
|
|
64
|
+
crd_interaction_group: 'hooks-appointment-book',
|
|
65
|
+
include_in_cross_hook_analysis: true
|
|
64
66
|
}
|
|
65
67
|
)
|
|
66
68
|
|
|
@@ -70,11 +72,14 @@ module DaVinciCRDTestKit
|
|
|
70
72
|
end
|
|
71
73
|
|
|
72
74
|
group do
|
|
73
|
-
title '
|
|
74
|
-
|
|
75
|
-
test from: :
|
|
76
|
-
test from: :
|
|
77
|
-
test from: :
|
|
75
|
+
title 'Response Handling'
|
|
76
|
+
|
|
77
|
+
test from: :crd_v221_card_display_attest_test
|
|
78
|
+
test from: :crd_v221_inferno_response_validation
|
|
79
|
+
test from: :crd_v221_client_hook_response_support_coverage_information do
|
|
80
|
+
title 'Client supports the Coverage Information response type on the appointment-book hook'
|
|
81
|
+
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@hook-26'
|
|
82
|
+
end
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
group do
|
|
@@ -95,14 +100,11 @@ module DaVinciCRDTestKit
|
|
|
95
100
|
end
|
|
96
101
|
|
|
97
102
|
group do
|
|
98
|
-
title '
|
|
99
|
-
|
|
100
|
-
test from: :
|
|
101
|
-
test from: :
|
|
102
|
-
test from: :
|
|
103
|
-
title 'Client supports the Coverage Information response type on the appointment-book hook'
|
|
104
|
-
verifies_requirements 'hl7.fhir.us.davinci-crd_2.2.1@hook-26'
|
|
105
|
-
end
|
|
103
|
+
title 'Authorization'
|
|
104
|
+
test from: :crd_v221_decode_auth_token
|
|
105
|
+
test from: :crd_v221_retrieve_jwks
|
|
106
|
+
test from: :crd_v221_token_header
|
|
107
|
+
test from: :crd_v221_token_payload
|
|
106
108
|
end
|
|
107
109
|
end
|
|
108
110
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require_relative 'attestations/attestation_instructions'
|
|
2
|
+
require_relative 'attestations/client_authorized_scope_attestation_test'
|
|
3
|
+
require_relative 'attestations/client_coverage_based_invocation_attestation_test'
|
|
4
|
+
require_relative 'attestations/client_data_element_expectations_attestation_test'
|
|
5
|
+
require_relative 'attestations/client_hook_invocation_logging_attestation_test'
|
|
6
|
+
require_relative 'attestations/client_must_support_exposure_attestation_test'
|
|
7
|
+
require_relative 'attestations/client_must_support_use_attestation_test'
|
|
8
|
+
require_relative 'attestations/client_order_sign_support_attestation_test'
|
|
9
|
+
require_relative 'attestations/client_prefetch_key_omission_attestation_test'
|
|
10
|
+
require_relative 'attestations/client_resource_identifiers_attestation_test'
|
|
11
|
+
require_relative 'attestations/client_response_case_distinction_attestation_test'
|
|
12
|
+
require_relative 'attestations/client_security_and_privacy_attestation_test'
|
|
13
|
+
require_relative 'attestations/client_workflow_integration_attestation_test'
|
|
14
|
+
|
|
15
|
+
module DaVinciCRDTestKit
|
|
16
|
+
module V221
|
|
17
|
+
class CRDClientAttestationsGroup < Inferno::TestGroup
|
|
18
|
+
id :crd_v221_client_attestations
|
|
19
|
+
title 'Visual Inspection and Attestation'
|
|
20
|
+
description %(
|
|
21
|
+
Each test in this group asks the tester to confirm system conformance to one or more
|
|
22
|
+
**SHALL** requirements from the [CRD v2.2.1 Implementation Guide](https://hl7.org/fhir/us/davinci-crd/2.2.1)
|
|
23
|
+
or the [CDS Hooks specification](https://cds-hooks.hl7.org/2026Jan/en/).
|
|
24
|
+
The tester attests that the client system under test meets the statement by selecting "Yes",
|
|
25
|
+
and may record supporting details in the accompanying notes field. Selecting "No" fails the
|
|
26
|
+
test. Notes provided are recorded in the test result.
|
|
27
|
+
|
|
28
|
+
Testers are responsible for confirming that their system meets all requirements associated with a test
|
|
29
|
+
before selecting "Yes" on the attestation input with the same name as the test. The text of the
|
|
30
|
+
attested requirement(s) for each input can be reviewed by clicking the "View Specification Requirements" link
|
|
31
|
+
in the "About" tab of the test with the same name.
|
|
32
|
+
)
|
|
33
|
+
input_instructions ATTESTATION_INPUT_INSTRUCTIONS
|
|
34
|
+
|
|
35
|
+
test from: :crd_v221_security_and_privacy_attestation
|
|
36
|
+
test from: :crd_v221_response_case_distinction_attestation
|
|
37
|
+
test from: :crd_v221_data_element_expectations_attestation
|
|
38
|
+
test from: :crd_v221_resource_identifiers_attestation
|
|
39
|
+
test from: :crd_v221_must_support_exposure_attestation
|
|
40
|
+
test from: :crd_v221_workflow_integration_attestation
|
|
41
|
+
test from: :crd_v221_coverage_based_invocation_attestation
|
|
42
|
+
test from: :crd_v221_prefetch_key_omission_attestation
|
|
43
|
+
test from: :crd_v221_hook_invocation_logging_attestation
|
|
44
|
+
test from: :crd_v221_order_sign_support_attestation
|
|
45
|
+
test from: :crd_v221_authorized_scope_attestation
|
|
46
|
+
test from: :crd_v221_must_support_use_attestation
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|