davinci_dtr_test_kit 0.13.0 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_questionnaire_completion_group.rb +23 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_questionnaire_followup_questions_group.rb +26 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_questionnaire_next_question_request_test.rb +93 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_questionnaire_next_question_request_validation_test.rb +62 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_questionnaire_next_question_retrieval_group.rb +23 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_adaptive_questionnaire_response_validation_test.rb +66 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_full_ehr_adaptive_dinner_questionnaire_workflow_group.rb +76 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_full_ehr_adaptive_questionnaire_initial_retrieval_group.rb +27 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_full_ehr_adaptive_questionnaire_request_test.rb +63 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_smart_app_adaptive_questionnaire_initial_retrieval_group.rb +24 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_smart_app_adaptive_questionnaire_request_test.rb +148 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_adaptive/dtr_smart_app_questionnaire_workflow_group.rb +75 -0
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_full_ehr_questionnaire_workflow_group.rb +22 -38
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_smart_app_dinner_questionnaire_package_request_test.rb +13 -16
- data/lib/davinci_dtr_test_kit/client_groups/dinner_static/dtr_smart_app_questionnaire_workflow_group.rb +9 -31
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → full_ehr}/dtr_full_ehr_launch_attestation_test.rb +7 -6
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → full_ehr}/dtr_full_ehr_prepopulation_attestation_test.rb +7 -7
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → full_ehr}/dtr_full_ehr_prepopulation_override_attestation_test.rb +7 -7
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static/dtr_full_ehr_dinner_questionnaire_package_request_test.rb → full_ehr/dtr_full_ehr_questionnaire_package_request_test.rb} +2 -3
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static/dtr_full_ehr_dinner_static_questionnaire_response_conformance_test.rb → full_ehr/dtr_full_ehr_questionnaire_response_conformance_test.rb} +7 -3
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static/dtr_full_ehr_dinner_static_questionnaire_response_correctness_test.rb → full_ehr/dtr_full_ehr_questionnaire_response_correctness_test.rb} +14 -7
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → full_ehr}/dtr_full_ehr_rendering_enabled_questions_attestation_test.rb +7 -7
- data/lib/davinci_dtr_test_kit/client_groups/full_ehr/dtr_full_ehr_saving_questionnaire_response_group.rb +29 -0
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → full_ehr}/dtr_full_ehr_store_attestation_test.rb +7 -7
- data/lib/davinci_dtr_test_kit/client_groups/resp_assist_device/dtr_questionnaire_rendering_attestation_test.rb +7 -6
- data/lib/davinci_dtr_test_kit/client_groups/resp_assist_device/dtr_resp_questionnaire_package_request_test.rb +15 -18
- data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_basic_conformance_test.rb +5 -1
- data/lib/davinci_dtr_test_kit/client_groups/shared/dtr_questionnaire_response_pre_population_test.rb +12 -5
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → smart_app}/dtr_smart_app_prepopulation_attestation_test.rb +7 -7
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → smart_app}/dtr_smart_app_prepopulation_override_attestation_test.rb +7 -6
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → smart_app}/dtr_smart_app_questionnaire_response_save_test.rb +17 -7
- data/lib/davinci_dtr_test_kit/client_groups/{dinner_static → smart_app}/dtr_smart_app_rendering_enabled_questions_attestation_test.rb +7 -7
- data/lib/davinci_dtr_test_kit/client_groups/smart_app/dtr_smart_app_saving_questionnaire_response_group.rb +27 -0
- data/lib/davinci_dtr_test_kit/cql_test.rb +37 -140
- data/lib/davinci_dtr_test_kit/create_test.rb +25 -0
- data/lib/davinci_dtr_test_kit/docs/dtr_full_ehr_suite_description_v201.md +95 -37
- data/lib/davinci_dtr_test_kit/docs/dtr_light_ehr_suite_description_v201.md +15 -6
- data/lib/davinci_dtr_test_kit/docs/dtr_payer_server_suite_description_v201.md +32 -29
- data/lib/davinci_dtr_test_kit/docs/dtr_smart_app_suite_description_v201.md +48 -32
- data/lib/davinci_dtr_test_kit/dtr_full_ehr_suite.rb +13 -17
- data/lib/davinci_dtr_test_kit/dtr_light_ehr_suite.rb +67 -2
- data/lib/davinci_dtr_test_kit/dtr_payer_server_suite.rb +9 -20
- data/lib/davinci_dtr_test_kit/dtr_questionnaire_response_validation.rb +18 -10
- data/lib/davinci_dtr_test_kit/dtr_smart_app_suite.rb +32 -59
- data/lib/davinci_dtr_test_kit/endpoints/cors.rb +20 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_authorization/authorize_endpoint.rb +32 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_authorization/simple_token_endpoint.rb +19 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_authorization/token_endpoint.rb +116 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_authorization.rb +83 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_ehr/fhir_get_endpoint.rb +95 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_ehr/questionnaire_response_endpoint.rb +22 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_ehr.rb +25 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer/full_ehr_next_question_endpoint.rb +11 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer/full_ehr_questionnaire_package_endpoint.rb +11 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer/next_question_endpoint.rb +162 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer/next_question_proxy_endpoint.rb +36 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer/questionnaire_package_endpoint.rb +62 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer/questionnaire_package_proxy_endpoint.rb +38 -0
- data/lib/davinci_dtr_test_kit/endpoints/mock_payer.rb +36 -0
- data/lib/davinci_dtr_test_kit/fixtures/dinner_adaptive/dinner_order_adaptive_next_question_burrito.json +10 -2
- data/lib/davinci_dtr_test_kit/fixtures/dinner_adaptive/dinner_order_adaptive_next_question_hamburger.json +10 -2
- data/lib/davinci_dtr_test_kit/fixtures/dinner_adaptive/dinner_order_adaptive_next_question_initial.json +10 -2
- data/lib/davinci_dtr_test_kit/fixtures/dinner_adaptive/questionnaire_dinner_order_adaptive.json +4 -3
- data/lib/davinci_dtr_test_kit/fixtures.rb +24 -1
- data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_form_libraries_test.rb +2 -2
- data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_form_questionnaire_expressions_test.rb +4 -3
- data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_form_questionnaire_extensions_test.rb +3 -2
- data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_next_questionnaire_expressions_test.rb +6 -6
- data/lib/davinci_dtr_test_kit/payer_server_groups/adaptive_next_questionnaire_extensions_test.rb +6 -5
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_group.rb +2 -2
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_bundles_validation_test.rb +6 -9
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_search_validation_test.rb +15 -12
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_adaptive_response_validation_test.rb +33 -23
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_response_complete_test.rb +4 -4
- data/lib/davinci_dtr_test_kit/payer_server_groups/payer_server_next_response_validation_test.rb +16 -12
- data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_libraries_test.rb +2 -2
- data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_questionnaire_expressions_test.rb +5 -4
- data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_questionnaire_extensions_test.rb +4 -3
- data/lib/davinci_dtr_test_kit/payer_server_groups/static_form_response_validation_test.rb +32 -25
- data/lib/davinci_dtr_test_kit/profiles/communication_request/communication_request_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/communication_request/communication_request_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/communication_request_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/profiles/coverage/coverage_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/coverage/coverage_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/coverage_group.rb +38 -0
- data/lib/davinci_dtr_test_kit/profiles/device_request/device_request_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/device_request/device_request_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/device_request_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/profiles/encounter/encounter_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/encounter/encounter_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/encounter_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/profiles/medication_request/medication_request_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/medication_request/medication_request_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/medication_request_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/profiles/nutrition_order/nutrition_order_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/nutrition_order/nutrition_order_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/nutrition_order_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response/questionnaire_response_context_search.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response/questionnaire_response_create.rb +26 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response/questionnaire_response_patient_search.rb +55 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response/questionnaire_response_read.rb +22 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response/questionnaire_response_update.rb +26 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response/questionnaire_response_validation.rb +37 -0
- data/lib/davinci_dtr_test_kit/profiles/questionnaire_response_group.rb +66 -0
- data/lib/davinci_dtr_test_kit/profiles/service_request/service_request_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/service_request/service_request_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/service_request_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/profiles/task/task_create.rb +26 -0
- data/lib/davinci_dtr_test_kit/profiles/task/task_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/task/task_update.rb +26 -0
- data/lib/davinci_dtr_test_kit/profiles/task/task_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/task_group.rb +52 -0
- data/lib/davinci_dtr_test_kit/profiles/vision_prescription/vision_prescription_read.rb +29 -0
- data/lib/davinci_dtr_test_kit/profiles/vision_prescription/vision_prescription_validation.rb +35 -0
- data/lib/davinci_dtr_test_kit/profiles/vision_prescription_group.rb +39 -0
- data/lib/davinci_dtr_test_kit/read_test.rb +22 -0
- data/lib/davinci_dtr_test_kit/tags.rb +5 -7
- data/lib/davinci_dtr_test_kit/update_test.rb +25 -0
- data/lib/davinci_dtr_test_kit/validation_test.rb +19 -4
- data/lib/davinci_dtr_test_kit/version.rb +1 -1
- metadata +110 -21
- data/lib/davinci_dtr_test_kit/ext/inferno_core/record_response_route.rb +0 -98
- data/lib/davinci_dtr_test_kit/ext/inferno_core/request.rb +0 -19
- data/lib/davinci_dtr_test_kit/ext/inferno_core/runnable.rb +0 -35
- data/lib/davinci_dtr_test_kit/mock_auth_server.rb +0 -228
- data/lib/davinci_dtr_test_kit/mock_ehr.rb +0 -105
- data/lib/davinci_dtr_test_kit/mock_payer.rb +0 -100
@@ -1,6 +1,17 @@
|
|
1
|
+
require 'us_core_test_kit'
|
1
2
|
require 'tls_test_kit'
|
2
3
|
require_relative 'version'
|
3
4
|
require_relative 'dtr_options'
|
5
|
+
require_relative 'profiles/questionnaire_response_group'
|
6
|
+
require_relative 'profiles/coverage_group'
|
7
|
+
require_relative 'profiles/communication_request_group'
|
8
|
+
require_relative 'profiles/device_request_group'
|
9
|
+
require_relative 'profiles/encounter_group'
|
10
|
+
require_relative 'profiles/medication_request_group'
|
11
|
+
require_relative 'profiles/nutrition_order_group'
|
12
|
+
require_relative 'profiles/service_request_group'
|
13
|
+
require_relative 'profiles/task_group'
|
14
|
+
require_relative 'profiles/vision_prescription_group'
|
4
15
|
require 'smart_app_launch/smart_stu1_suite'
|
5
16
|
require 'smart_app_launch/smart_stu2_suite'
|
6
17
|
|
@@ -32,8 +43,17 @@ module DaVinciDTRTestKit
|
|
32
43
|
]
|
33
44
|
|
34
45
|
input :url,
|
35
|
-
title: 'FHIR
|
36
|
-
description: 'URL of the DTR
|
46
|
+
title: 'FHIR Server Base Url',
|
47
|
+
description: 'URL of the target DTR Light EHR'
|
48
|
+
|
49
|
+
# Hl7 Validator Wrapper:
|
50
|
+
fhir_resource_validator do
|
51
|
+
igs('hl7.fhir.us.davinci-dtr#2.0.1', 'hl7.fhir.us.davinci-pas#2.0.1', 'hl7.fhir.us.davinci-crd#2.0.1')
|
52
|
+
|
53
|
+
exclude_message do |message|
|
54
|
+
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
55
|
+
end
|
56
|
+
end
|
37
57
|
|
38
58
|
group do
|
39
59
|
title 'Authorization'
|
@@ -71,5 +91,50 @@ module DaVinciDTRTestKit
|
|
71
91
|
required_suite_options: DTROptions::SMART_2_REQUIREMENT,
|
72
92
|
run_as_group: true
|
73
93
|
end
|
94
|
+
|
95
|
+
group do
|
96
|
+
title 'FHIR API'
|
97
|
+
description %(This test group tests systems for their conformance to
|
98
|
+
the US Core v3.1.1 Capability Statement as defined by the DaVinci Documentation
|
99
|
+
Templates and Rules (DTR) v2.0.1 Implementation Guide Light DTR EHR
|
100
|
+
Capability Statement.
|
101
|
+
|
102
|
+
)
|
103
|
+
|
104
|
+
group from: :'us_core_v311-us_core_v311_fhir_api',
|
105
|
+
run_as_group: true
|
106
|
+
end
|
107
|
+
|
108
|
+
group do
|
109
|
+
title 'DTR Light EHR Profiles'
|
110
|
+
description %(This test group tests system for their conformance to
|
111
|
+
the RESTful capabilities by specified Resources/Profiles as defined by
|
112
|
+
the DaVinci Documentation Templates and Rules (DTR) v2.0,1 Implementation
|
113
|
+
Guide Light DTR EHR Capability Statement.
|
114
|
+
|
115
|
+
)
|
116
|
+
|
117
|
+
input :credentials,
|
118
|
+
title: 'OAuth Credentials',
|
119
|
+
type: :oauth_credentials,
|
120
|
+
optional: true
|
121
|
+
|
122
|
+
# All FHIR requests in this suite will use this FHIR client
|
123
|
+
fhir_client do
|
124
|
+
url :url
|
125
|
+
oauth_credentials :credentials
|
126
|
+
end
|
127
|
+
|
128
|
+
group from: :questionnaire_response_group
|
129
|
+
group from: :coverage_group
|
130
|
+
group from: :communication_request_group
|
131
|
+
group from: :device_request_group
|
132
|
+
group from: :encounter_group
|
133
|
+
group from: :medication_request_group
|
134
|
+
group from: :nutrition_order_group
|
135
|
+
group from: :service_request_group
|
136
|
+
group from: :task_group
|
137
|
+
group from: :vision_prescription_group
|
138
|
+
end
|
74
139
|
end
|
75
140
|
end
|
@@ -1,17 +1,15 @@
|
|
1
|
-
require_relative 'ext/inferno_core/runnable'
|
2
|
-
require_relative 'ext/inferno_core/record_response_route'
|
3
|
-
require_relative 'ext/inferno_core/request'
|
4
1
|
require_relative 'payer_server_groups/payer_server_static_group'
|
5
2
|
require_relative 'payer_server_groups/payer_server_adaptive_group'
|
6
3
|
require_relative 'tags'
|
7
|
-
require_relative '
|
8
|
-
require_relative '
|
4
|
+
require_relative 'endpoints/cors'
|
5
|
+
require_relative 'endpoints/mock_authorization/simple_token_endpoint'
|
6
|
+
require_relative 'endpoints/mock_payer/questionnaire_package_proxy_endpoint'
|
7
|
+
require_relative 'endpoints/mock_payer/next_question_proxy_endpoint'
|
9
8
|
require_relative 'version'
|
10
9
|
|
11
10
|
module DaVinciDTRTestKit
|
12
11
|
class DTRPayerServerSuite < Inferno::TestSuite
|
13
|
-
extend
|
14
|
-
extend MockPayer
|
12
|
+
extend CORS
|
15
13
|
|
16
14
|
id :dtr_payer_server
|
17
15
|
title 'Da Vinci DTR Payer Server Test Suite'
|
@@ -106,22 +104,13 @@ module DaVinciDTRTestKit
|
|
106
104
|
|
107
105
|
allow_cors QUESTIONNAIRE_PACKAGE_PATH, NEXT_PATH
|
108
106
|
|
109
|
-
|
110
|
-
DTRPayerServerSuite.extract_client_id_from_form_params(request)
|
111
|
-
end
|
112
|
-
|
113
|
-
record_response_route :post, QUESTIONNAIRE_PACKAGE_PATH, QUESTIONNAIRE_TAG,
|
114
|
-
method(:payer_questionnaire_response), resumes: method(:test_resumes?) do |request|
|
115
|
-
DTRPayerServerSuite.extract_bearer_token(request)
|
116
|
-
end
|
107
|
+
suite_endpoint :post, PAYER_TOKEN_PATH, MockAuthorization::SimpleTokenEndpoint
|
117
108
|
|
118
|
-
|
119
|
-
|
120
|
-
DTRPayerServerSuite.extract_bearer_token(request)
|
121
|
-
end
|
109
|
+
suite_endpoint :post, QUESTIONNAIRE_PACKAGE_PATH, MockPayer::QuestionnairePackageProxyEndpoint
|
110
|
+
suite_endpoint :post, NEXT_PATH, MockPayer::NextQuestionProxyEndpoint
|
122
111
|
|
123
112
|
resume_test_route :get, RESUME_PASS_PATH do |request|
|
124
|
-
|
113
|
+
request.query_parameters['token']
|
125
114
|
end
|
126
115
|
|
127
116
|
group from: :payer_server_static_package
|
@@ -20,10 +20,10 @@ module DaVinciDTRTestKit
|
|
20
20
|
assert_resource_type(:questionnaire_response, resource: questionnaire_response)
|
21
21
|
end
|
22
22
|
|
23
|
-
def verify_basic_conformance(questionnaire_response_json)
|
23
|
+
def verify_basic_conformance(questionnaire_response_json, profile_url = nil)
|
24
|
+
profile_url ||= 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaireresponse|2.0.1'
|
24
25
|
check_is_questionnaire_response(questionnaire_response_json)
|
25
|
-
assert_valid_resource(resource: FHIR.from_contents(questionnaire_response_json),
|
26
|
-
profile_url: 'http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaireresponse|2.0.1')
|
26
|
+
assert_valid_resource(resource: FHIR.from_contents(questionnaire_response_json), profile_url:)
|
27
27
|
end
|
28
28
|
|
29
29
|
# This only checks answers in the questionnaire response, meaning it does not catch missing answers
|
@@ -55,17 +55,25 @@ module DaVinciDTRTestKit
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
#
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
add_message('error', "No answer for item #{
|
58
|
+
# Ensures that all required questions have been answered.
|
59
|
+
# If required_link_ids not provided, all questions are treated as optional.
|
60
|
+
def check_answer_presence(response_items, required_link_ids = [])
|
61
|
+
required_link_ids.each do |link_id|
|
62
|
+
item = find_item_by_link_id(response_items, link_id)
|
63
|
+
unless item&.answer&.any? { |answer| answer.value.present? }
|
64
|
+
add_message('error', "No answer for item #{link_id}")
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
def extract_required_link_ids(questionnaire_items)
|
70
|
+
questionnaire_items.each_with_object([]) do |item, required_link_ids|
|
71
|
+
required_link_ids << item.linkId if item.required
|
72
|
+
|
73
|
+
required_link_ids.concat(extract_required_link_ids(item.item)) if item.item.present?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
69
77
|
# Requirements:
|
70
78
|
# - Prior to exposing the draft QuestionnaireResponse to the user for completion and/or review, the DTR client
|
71
79
|
# SHALL execute all CQL necessary to resolve the initialExpression, candidateExpression and
|
@@ -1,19 +1,21 @@
|
|
1
|
-
require_relative 'ext/inferno_core/runnable'
|
2
|
-
require_relative 'ext/inferno_core/record_response_route'
|
3
|
-
require_relative 'ext/inferno_core/request'
|
4
1
|
require_relative 'auth_groups/oauth2_authentication_group'
|
5
2
|
require_relative 'client_groups/resp_assist_device/dtr_smart_app_questionnaire_workflow_group'
|
6
3
|
require_relative 'client_groups/dinner_static/dtr_smart_app_questionnaire_workflow_group'
|
7
4
|
require_relative 'client_groups/dinner_adaptive/dtr_smart_app_questionnaire_workflow_group'
|
8
|
-
require_relative '
|
9
|
-
require_relative '
|
5
|
+
require_relative 'endpoints/cors'
|
6
|
+
require_relative 'endpoints/mock_authorization'
|
7
|
+
require_relative 'endpoints/mock_authorization/authorize_endpoint'
|
8
|
+
require_relative 'endpoints/mock_authorization/token_endpoint'
|
9
|
+
require_relative 'endpoints/mock_payer/questionnaire_package_endpoint'
|
10
|
+
require_relative 'endpoints/mock_payer/next_question_endpoint'
|
11
|
+
require_relative 'endpoints/mock_ehr'
|
12
|
+
require_relative 'endpoints/mock_ehr/questionnaire_response_endpoint'
|
13
|
+
require_relative 'endpoints/mock_ehr/fhir_get_endpoint'
|
10
14
|
require_relative 'version'
|
11
15
|
|
12
16
|
module DaVinciDTRTestKit
|
13
17
|
class DTRSmartAppSuite < Inferno::TestSuite
|
14
|
-
extend
|
15
|
-
extend MockEHR
|
16
|
-
extend MockPayer
|
18
|
+
extend CORS
|
17
19
|
|
18
20
|
id :dtr_smart_app
|
19
21
|
title 'Da Vinci DTR SMART App Test Suite'
|
@@ -50,61 +52,32 @@ module DaVinciDTRTestKit
|
|
50
52
|
end
|
51
53
|
|
52
54
|
allow_cors QUESTIONNAIRE_PACKAGE_PATH, QUESTIONNAIRE_RESPONSE_PATH, FHIR_RESOURCE_PATH, FHIR_SEARCH_PATH,
|
53
|
-
EHR_AUTHORIZE_PATH, EHR_TOKEN_PATH, JKWS_PATH, OPENID_CONFIG_PATH
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
route(:get,
|
58
|
-
route(:get,
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
record_response_route :post, EHR_TOKEN_PATH, 'dtr_smart_app_ehr_token', method(:ehr_token_response),
|
73
|
-
resumes: ->(_) { false } do |request|
|
74
|
-
DTRSmartAppSuite.extract_client_id_from_token_request(request)
|
75
|
-
end
|
76
|
-
|
77
|
-
record_response_route :post, PAYER_TOKEN_PATH, 'dtr_smart_app_payer_token',
|
78
|
-
method(:payer_token_response) do |request|
|
79
|
-
DTRSmartAppSuite.extract_client_id_from_client_assertion(request)
|
80
|
-
end
|
81
|
-
|
82
|
-
record_response_route :post, QUESTIONNAIRE_PACKAGE_PATH, QUESTIONNAIRE_PACKAGE_TAG,
|
83
|
-
method(:questionnaire_package_response), resumes: ->(_) { false } do |request|
|
84
|
-
DTRSmartAppSuite.extract_client_id_from_bearer_token(request)
|
85
|
-
end
|
86
|
-
|
87
|
-
record_response_route :post, QUESTIONNAIRE_RESPONSE_PATH, 'dtr_smart_app_questionnaire_response',
|
88
|
-
method(:questionnaire_response_response) do |request|
|
89
|
-
DTRSmartAppSuite.extract_client_id_from_bearer_token(request)
|
90
|
-
end
|
91
|
-
|
92
|
-
record_response_route :get, FHIR_RESOURCE_PATH, SMART_APP_EHR_REQUEST_TAG, method(:get_fhir_resource),
|
93
|
-
resumes: ->(_) { false } do |request|
|
94
|
-
DTRSmartAppSuite.extract_client_id_from_bearer_token(request)
|
95
|
-
end
|
96
|
-
|
97
|
-
record_response_route :get, FHIR_SEARCH_PATH, SMART_APP_EHR_REQUEST_TAG, method(:get_fhir_resource),
|
98
|
-
resumes: ->(_) { false } do |request|
|
99
|
-
DTRSmartAppSuite.extract_client_id_from_bearer_token(request)
|
100
|
-
end
|
55
|
+
EHR_AUTHORIZE_PATH, EHR_TOKEN_PATH, JKWS_PATH, OPENID_CONFIG_PATH, NEXT_PATH
|
56
|
+
|
57
|
+
# Authorization server
|
58
|
+
route(:get, SMART_CONFIG_PATH, MockAuthorization.method(:ehr_smart_config))
|
59
|
+
route(:get, OPENID_CONFIG_PATH, MockAuthorization.method(:ehr_openid_config))
|
60
|
+
route(:get, JKWS_PATH, MockAuthorization.method(:jwks))
|
61
|
+
suite_endpoint :get, EHR_AUTHORIZE_PATH, MockAuthorization::AuthorizeEndpoint
|
62
|
+
suite_endpoint :post, EHR_AUTHORIZE_PATH, MockAuthorization::AuthorizeEndpoint
|
63
|
+
suite_endpoint :post, EHR_TOKEN_PATH, MockAuthorization::TokenEndpoint
|
64
|
+
|
65
|
+
# Payer
|
66
|
+
suite_endpoint :post, QUESTIONNAIRE_PACKAGE_PATH, MockPayer::QuestionnairePackageEndpoint
|
67
|
+
suite_endpoint :post, NEXT_PATH, MockPayer::NextQuestionEndpoint
|
68
|
+
|
69
|
+
# EHR
|
70
|
+
route(:get, '/fhir/metadata', MockEHR.method(:metadata))
|
71
|
+
suite_endpoint :post, QUESTIONNAIRE_RESPONSE_PATH, MockEHR::QuestionnaireResponseEndpoint
|
72
|
+
suite_endpoint :get, FHIR_RESOURCE_PATH, MockEHR::FHIRGetEndpoint
|
73
|
+
suite_endpoint :get, FHIR_SEARCH_PATH, MockEHR::FHIRGetEndpoint
|
101
74
|
|
102
75
|
resume_test_route :get, RESUME_PASS_PATH do |request|
|
103
|
-
|
76
|
+
request.query_parameters['client_id'] || request.query_parameters['token']
|
104
77
|
end
|
105
78
|
|
106
79
|
resume_test_route :get, RESUME_FAIL_PATH, result: 'fail' do |request|
|
107
|
-
|
80
|
+
request.query_parameters['client_id'] || request.query_parameters['token']
|
108
81
|
end
|
109
82
|
|
110
83
|
# TODO: Update based on SMART Launch changes. Do we even want to have this group now?
|
@@ -118,7 +91,7 @@ module DaVinciDTRTestKit
|
|
118
91
|
)
|
119
92
|
|
120
93
|
group from: :dtr_smart_app_static_dinner_questionnaire_workflow
|
121
|
-
|
94
|
+
group from: :dtr_smart_app_adaptive_dinner_questionnaire_workflow
|
122
95
|
end
|
123
96
|
group do
|
124
97
|
id :dtr_smart_app_questionnaire_functionality
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module DaVinciDTRTestKit
|
2
|
+
module CORS
|
3
|
+
PRE_FLIGHT_HANDLER = proc do
|
4
|
+
[
|
5
|
+
200,
|
6
|
+
{
|
7
|
+
'Access-Control-Allow-Origin' => '*',
|
8
|
+
'Access-Control-Allow-Headers' => 'Content-Type, Authorization'
|
9
|
+
},
|
10
|
+
['']
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
def allow_cors(*paths)
|
15
|
+
paths.each do |path|
|
16
|
+
route(:options, path, PRE_FLIGHT_HANDLER)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DaVinciDTRTestKit
|
2
|
+
module MockAuthorization
|
3
|
+
class AuthorizeEndpoint < Inferno::DSL::SuiteEndpoint
|
4
|
+
def test_run_identifier
|
5
|
+
request.params[:client_id]
|
6
|
+
end
|
7
|
+
|
8
|
+
def tags
|
9
|
+
[EHR_AUTHORIZE_TAG]
|
10
|
+
end
|
11
|
+
|
12
|
+
def make_response
|
13
|
+
if request.params[:redirect_uri].present?
|
14
|
+
redirect_uri = "#{request.params[:redirect_uri]}?" \
|
15
|
+
"code=#{SecureRandom.hex}&" \
|
16
|
+
"state=#{request.params[:state]}"
|
17
|
+
response.status = 302
|
18
|
+
response.headers['Location'] = redirect_uri
|
19
|
+
else
|
20
|
+
response.status = 400
|
21
|
+
response.format = 'application/fhir+json'
|
22
|
+
response.body = FHIR::OperationOutcome.new(
|
23
|
+
issue: FHIR::OperationOutcome::Issue.new(severity: 'fatal', code: 'required',
|
24
|
+
details: FHIR::CodeableConcept.new(
|
25
|
+
text: 'No redirect_uri provided'
|
26
|
+
))
|
27
|
+
).to_json
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module DaVinciDTRTestKit
|
2
|
+
module MockAuthorization
|
3
|
+
class SimpleTokenEndpoint < Inferno::DSL::SuiteEndpoint
|
4
|
+
def test_run_identifier
|
5
|
+
request.params[:client_id]
|
6
|
+
end
|
7
|
+
|
8
|
+
# Placeholder for a more complete mock token endpoint
|
9
|
+
def make_response
|
10
|
+
response.body = { access_token: SecureRandom.hex, token_type: 'bearer', expires_in: 3600 }.to_json
|
11
|
+
response.status = 200
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_result
|
15
|
+
results_repo.update_result(result.id, 'pass')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../urls'
|
4
|
+
require_relative '../mock_authorization'
|
5
|
+
|
6
|
+
module DaVinciDTRTestKit
|
7
|
+
module MockAuthorization
|
8
|
+
AUTHORIZED_PRACTITIONER_ID = 'pra1234' # Must exist on the FHIR_REFERENCE_SERVER (env var)
|
9
|
+
|
10
|
+
class TokenEndpoint < Inferno::DSL::SuiteEndpoint
|
11
|
+
def test_run_identifier
|
12
|
+
extract_client_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def make_response
|
16
|
+
client_id = extract_client_id
|
17
|
+
access_token = JWT.encode({ inferno_client_id: client_id }, nil, 'none')
|
18
|
+
granted_scopes = SUPPORTED_SCOPES & requested_scopes
|
19
|
+
|
20
|
+
response_hash = { access_token:, scope: granted_scopes.join(' '), token_type: 'bearer', expires_in: 3600 }
|
21
|
+
|
22
|
+
if granted_scopes.include?('openid')
|
23
|
+
response_hash.merge!(id_token: create_id_token(client_id, fhir_user: granted_scopes.include?('fhirUser')))
|
24
|
+
end
|
25
|
+
|
26
|
+
fhir_context_input = find_test_input("#{input_group_prefix}_smart_fhir_context")
|
27
|
+
begin
|
28
|
+
fhir_context = JSON.parse(fhir_context_input)
|
29
|
+
rescue StandardError
|
30
|
+
fhir_context = nil
|
31
|
+
end
|
32
|
+
response_hash.merge!(fhirContext: fhir_context) if fhir_context
|
33
|
+
|
34
|
+
smart_patient_input = find_test_input("#{input_group_prefix}_smart_patient_id")
|
35
|
+
response_hash.merge!(patient: smart_patient_input) if smart_patient_input
|
36
|
+
|
37
|
+
response.body = response_hash.to_json
|
38
|
+
response.headers['Cache-Control'] = 'no-store'
|
39
|
+
response.headers['Pragma'] = 'no-cache'
|
40
|
+
response.headers['Access-Control-Allow-Origin'] = '*'
|
41
|
+
response.status = 200
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def extract_client_id
|
47
|
+
# Public client || confidential client asymmetric || confidential client symmetric
|
48
|
+
request.params[:client_id] || extract_client_id_from_client_assertion || extract_client_id_from_basic_auth
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_client_id_from_client_assertion
|
52
|
+
encoded_jwt = request.params[:client_assertion]
|
53
|
+
return unless encoded_jwt.present?
|
54
|
+
|
55
|
+
jwt_payload =
|
56
|
+
begin
|
57
|
+
JWT.decode(encoded_jwt, nil, false)&.first # skip signature verification
|
58
|
+
rescue StandardError
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
jwt_payload['iss'] || jwt_payload['sub'] if jwt_payload.present?
|
63
|
+
end
|
64
|
+
|
65
|
+
def input_group_prefix
|
66
|
+
if test.id.include?('static')
|
67
|
+
'static'
|
68
|
+
elsif test.id.include?('adaptive')
|
69
|
+
'adaptive'
|
70
|
+
else
|
71
|
+
'resp'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_test_input(input_name)
|
76
|
+
JSON.parse(result.input_json)&.find { |input| input['name'] == input_name }&.dig('value')
|
77
|
+
end
|
78
|
+
|
79
|
+
def extract_client_id_from_basic_auth
|
80
|
+
encoded_credentials = request.headers['authorization']&.delete_prefix('Basic ')
|
81
|
+
return unless encoded_credentials.present?
|
82
|
+
|
83
|
+
decoded_credentials = Base64.decode64(encoded_credentials)
|
84
|
+
decoded_credentials&.split(':')&.first
|
85
|
+
end
|
86
|
+
|
87
|
+
def requested_scopes
|
88
|
+
auth_request = requests_repo.tagged_requests(result.test_session_id, [EHR_AUTHORIZE_TAG]).last
|
89
|
+
return [] unless auth_request
|
90
|
+
|
91
|
+
auth_params = if auth_request.verb.downcase == 'get'
|
92
|
+
auth_request.query_parameters
|
93
|
+
else
|
94
|
+
URI.decode_www_form(auth_request.request_body)&.to_h
|
95
|
+
end
|
96
|
+
scope_str = auth_params&.dig('scope')
|
97
|
+
scope_str ? URI.decode_www_form_component(scope_str).split : []
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_id_token(client_id, fhir_user: false)
|
101
|
+
# No point in mocking an identity provider, just always use known Practitioner as the authorized user
|
102
|
+
suite_fhir_base_url = request.url.split(EHR_TOKEN_PATH).first + FHIR_BASE_PATH
|
103
|
+
id_token_hash = {
|
104
|
+
iss: suite_fhir_base_url,
|
105
|
+
sub: AUTHORIZED_PRACTITIONER_ID,
|
106
|
+
aud: client_id,
|
107
|
+
exp: Time.now.to_i + (24 * 60 * 60), # 24 hrs
|
108
|
+
iat: Time.now.to_i
|
109
|
+
}
|
110
|
+
id_token_hash.merge!(fhirUser: "#{suite_fhir_base_url}/Practitioner/#{AUTHORIZED_PRACTITIONER_ID}") if fhir_user
|
111
|
+
|
112
|
+
JWT.encode(id_token_hash, RSA_PRIVATE_KEY, 'RS256')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module DaVinciDTRTestKit
|
2
|
+
module MockAuthorization
|
3
|
+
RSA_PRIVATE_KEY = OpenSSL::PKey::RSA.generate(2048)
|
4
|
+
RSA_PUBLIC_KEY = RSA_PRIVATE_KEY.public_key
|
5
|
+
SUPPORTED_SCOPES = ['launch', 'patient/*.rs', 'user/*.rs', 'offline_access', 'openid', 'fhirUser'].freeze
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def extract_client_id_from_bearer_token(request)
|
10
|
+
token = request.headers['authorization']&.delete_prefix('Bearer ')
|
11
|
+
jwt =
|
12
|
+
begin
|
13
|
+
JWT.decode(token, nil, false)
|
14
|
+
rescue StandardError
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
jwt&.first&.dig('inferno_client_id')
|
18
|
+
end
|
19
|
+
|
20
|
+
def jwks(_env)
|
21
|
+
response_body = {
|
22
|
+
keys: [
|
23
|
+
{
|
24
|
+
kty: 'RSA',
|
25
|
+
alg: 'RS256',
|
26
|
+
n: Base64.urlsafe_encode64(RSA_PUBLIC_KEY.n.to_s(2), padding: false),
|
27
|
+
e: Base64.urlsafe_encode64(RSA_PUBLIC_KEY.e.to_s(2), padding: false),
|
28
|
+
use: 'sig'
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}.to_json
|
32
|
+
|
33
|
+
[200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
|
34
|
+
end
|
35
|
+
|
36
|
+
def ehr_smart_config(env)
|
37
|
+
base_url = env_base_url(env, SMART_CONFIG_PATH)
|
38
|
+
response_body =
|
39
|
+
{
|
40
|
+
authorization_endpoint: base_url + EHR_AUTHORIZE_PATH,
|
41
|
+
token_endpoint: base_url + EHR_TOKEN_PATH,
|
42
|
+
token_endpoint_auth_methods_supported: ['private_key_jwt'],
|
43
|
+
token_endpoint_auth_signing_alg_values_supported: ['RS256'],
|
44
|
+
grant_types_supported: ['authorization_code'],
|
45
|
+
scopes_supported: SUPPORTED_SCOPES,
|
46
|
+
response_types_supported: ['code'],
|
47
|
+
code_challenge_methods_supported: ['S256'],
|
48
|
+
capabilities: [
|
49
|
+
'launch-ehr',
|
50
|
+
'permission-patient',
|
51
|
+
'permission-user',
|
52
|
+
'client-public',
|
53
|
+
'client-confidential-symmetric',
|
54
|
+
'client-confidential-asymmetric'
|
55
|
+
]
|
56
|
+
}.to_json
|
57
|
+
|
58
|
+
[200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
|
59
|
+
end
|
60
|
+
|
61
|
+
def ehr_openid_config(env)
|
62
|
+
base_url = env_base_url(env, OPENID_CONFIG_PATH)
|
63
|
+
response_body = {
|
64
|
+
issuer: base_url + FHIR_BASE_PATH,
|
65
|
+
authorization_endpoint: base_url + EHR_AUTHORIZE_PATH,
|
66
|
+
token_endpoint: base_url + EHR_TOKEN_PATH,
|
67
|
+
jwks_uri: base_url + JKWS_PATH,
|
68
|
+
response_types_supported: ['id_token'],
|
69
|
+
subject_types_supported: ['public'],
|
70
|
+
id_token_signing_alg_values_supported: ['RS256']
|
71
|
+
}.to_json
|
72
|
+
[200, { 'Content-Type' => 'application/json', 'Access-Control-Allow-Origin' => '*' }, [response_body]]
|
73
|
+
end
|
74
|
+
|
75
|
+
def env_base_url(env, endpoint_path)
|
76
|
+
protocol = env['rack.url_scheme']
|
77
|
+
host = env['HTTP_HOST']
|
78
|
+
path = env['REQUEST_PATH'] || env['PATH_INFO']
|
79
|
+
path.gsub!(%r{#{endpoint_path}(/)?}, '')
|
80
|
+
"#{protocol}://#{host + path}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative '../mock_authorization'
|
2
|
+
require_relative '../mock_ehr'
|
3
|
+
|
4
|
+
module DaVinciDTRTestKit
|
5
|
+
module MockEHR
|
6
|
+
class FHIRGetEndpoint < Inferno::DSL::SuiteEndpoint
|
7
|
+
include MockEHR
|
8
|
+
|
9
|
+
def test_run_identifier
|
10
|
+
MockAuthorization.extract_client_id_from_bearer_token(request)
|
11
|
+
end
|
12
|
+
|
13
|
+
def make_response
|
14
|
+
fhir_class, id = fhir_class_and_id_from_url(request.url)
|
15
|
+
response.format = 'application/fhir+json'
|
16
|
+
response.headers['Access-Control-Allow-Origin'] = '*'
|
17
|
+
|
18
|
+
if fhir_class.nil?
|
19
|
+
response.status = 400
|
20
|
+
response.body = FHIR::OperationOutcome.new(
|
21
|
+
issue: FHIR::OperationOutcome::Issue.new(severity: 'warning', code: 'not-supported',
|
22
|
+
details: FHIR::CodeableConcept.new(
|
23
|
+
text: 'No recognized resource type in URL'
|
24
|
+
))
|
25
|
+
).to_json
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
# Respond with user-inputted resource if there is one that matches the request
|
30
|
+
ehr_bundle = ehr_input_bundle(test, result)
|
31
|
+
if id.present? && ehr_bundle.present?
|
32
|
+
matching_resource = find_resource_in_bundle(ehr_bundle, fhir_class, id)
|
33
|
+
if matching_resource.present?
|
34
|
+
response.status = 200
|
35
|
+
response.body = matching_resource.to_json
|
36
|
+
return
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Proxy resource request to the reference server
|
41
|
+
proxy_response = if id.present?
|
42
|
+
resource_server_client.read(fhir_class, id)
|
43
|
+
else
|
44
|
+
resource_server_client.search(fhir_class,
|
45
|
+
search: { parameters: request.env['rack.request.query_hash'] })
|
46
|
+
end
|
47
|
+
response.status = proxy_response.code
|
48
|
+
response.body = proxy_response.body
|
49
|
+
end
|
50
|
+
|
51
|
+
def ehr_input_bundle(test, test_result)
|
52
|
+
input_name = "#{input_group_prefix(test)}_ehr_bundle"
|
53
|
+
ehr_bundle_input = JSON.parse(test_result.input_json).find { |input| input['name'] == input_name }
|
54
|
+
ehr_bundle_input_value = ehr_bundle_input_value = ehr_bundle_input['value'] if ehr_bundle_input.present?
|
55
|
+
ehr_bundle = FHIR.from_contents(ehr_bundle_input_value) if ehr_bundle_input_value.present?
|
56
|
+
ehr_bundle if ehr_bundle.is_a?(FHIR::Bundle)
|
57
|
+
rescue StandardError
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def input_group_prefix(test)
|
62
|
+
if test.id.include?('static')
|
63
|
+
'static'
|
64
|
+
elsif test.id.include?('adaptive')
|
65
|
+
'adaptive'
|
66
|
+
else
|
67
|
+
'resp'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_resource_in_bundle(bundle, fhir_class, id)
|
72
|
+
bundle.entry&.find do |entry|
|
73
|
+
entry.resource.is_a?(fhir_class) && entry.resource&.id == id
|
74
|
+
end&.resource
|
75
|
+
end
|
76
|
+
|
77
|
+
# Pull resource type class and ID from url
|
78
|
+
# e.g. http://example.org/fhir/Patient/123 -> [FHIR::Patient, '123']
|
79
|
+
# @private
|
80
|
+
def fhir_class_and_id_from_url(url)
|
81
|
+
path = url.split('?').first.split('/fhir/').second
|
82
|
+
path.sub!(%r{/$}, '')
|
83
|
+
resource_type, id = path.split('/')
|
84
|
+
|
85
|
+
begin
|
86
|
+
fhir_class = FHIR.const_get(resource_type)
|
87
|
+
rescue NameError
|
88
|
+
fhir_class = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
[fhir_class, id]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|