pacio_inferno_core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/conformance_support_test.rb +41 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/fhir_version_test.rb +15 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/instantiate_test.rb +23 -0
- data/lib/pacio_inferno_core/custom_groups/capability_statement/json_support_test.rb +40 -0
- data/lib/pacio_inferno_core/date_search_validation.rb +112 -0
- data/lib/pacio_inferno_core/fhir_resource_navigation.rb +7 -0
- data/lib/pacio_inferno_core/generator/group_generator.rb +161 -0
- data/lib/pacio_inferno_core/generator/group_metadata.rb +114 -0
- data/lib/pacio_inferno_core/generator/group_metadata_extractor.rb +301 -0
- data/lib/pacio_inferno_core/generator/ig_loader.rb +118 -0
- data/lib/pacio_inferno_core/generator/ig_metadata.rb +60 -0
- data/lib/pacio_inferno_core/generator/ig_metadata_extractor.rb +88 -0
- data/lib/pacio_inferno_core/generator/ig_resources.rb +88 -0
- data/lib/pacio_inferno_core/generator/must_support_metadata_extractor.rb +8 -0
- data/lib/pacio_inferno_core/generator/must_support_test_generator.rb +138 -0
- data/lib/pacio_inferno_core/generator/naming.rb +50 -0
- data/lib/pacio_inferno_core/generator/read_test_generator.rb +102 -0
- data/lib/pacio_inferno_core/generator/reference_resolution_test_generator.rb +96 -0
- data/lib/pacio_inferno_core/generator/search_definition_metadata_extractor.rb +228 -0
- data/lib/pacio_inferno_core/generator/search_metadata_extractor.rb +78 -0
- data/lib/pacio_inferno_core/generator/search_test_generator.rb +298 -0
- data/lib/pacio_inferno_core/generator/special_cases.rb +61 -0
- data/lib/pacio_inferno_core/generator/suite_generator.rb +105 -0
- data/lib/pacio_inferno_core/generator/templates/group.rb.erb +27 -0
- data/lib/pacio_inferno_core/generator/templates/must_support.rb.erb +36 -0
- data/lib/pacio_inferno_core/generator/templates/read.rb.erb +34 -0
- data/lib/pacio_inferno_core/generator/templates/reference_resolution.rb.erb +40 -0
- data/lib/pacio_inferno_core/generator/templates/search.rb.erb +40 -0
- data/lib/pacio_inferno_core/generator/templates/validation.rb.erb +36 -0
- data/lib/pacio_inferno_core/generator/terminology_binding_metadata_extractor.rb +116 -0
- data/lib/pacio_inferno_core/generator/validation_test_generator.rb +146 -0
- data/lib/pacio_inferno_core/generator/value_extractor.rb +152 -0
- data/lib/pacio_inferno_core/generator.rb +130 -0
- data/lib/pacio_inferno_core/must_support_test.rb +20 -0
- data/lib/pacio_inferno_core/primitive_type.rb +5 -0
- data/lib/pacio_inferno_core/read_test.rb +103 -0
- data/lib/pacio_inferno_core/reference_resolution_test.rb +181 -0
- data/lib/pacio_inferno_core/request_logger.rb +46 -0
- data/lib/pacio_inferno_core/resource_search_param_checker.rb +136 -0
- data/lib/pacio_inferno_core/search_test.rb +859 -0
- data/lib/pacio_inferno_core/search_test_properties.rb +56 -0
- data/lib/pacio_inferno_core/validation_test.rb +55 -0
- data/lib/pacio_inferno_core/version.rb +4 -0
- data/lib/pacio_inferno_core/well_known_code_systems.rb +21 -0
- metadata +165 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require_relative 'fhir_resource_navigation'
|
|
2
|
+
|
|
3
|
+
module PacioInfernoCore
|
|
4
|
+
module ResourceSearchParamChecker
|
|
5
|
+
include Inferno::DSL::FHIRResourceNavigation
|
|
6
|
+
|
|
7
|
+
def search_param_paths(name)
|
|
8
|
+
paths = metadata.search_definitions[name.to_sym][:paths]
|
|
9
|
+
paths[0] = 'local_class' if paths.first == 'class'
|
|
10
|
+
|
|
11
|
+
paths
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def element_has_valid_value?(element, include_system)
|
|
15
|
+
case element
|
|
16
|
+
when FHIR::Reference
|
|
17
|
+
element.reference.present?
|
|
18
|
+
when FHIR::CodeableConcept
|
|
19
|
+
if include_system
|
|
20
|
+
coding =
|
|
21
|
+
find_a_value_at(element, 'coding') { |coding| coding.code.present? && coding.system.present? }
|
|
22
|
+
coding.present?
|
|
23
|
+
else
|
|
24
|
+
find_a_value_at(element, 'coding.code').present?
|
|
25
|
+
end
|
|
26
|
+
when FHIR::Identifier
|
|
27
|
+
include_system ? element.value.present? && element.system.present? : element.value.present?
|
|
28
|
+
when FHIR::Coding
|
|
29
|
+
include_system ? element.code.present? && element.system.present? : element.code.present?
|
|
30
|
+
when FHIR::HumanName
|
|
31
|
+
(element.family || element.given&.first || element.text).present?
|
|
32
|
+
when FHIR::Address
|
|
33
|
+
(element.text || element.city || element.state || element.postalCode || element.country).present?
|
|
34
|
+
when FHIR::Element
|
|
35
|
+
false
|
|
36
|
+
else
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def resource_matches_param?(resource, search_param_name, escaped_search_value, values_found = [])
|
|
42
|
+
search_value = unescape_search_value(escaped_search_value)
|
|
43
|
+
paths = search_param_paths(search_param_name)
|
|
44
|
+
|
|
45
|
+
match_found = false
|
|
46
|
+
|
|
47
|
+
paths.each do |path|
|
|
48
|
+
type = metadata.search_definitions[search_param_name.to_sym][:type]
|
|
49
|
+
values_found =
|
|
50
|
+
resolve_path(resource, path)
|
|
51
|
+
.map do |value|
|
|
52
|
+
if value.is_a? FHIR::Reference
|
|
53
|
+
value.reference
|
|
54
|
+
else
|
|
55
|
+
value
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
match_found =
|
|
60
|
+
case type
|
|
61
|
+
when 'Period', 'date', 'instant', 'dateTime'
|
|
62
|
+
values_found.any? { |date| validate_date_search(search_value, date) }
|
|
63
|
+
when 'HumanName'
|
|
64
|
+
# When a string search parameter refers to the types HumanName and Address,
|
|
65
|
+
# the search covers the elements of type string, and does not cover elements such as use and period
|
|
66
|
+
# https://www.hl7.org/fhir/search.html#string
|
|
67
|
+
search_value_downcase = search_value.downcase
|
|
68
|
+
values_found.any? do |name|
|
|
69
|
+
name&.text&.downcase&.start_with?(search_value_downcase) ||
|
|
70
|
+
name&.family&.downcase&.start_with?(search_value_downcase) ||
|
|
71
|
+
name&.given&.any? { |given| given.downcase.start_with?(search_value_downcase) } ||
|
|
72
|
+
name&.prefix&.any? { |prefix| prefix.downcase.start_with?(search_value_downcase) } ||
|
|
73
|
+
name&.suffix&.any? { |suffix| suffix.downcase.start_with?(search_value_downcase) }
|
|
74
|
+
end
|
|
75
|
+
when 'Address'
|
|
76
|
+
search_value_downcase = search_value.downcase
|
|
77
|
+
values_found.any? do |address|
|
|
78
|
+
address&.text&.downcase&.start_with?(search_value_downcase) ||
|
|
79
|
+
address&.city&.downcase&.start_with?(search_value_downcase) ||
|
|
80
|
+
address&.state&.downcase&.start_with?(search_value_downcase) ||
|
|
81
|
+
address&.postalCode&.downcase&.start_with?(search_value_downcase) ||
|
|
82
|
+
address&.country&.downcase&.start_with?(search_value_downcase)
|
|
83
|
+
end
|
|
84
|
+
when 'CodeableConcept'
|
|
85
|
+
# FHIR token search (https://www.hl7.org/fhir/search.html#token): "When in doubt, servers SHOULD
|
|
86
|
+
# treat tokens in a case-insensitive manner, on the grounds that including undesired data has
|
|
87
|
+
# less safety implications than excluding desired behavior".
|
|
88
|
+
codings = values_found.flat_map(&:coding)
|
|
89
|
+
if search_value.include? '|'
|
|
90
|
+
system = search_value.split('|').first
|
|
91
|
+
code = search_value.split('|').last
|
|
92
|
+
codings&.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
|
|
93
|
+
else
|
|
94
|
+
codings&.any? { |coding| coding.code&.casecmp?(search_value) }
|
|
95
|
+
end
|
|
96
|
+
when 'Coding'
|
|
97
|
+
if search_value.include? '|'
|
|
98
|
+
system = search_value.split('|').first
|
|
99
|
+
code = search_value.split('|').last
|
|
100
|
+
values_found.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
|
|
101
|
+
else
|
|
102
|
+
values_found.any? { |coding| coding.code&.casecmp?(search_value) }
|
|
103
|
+
end
|
|
104
|
+
when 'Identifier'
|
|
105
|
+
if search_value.include? '|'
|
|
106
|
+
values_found.any? { |identifier| "#{identifier.system}|#{identifier.value}" == search_value }
|
|
107
|
+
else
|
|
108
|
+
values_found.any? { |identifier| identifier.value == search_value }
|
|
109
|
+
end
|
|
110
|
+
when 'string'
|
|
111
|
+
searched_values = search_value.downcase.split(/(?<!\\\\),/).map { |string| string.gsub('\\,', ',') }
|
|
112
|
+
values_found.any? do |value_found|
|
|
113
|
+
searched_values.any? { |searched_value| value_found.downcase.starts_with? searched_value }
|
|
114
|
+
end
|
|
115
|
+
else
|
|
116
|
+
# searching by patient requires special case because we are searching by a resource identifier
|
|
117
|
+
# references can also be URLs, so we may need to resolve those URLs
|
|
118
|
+
if %w[subject patient].include? search_param_name.to_s
|
|
119
|
+
id = search_value.split('Patient/').last
|
|
120
|
+
possible_values = [id, "Patient/#{id}", "#{url}/Patient/#{id}"]
|
|
121
|
+
values_found.any? do |reference|
|
|
122
|
+
possible_values.include? reference
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
search_values = search_value.split(/(?<!\\\\),/).map { |string| string.gsub('\\,', ',') }
|
|
126
|
+
values_found.any? { |value_found| search_values.include? value_found }
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
break if match_found
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
match_found
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|