davinci_pdex_test_kit 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/lib/davinci_pdex_test_kit/docs/payer_client_suite_description_v200.md +91 -0
  4. data/lib/davinci_pdex_test_kit/docs/payer_server_suite_description_v200.md +119 -0
  5. data/lib/davinci_pdex_test_kit/ext/inferno_core/record_response_route.rb +98 -0
  6. data/lib/davinci_pdex_test_kit/ext/inferno_core/request.rb +19 -0
  7. data/lib/davinci_pdex_test_kit/ext/inferno_core/runnable.rb +18 -0
  8. data/lib/davinci_pdex_test_kit/fhir_resource_navigation.rb +154 -0
  9. data/lib/davinci_pdex_test_kit/group_metadata.rb +109 -0
  10. data/lib/davinci_pdex_test_kit/metadata/mock_capability_statement.json +1052 -0
  11. data/lib/davinci_pdex_test_kit/metadata/mock_operation_outcome_resource.json +16 -0
  12. data/lib/davinci_pdex_test_kit/mock_server.rb +247 -0
  13. data/lib/davinci_pdex_test_kit/must_support_test.rb +252 -0
  14. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_member_match_tests/client_member_match_submit_test.rb +24 -0
  15. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_member_match_tests/client_member_match_validation_test.rb +23 -0
  16. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_must_support_tests/client_member_match_must_support_submit_test.rb +26 -0
  17. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_must_support_tests/client_member_match_must_support_validation_test.rb +32 -0
  18. data/lib/davinci_pdex_test_kit/pdex_payer_client/client_validation_test.rb +94 -0
  19. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/allergyintolerance_clinical_data_request_test.rb +23 -0
  20. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/careplan_clinical_data_request_test.rb +23 -0
  21. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/careteam_clinical_data_request_test.rb +23 -0
  22. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/condition_clinical_data_request_test.rb +23 -0
  23. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/device_clinical_data_request_test.rb +23 -0
  24. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/diagnosticreport_clinical_data_request_test.rb +23 -0
  25. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/documentreference_clinical_data_request_test.rb +23 -0
  26. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/encounter_clinical_data_request_test.rb +23 -0
  27. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/explanationofbenefit_clinical_data_request_test.rb +23 -0
  28. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/goal_clinical_data_request_test.rb +23 -0
  29. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/immunization_clinical_data_request_test.rb +23 -0
  30. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_scratch_storing.rb +34 -0
  31. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/initial_wait_test.rb +28 -0
  32. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/location_clinical_data_request_test.rb +23 -0
  33. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/medicationdispense_clinical_data_request_test.rb +23 -0
  34. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/medicationrequest_clinical_data_request_test.rb +23 -0
  35. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/observation_clinical_data_request_test.rb +23 -0
  36. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/organization_clinical_data_request_test.rb +23 -0
  37. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/patient_clinical_data_request_test.rb +23 -0
  38. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/practitioner_clinical_data_request_test.rb +23 -0
  39. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/practitionerrole_clinical_data_request_test.rb +23 -0
  40. data/lib/davinci_pdex_test_kit/pdex_payer_client/clinical_data_request_tests/procedure_clinical_data_request_test.rb +23 -0
  41. data/lib/davinci_pdex_test_kit/pdex_payer_client/collection.rb +46 -0
  42. data/lib/davinci_pdex_test_kit/pdex_payer_client_suite.rb +152 -0
  43. data/lib/davinci_pdex_test_kit/pdex_payer_server/abstract_member_match_request_conformance_test.rb +33 -0
  44. data/lib/davinci_pdex_test_kit/pdex_payer_server/abstract_member_match_request_local_references_test.rb +35 -0
  45. data/lib/davinci_pdex_test_kit/pdex_payer_server/coverage_to_link_has_minimal_data_test.rb +52 -0
  46. data/lib/davinci_pdex_test_kit/pdex_payer_server/coverage_to_link_must_support_test.rb +28 -0
  47. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_id_search_test.rb +58 -0
  48. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_identifier_search_test.rb +58 -0
  49. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_must_support_test.rb +40 -0
  50. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_last_updated_search_test.rb +63 -0
  51. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_service_date_search_test.rb +63 -0
  52. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_type_search_test.rb +63 -0
  53. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_patient_use_search_test.rb +68 -0
  54. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_provenance_revinclude_search_test.rb +52 -0
  55. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_read_test.rb +26 -0
  56. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_reference_resolution_test.rb +43 -0
  57. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit/explanation_of_benefit_validation_test.rb +40 -0
  58. data/lib/davinci_pdex_test_kit/pdex_payer_server/explanation_of_benefit_group.rb +105 -0
  59. data/lib/davinci_pdex_test_kit/pdex_payer_server/export_patient_group.rb +103 -0
  60. data/lib/davinci_pdex_test_kit/pdex_payer_server/export_validation_group.rb +59 -0
  61. data/lib/davinci_pdex_test_kit/pdex_payer_server/multiple_member_matches_group.rb +66 -0
  62. data/lib/davinci_pdex_test_kit/pdex_payer_server/no_member_matches_group.rb +69 -0
  63. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_clinical_data.rb +66 -0
  64. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_everything.rb +184 -0
  65. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_export.rb +67 -0
  66. data/lib/davinci_pdex_test_kit/pdex_payer_server/workflow_member_match.rb +171 -0
  67. data/lib/davinci_pdex_test_kit/pdex_payer_server_suite.rb +158 -0
  68. data/lib/davinci_pdex_test_kit/pdex_provider_client_suite.rb +36 -0
  69. data/lib/davinci_pdex_test_kit/tags.rb +9 -0
  70. data/lib/davinci_pdex_test_kit/urls.rb +67 -0
  71. data/lib/davinci_pdex_test_kit/user_input_response.rb +32 -0
  72. data/lib/davinci_pdex_test_kit/version.rb +5 -0
  73. data/lib/davinci_pdex_test_kit.rb +8 -0
  74. metadata +218 -0
