Module: DaVinciCRDTestKit::CardsValidation

Constant Summary collapse

HOOKS =
[
  'appointment-book', 'encounter-discharge', 'encounter-start',
  'order-dispatch', 'order-select', 'order-sign'
].freeze

Instance Method Summary collapse

Methods included from SuggestionActionsValidation

#action_fields_validation, #action_required_fields, #action_resource_field_validation, #action_resource_id_field_validation, #action_resource_type_check, #action_type_field_validation, #actions_check, #create_or_update_action_check, #delete_action_check, #draft_orders_bundle_entry_refs, #extract_resource_types_by_action

Methods included from ServerHookRequestValidation

#client_test?, #server_test?

Methods included from HookRequestFieldValidation

#appointment_book_context_check, #bundle_entries_check, #check_patient_scope_requirement, #common_context_fields, #context_optional_fields_by_hook, #context_required_fields_by_hook, #context_selections_check, #context_user_types_by_hook, #context_validate_optional_fields, #encounter_start_or_discharge_context_check, #fhir_auth_fields_valid?, #fhir_authorization_required_fields, #hook_optional_fields, #hook_request_context_check, #hook_request_fhir_auth_check, #hook_request_optional_fields_check, #hook_request_prefetch_check, #hook_request_required_fields_check, #hook_required_fields, #hook_specific_context_check, #hook_user_type_check, #id_only_fields_check, #json_parse, #no_error_validation, #optional_field_resource_types, #order_dispatch_context_check, #order_select_or_sign_context_check, #parse_fhir_bundle_from_context, #query_and_validate_id_field, #request_number, #resource_reference_check, #status_check, #structure_definition_map, #valid_id_format?, #valid_reference_format?, #valid_url?, #validate_hash_fields, #validate_prefetch_coverage, #validate_prefetch_resource, #validate_presence_and_type

Instance Method Details

#all_requestsObject



213
214
215
216
217
218
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 213

def all_requests
  @all_requests ||= HOOKS.each_with_object([]) do |hook, reqs|
    load_tagged_requests(hook)
    reqs.concat(requests)
  end
end

#card_indicator_check(card) ⇒ Object



172
173
174
175
176
177
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 172

def card_indicator_check(card)
  return if !card['indicator'].is_a?(String) || ['info', 'warning', 'critical'].include?(card['indicator'])

  msg = "`indicator` is `#{card['indicator']}`. Allowed values are `info`, `warning`, `critical`: `#{card}`"
  add_message('error', msg)
end


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 99

def card_link_type_check(card, link)
  return unless link['type']

  unless ['absolute', 'smart'].include?(link['type'])
    add_message('error',
                "`Link.type` must be `absolute` or `smart`. Got `#{link['type']}`: `#{link}`. In Card `#{card}`")
    return
  end

  return unless link['type'] == 'absolute' && link['appContext'].present?

  msg = '`appContext` field should only be valued if the link type is smart and is not valid for absolute links: ' \
        "`#{link}`. In Card `#{card}`"
  add_message('error', msg)
end


87
88
89
90
91
92
93
94
95
96
97
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 87

def card_links_check(card)
  return unless card['links'].is_a?(Array) && card['links'].present?

  card['links'].each do |link|
    link_required_fields.each do |field, type|
      validate_presence_and_type(link, field, type, 'Link')
    end

    card_link_type_check(card, link)
  end
end

#card_optional_fieldsObject



26
27
28
29
30
31
32
33
34
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 26

def card_optional_fields
  {
    'uuid' => String,
    'detail' => String,
    'suggestions' => Array,
    'overrideReasons' => Array,
    'links' => Array
  }
end

#card_override_reasons_check(card) ⇒ Object



77
78
79
80
81
82
83
84
85
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 77

def card_override_reasons_check(card)
  return unless card['overrideReasons'].is_a?(Array)

  card['overrideReasons'].each do |reason|
    override_reasons_required_fields.each do |field, type|
      validate_presence_and_type(reason, field, type, 'OverrideReason Coding')
    end
  end
end

#card_required_fieldsObject



14
15
16
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 14

def card_required_fields
  { 'summary' => String, 'indicator' => String, 'source' => Hash }
end

#card_selection_behavior_check(card) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 60

def card_selection_behavior_check(card)
  return unless card['suggestions'].present?

  selection_behavior = card['selectionBehavior']
  unless selection_behavior
    add_message('error', "`Card.selectionBehavior` must be provided if suggestions are present. In Card `#{card}`")
    return
  end

  allowed_values = ['at-most-one', 'any']
  return if allowed_values.include?(selection_behavior)

  error_msg = "`selectionBehavior` #{selection_behavior} not allowed. " \
              "Allowed values: #{allowed_values.to_sentence}. In Card `#{card}`"
  add_message('error', error_msg)
end

#card_source_check(card) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 146

