Module: USCoreTestKit::ResourceSearchParamChecker

Includes:
Inferno::DSL::FHIRResourceNavigation
Included in:
GranularScopeReadTest, GranularScopeSearchTest, SearchTest
Defined in:
lib/us_core_test_kit/resource_search_param_checker.rb

Instance Method Summary collapse

Instance Method Details

#element_has_valid_value?(element, include_system) ⇒ Boolean



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/us_core_test_kit/resource_search_param_checker.rb', line 16

def element_has_valid_value?(element, include_system)
  case element
  when FHIR::Reference
    element.reference.present?
  when FHIR::CodeableConcept
    if include_system
      coding =
        find_a_value_at(element, 'coding') { |coding| coding.code.present? && coding.system.present? }
      coding.present?
    else
      find_a_value_at(element, 'coding.code').present?
    end
  when FHIR::Identifier
    include_system ? element.value.present? && element.system.present? : element.value.present?
  when FHIR::Coding
    include_system ? element.code.present? && element.system.present? : element.code.present?
  when FHIR::HumanName
    (element.family || element.given&.first || element.text).present?
  when FHIR::Address
    (element.text || element.city || element.state || element.postalCode || element.country).present?
  when FHIR::Element
    false
  else
    true
  end
end

#resource_matches_param?(resource, search_param_name, escaped_search_value, values_found = []) ⇒ Boolean



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/us_core_test_kit/resource_search_param_checker.rb', line 43

def resource_matches_param?(resource, search_param_name, escaped_search_value, values_found = [])
  search_value = unescape_search_value(escaped_search_value)
  paths = search_param_paths(search_param_name)

  match_found = false

  paths.each do |path|
    type = .search_definitions[search_param_name.to_sym][:type]
    values_found =
      resolve_path(resource, path)
        .map do |value|
      if value.is_a? FHIR::Reference
        value.reference
      else
        value
      end
    end

    match_found =
      case type
      when 'Period', 'date', 'instant', 'dateTime'
        values_found.any? { |date| validate_date_search(search_value, date) }
      when 'HumanName'
        # When a string search parameter refers to the types HumanName and Address,
        # the search covers the elements of type string, and does not cover elements such as use and period
        # https://www.hl7.org/fhir/search.html#string
        search_value_downcase = search_value.downcase
        values_found.any? do |name|
          name&.text&.downcase&.start_with?(search_value_downcase) ||
            name&.family&.downcase&.start_with?(search_value_downcase) ||
            name&.given&.any? { |given| given.downcase.start_with?(search_value_downcase) } ||
            name&.prefix&.any? { |prefix| prefix.downcase.start_with?(search_value_downcase) } ||
            name&.suffix&.any? { |suffix| suffix.downcase.start_with?(search_value_downcase) }
        end
      when 'Address'
        search_value_downcase = search_value.downcase
        values_found.any? do |address|
          address&.text&.downcase&.start_with?(search_value_downcase) ||
            address&.city&.downcase&.start_with?(search_value_downcase) ||
            address&.state&.downcase&.start_with?(search_value_downcase) ||
            address&.postalCode&.downcase&.start_with?(search_value_downcase) ||
            address&.country&.downcase&.start_with?(search_value_downcase)
        end
      when 'CodeableConcept'
        # FHIR token search (https://www.hl7.org/fhir/search.html#token): "When in doubt, servers SHOULD
        # treat tokens in a case-insensitive manner, on the grounds that including undesired data has
        # less safety implications than excluding desired behavior".
        codings = values_found.flat_map(&:coding)
        if search_value.include? '|'
          system = search_value.split('|').first
          code = search_value.split('|').last
          codings&.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
        else
          codings&.any? { |coding| coding.code&.casecmp?(search_value) }
        end
      when 'Coding'
        if search_value.include? '|'
          system = search_value.split('|').first
          code = search_value.split('|').last
          values_found.any? { |coding| coding.system == system && coding.code&.casecmp?(code) }
        else
          values_found.any? { |coding| coding.code&.casecmp?(search_value) }
        end
      when 'Identifier'
        if search_value.include? '|'
          values_found.any? { |identifier| "#{identifier.system}|#{identifier.value}" == search_value }
        else
          values_found.any? { |identifier| identifier.value == search_value }
        end
      when 'string'
        searched_values = search_value.downcase.split(/(?<!\\\\),/).map{ |string| string.gsub('\\,', ',') }
        values_found.any? do |value_found|
          searched_values.any? { |searched_value| value_found.downcase.starts_with? searched_value }
        end
      else
        # searching by patient requires special case because we are searching by a resource identifier
        # references can also be URLs, so we may need to resolve those URLs
        if ['subject', 'patient'].include? search_param_name.to_s
          id = search_value.split('Patient/').last
          possible_values = [id, "Patient/#{id}", "#{url}/Patient/#{id}"]
          values_found.any? do |reference|
            possible_values.include? reference
          end
        else
          search_values = search_value.split(/(?<!\\\\),/).map { |string| string.gsub('\\,', ',') }
          values_found.any? { |value_found| search_values.include? value_found }
        end
      end

    break if match_found
  end

  match_found
end

#search_param_paths(name) ⇒ Object



7
8
9
10
11
12
13
14
# File 'lib/us_core_test_kit/resource_search_param_checker.rb', line 7

def search_param_paths(name)
  paths = .search_definitions[name.to_sym][:paths]
  if paths.first =='class'
    paths[0] = 'local_class'
  end

  paths
end