@@ -0,0 +1,154 @@
1
+ module DaVinciPDexTestKit
2
+ module FHIRResourceNavigation
3
+ DAR_EXTENSION_URL = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason'.freeze
4
+ PRIMITIVE_DATA_TYPES = FHIR::PRIMITIVES.keys
5
+
6
+ def resolve_path(elements, path)
7
+ elements = Array.wrap(elements)
8
+ return elements if path.blank?
9
+
10
+ paths = path.split(/(?<!hl7)\./)
11
+ segment = paths.first
12
+ remaining_path = paths.drop(1).join('.')
13
+
14
+ elements.flat_map do |element|
15
+ child = get_next_value(element, segment)
16
+ resolve_path(child, remaining_path)
17
+ end.compact
18
+ end
19
+
20
+ def find_a_value_at(element, path, include_dar: false)
21
+ return nil if element.nil?
22
+
23
+ elements = Array.wrap(element)
24
+ if path.empty?
25
+ unless include_dar
26
+ elements = elements.reject do |el|
27
+ el.respond_to?(:extension) && el.extension.any? { |ext| ext.url == DAR_EXTENSION_URL}
28
+ end
29
+ end
30
+
31
+ return elements.find { |el| yield(el) } if block_given?
32
+ return elements.first
33
+ end
34
+
35
+ path_segments = path.split(/(?<!hl7)\./)
36
+
37
+ segment = path_segments.shift.delete_suffix('[x]').gsub(/^class$/, 'local_class').gsub(/\[x\]:/, ':').to_sym
38
+ no_elements_present =
39
+ elements.none? do |element|
40
+ child = get_next_value(element, segment)
41
+ child.present? || child == false
42
+ end
43
+ return nil if no_elements_present
44
+
45
+ remaining_path = path_segments.join('.')
46
+ elements.each do |element|
47
+ child = get_next_value(element, segment)
48
+ element_found =
49
+ if block_given?
50
+ find_a_value_at(child, remaining_path, include_dar: include_dar) { |value_found| yield(value_found) }
51
+ else
52
+ find_a_value_at(child, remaining_path, include_dar: include_dar)
53
+ end
54
+ return element_found if element_found.present? || element_found == false
55
+ end
56
+ nil
57
+ end
58
+
59
+ def get_next_value(element, property)
60
+ extension_url = property[/(?<=where\(url=').*(?='\))/]
61
+ if extension_url.present?
62
+ element.url == extension_url ? element : nil
63
+ elsif property.to_s.include?(':') && !property.to_s.include?('url')
64
+ slice = find_slice_via_discriminator(element, property)
65
+ slice
66
+ else
67
+ result = element.send(property)
68
+ result = find_primitive_extension(element, property) if result.nil?
69
+ result
70
+ end
71
+ rescue NoMethodError
72
+ nil
73
+ end
74
+
75
+ def find_primitive_extension(element, property)
76
+ type = element.class::METADATA[property.to_s]['type']
77
+ source_value = element.source_hash["_#{property}"]
78
+
79
+ PRIMITIVE_DATA_TYPES.include?(type) && source_value.present? ? FHIR::Element.new(source_value) : nil
80
+ end
81
+
82
+ def find_slice_via_discriminator(element, property)
83
+ element_name = property.to_s.split(':')[0].gsub(/^class$/, 'local_class')
84
+ slice_name = property.to_s.split(':')[1].gsub(/^class$/, 'local_class')
85
+ if metadata.present?
86
+ slice_by_name = metadata.must_supports[:slices].find{ |slice| slice[:slice_name] == slice_name }
87
+ discriminator = slice_by_name[:discriminator]
88
+ slices = Array.wrap(element.send(element_name))
89
+ slices.find do |slice|
90
+ case discriminator[:type]
91
+ when 'patternCodeableConcept'
92
+ slice_value = discriminator[:path].present? ? slice.send("#{discriminator[:path]}")&.coding : slice.coding
93
+ slice_value&.any? { |coding| coding.code == discriminator[:code] && coding.system == discriminator[:system] }
94
+ when 'patternCoding'
95
+ slice_value = discriminator[:path].present? ? slice.send(discriminator[:path]) : slice
96
+ slice_value&.code == discriminator[:code] && slice_value&.system == discriminator[:system]
97
+ when 'patternIdentifier'
98
+ slice.identifier.system == discriminator[:system]
99
+ when 'value'
100
+ values = discriminator[:values].map { |value| value.merge(path: value[:path].split('.')) }
101
+ verify_slice_by_values(slice, values)
102
+ when 'type'
103
+ case discriminator[:code]
104
+ when 'Date'
105
+ begin
106
+ Date.parse(slice)
107
+ rescue ArgumentError
108
+ false
109
+ end
110
+ when 'DateTime'
111
+ begin
112
+ DateTime.parse(slice)
113
+ rescue ArgumentError
114
+ false
115
+ end
116
+ when 'String'
117
+ slice.is_a? String
118
+ else
119
+ slice.is_a? FHIR.const_get(discriminator[:code])
120
+ end
121
+ when 'requiredBinding'
122
+ slice_value = discriminator[:path].present? ? slice.send("#{discriminator[:path]}").coding : slice.coding
123
+ slice_value {|coding| discriminator[:values].include?(coding.code) }
124
+ end
125
+ end
126
+ else
127
+ #TODO: Error handling for if this file doesn't have access to metadata for some reason (begin/rescue with StandardError?)
128
+ end
129
+ end
130
+
131
+ def verify_slice_by_values(element, value_definitions)
132
+ path_prefixes = value_definitions.map { |value_definition| value_definition[:path].first }.uniq
133
+ path_prefixes.all? do |path_prefix|
134
+ value_definitions_for_path =
135
+ value_definitions
136
+ .select { |value_definition| value_definition[:path].first == path_prefix }
137
+ .each { |value_definition| value_definition[:path].shift }
138
+ find_a_value_at(element, path_prefix) do |el_found|
139
+ child_element_value_definitions, current_element_value_definitions =
140
+ value_definitions_for_path.partition { |value_definition| value_definition[:path].present? }
141
+
142
+ current_element_values_match =
143
+ current_element_value_definitions
144
+ .all? { |value_definition| value_definition[:value] == el_found }
145
+
146
+ child_element_values_match =
147
+ child_element_value_definitions.present? ?
148
+ verify_slice_by_values(el_found, child_element_value_definitions) : true
149
+ current_element_values_match && child_element_values_match
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,109 @@
1
+ module DaVinciPDexTestKit
2
+ class GroupMetadata
3
+ ATTRIBUTES = [
4
+ :name,
5
+ :class_name,
6
+ :version,
7
+ :reformatted_version,
8
+ :resource,
9
+ :profile_url,
10
+ :profile_name,
11
+ :profile_version,
12
+ :title,
13
+ :short_description,
14
+ :is_delayed,
15
+ :interactions,
16
+ :operations,
17
+ :searches,
18
+ :search_definitions,
19
+ :include_params,
20
+ :revincludes,
21
+ :required_concepts,
22
+ :must_supports,
23
+ :mandatory_elements,
24
+ :bindings,
25
+ :references,
26
+ :tests,
27
+ :granular_scope_tests,
28
+ :id,
29
+ :file_name,
30
+ :delayed_references
31
+ ].freeze
32
+
33
+ ATTRIBUTES.each { |name| attr_accessor name }
34
+
35
+ def initialize(metadata)
36
+ metadata.each do |key, value|
37
+ raise "Unknown attribute #{key}" unless ATTRIBUTES.include? key
38
+
39
+ instance_variable_set(:"@#{key}", value)
40
+ end
41
+ end
42
+
43
+ def delayed?
44
+ @is_delayed ||= if resource == 'Patient'
45
+ false
46
+ else
47
+ no_patient_searches? || non_uscdi_resource?
48
+ end
49
+ end
50
+
51
+ def exclude_search_tests?
52
+ delayed? && !searchable_delayed_resource?
53
+ end
54
+
55
+ def no_patient_searches?
56
+ searches.none? { |search| search[:names].include? 'patient' }
57
+ end
58
+
59
+ def non_uscdi_resource?
60
+ SpecialCases::NON_USCDI_RESOURCES.key?(resource) && SpecialCases::NON_USCDI_RESOURCES[resource].include?(reformatted_version)
61
+ end
62
+
63
+ def searchable_delayed_resource?
64
+ SpecialCases::SEARCHABLE_DELAYED_RESOURCES.key?(resource) && SpecialCases::SEARCHABLE_DELAYED_RESOURCES[resource].include?(reformatted_version)
65
+ end
66
+
67
+ def add_test(id:, file_name:)
68
+ self.tests ||= []
69
+
70
+ test_metadata = {
71
+ id: id,
72
+ file_name: file_name
73
+ }
74
+
75
+ if delayed? && id.include?('read')
76
+ self.tests.unshift(test_metadata)
77
+ else
78
+ self.tests << test_metadata
79
+ end
80
+ end
81
+
82
+ def add_granular_scope_test(id:, file_name:)
83
+ self.granular_scope_tests ||= []
84
+
85
+ self.granular_scope_tests << {
86
+ id:,
87
+ file_name:
88
+ }
89
+ end
90
+
91
+ def to_hash
92
+ ATTRIBUTES.each_with_object({}) { |key, hash| hash[key] = send(key) unless send(key).nil? }
93
+ end
94
+
95
+ def add_delayed_references(delayed_profiles, ig_resources)
96
+ self.delayed_references =
97
+ references
98
+ .select { |reference| (reference[:profiles] & delayed_profiles).present? }
99
+ .map do |reference|
100
+ profile_urls = (reference[:profiles] & delayed_profiles)
101
+ delayed_resources = profile_urls.map { |url| ig_resources.resource_for_profile(url) }
102
+ {
103
+ path: reference[:path].gsub("#{resource}.", ''),
104
+ resources: delayed_resources
105
+ }
106
+ end
107
+ end
108
+ end
109
+ end