def card_source_check(card)
  source = card['source']
  return unless source.is_a?(Hash)

  source_required_fields.each do |field, type|
    validate_presence_and_type(source, field, type, 'Source')
  end

  card_source_topic_check(source['topic'])
  # TODO: How to validate topic binding to the ValueSet CRD Card Types?
end

#card_source_topic_check(topic) ⇒ Object



158
159
160
161
162
163
164
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 158

def card_source_topic_check(topic)
  return unless topic.is_a?(Hash)

  source_topic_required_fields.each do |field, type|
    validate_presence_and_type(topic, field, type, 'Source topic')
  end
end

#card_suggestions_check(card) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 115

def card_suggestions_check(card)
  return unless card['suggestions'].is_a?(Array) && card['suggestions'].present?

  card['suggestions'].each do |suggestion|
    process_suggestion(card, suggestion)
  end
end

#card_summary_check(card) ⇒ Object



166
167
168
169
170
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 166

def card_summary_check(card)
  return if !card['summary'].is_a?(String) || card['summary'].length < 140

  add_message('error', "`summary` is over the 140-character limit: `#{card}`")
end

#cards_check(cards) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 179

def cards_check(cards)
  cards.each do |card|
    current_error_count = messages.count { |msg| msg[:type] == 'error' }
    card_required_fields.each do |field, type|
      validate_presence_and_type(card, field, type, 'Card')
    end

    card_summary_check(card)
    card_indicator_check(card)
    card_source_check(card)

    valid_cards << card if current_error_count == messages.count { |msg| msg[:type] == 'error' }
  end
end

#extract_all_valid_cards_from_hooks_responsesObject



220
221
222
223
224
225
226
227
228
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 220

def extract_all_valid_cards_from_hooks_responses
  all_requests.keep_if { |request| request.status == 200 }
  all_requests.each_with_index do |request, index|
    service_response = JSON.parse(request.response_body)
    perform_cards_validation(service_response['cards'], index)
  rescue JSON::ParserError
    add_message('error', "Invalid JSON: #{response_label(response_index + 1).downcase} is not a valid JSON.")
  end
end


230
231
232
233
234
235
236
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 230

def extract_valid_cards_with_links_from_hooks_responses
  extract_all_valid_cards_from_hooks_responses

  valid_cards.each do |card|
    valid_cards_with_links << card if valid_card_with_optionals?(card) && (card['links'])
  end
end


40
41
42
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 40

def link_required_fields
  { 'label' => String, 'type' => String, 'url' => 'URL' }
end

#override_reasons_required_fieldsObject



36
37
38
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 36

def override_reasons_required_fields
  { 'code' => String, 'system' => String, 'display' => String }
end

#perform_cards_validation(cards, response_index = 0) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 198

def perform_cards_validation(cards, response_index = 0)
  unless cards
    add_message('error', "#{response_label(response_index + 1)} did not have the `cards` field.")
    return
  end
  unless cards.is_a?(Array)
    add_message('error', "`cards` field of #{response_label(response_index + 1).downcase} is not an array.")
    return
  end
  warning do
    assert cards.present?, "#{response_label(response_index + 1)} has no decision support."
  end
  cards_check(cards)
end

#process_suggestion(card, suggestion) ⇒ Object



123
124
125
126
127
128
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 123

def process_suggestion(card, suggestion)
  validate_presence_and_type(suggestion, 'label', String, 'Suggestion')
  return unless suggestion['actions']

  validate_and_process_actions(card, suggestion)
end

#response_label(index = nil) ⇒ Object



194
195
196
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 194

def response_label(index = nil)
  "Server response #{index}"
end

#source_required_fieldsObject



18
19
20
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 18

def source_required_fields
  { 'label' => String, 'topic' => Hash }
end

#source_topic_required_fieldsObject



22
23
24
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 22

def source_topic_required_fields
  { 'code' => String, 'system' => String }
end

#valid_card_with_optionals?(card) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 44

def valid_card_with_optionals?(card)
  current_error_count = messages.count { |msg| msg[:type] == 'error' }
  card_optional_fields.each do |field, type|
    next unless card[field]

    validate_presence_and_type(card, field, type, 'Card')
  end

  card_selection_behavior_check(card)
  card_override_reasons_check(card)
  card_links_check(card)
  card_suggestions_check(card)

  current_error_count == messages.count { |msg| msg[:type] == 'error' }
end

#validate_and_process_actions(card, suggestion) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/davinci_crd_test_kit/cards_validation.rb', line 130

def validate_and_process_actions(card, suggestion)
  actions = suggestion['actions']
  if !actions.is_a?(Array)
    add_message('error', "Suggestion `actions` field is not of type Array: `#{suggestion}`. In Card `#{card}`")
    return
  elsif actions.empty?
    add_message('error',
                "Suggestion `actions` field should not be an empty Array: `#{suggestion}`. In Card `#{card}`")
    return
  end

  actions.each do |action|
    action_fields_validation(action)
  end
end