Class: Inferno::DSL::MustSupportMetadataExtractor

Inherits:
Object
  • Object
show all
Defined in:
lib/inferno/dsl/must_support_metadata_extractor.rb

Overview

The MustSupportMetadataExtractor takes a StructureDefinition and parses it into a hash-based metadata that simplifies checking for MustSupport elements. MustSupport elements may be either plain elements, extensions, or slices. This logic was originally developed for the US Core Test Kit and has been migrated into Inferno core.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(profile_elements, profile, resource, ig_resources, requirement_extension_url = nil) ⇒ MustSupportMetadataExtractor

Construct a new extractor

Parameters:

  • profile_elements (Array<FHIR::ElementDefinition>)

    the elements of the profile to consider, ie, profile.snapshot.element

  • profile (FHIR::StructureDefinition)

    the profile to parse

  • resource (String)

    the resourceType that the profile applies to, ie, profile.type

  • ig_resources (Inferno::Entities::IG)
  • requirement_extension_url (String) (defaults to: nil)

    the URL of an extension to flag elements as required even if not MS



19
20
21
22
23
24
25
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 19

def initialize(profile_elements, profile, resource, ig_resources, requirement_extension_url = nil)
  self.profile_elements = profile_elements
  self.profile = profile
  self.resource = resource
  self.ig_resources = ig_resources
  self.requirement_extension_url = requirement_extension_url
end

Instance Attribute Details

#ig_resourcesObject

Returns the value of attribute ig_resources.



10
11
12
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 10

def ig_resources
  @ig_resources
end

#profileObject

Returns the value of attribute profile.



10
11
12
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 10

def profile
  @profile
end

#profile_elementsObject

Returns the value of attribute profile_elements.



10
11
12
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 10

def profile_elements
  @profile_elements
end

#requirement_extension_urlObject

Returns the value of attribute requirement_extension_url.



10
11
12
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 10

def requirement_extension_url
  @requirement_extension_url
end

#resourceObject

Returns the value of attribute resource.



10
11
12
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 10

def resource
  @resource
end

Instance Method Details

#all_must_support_elementsObject



77
78
79
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 77

def all_must_support_elements
  profile_elements.select { |element| element.mustSupport || by_requirement_extension_only?(element) }
end

#by_requirement_extension_only?(element) ⇒ Boolean

Returns:

  • (Boolean)


70
71
72
73
74
75
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 70

def by_requirement_extension_only?(element)
  requirement_extension_url && !element.mustSupport &&
    element.extension.any? do |extension|
      extension.url == requirement_extension_url && extension.valueBoolean
    end
end

#canonical_url_without_version(url) ⇒ Object



108
109
110
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 108

def canonical_url_without_version(url)
  url&.split('|')&.first
end

#discriminator_path(discriminator) ⇒ Object



291
292
293
294
295
296
297
298
299
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 291

def discriminator_path(discriminator)
  if discriminator.path == '$this'
    ''
  elsif discriminator.path.start_with?('$this.')
    discriminator.path[6..]
  else
    discriminator.path
  end
end

#discriminators(slice) ⇒ Object



126
127
128
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 126

def discriminators(slice)
  slice&.slicing&.discriminator
end

#element_matches_choice_type?(element, target_path, target_type) ⇒ Boolean

Returns:

  • (Boolean)


221
222
223
224
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 221

def element_matches_choice_type?(element, target_path, target_type)
  [element.id, element.path].include?(target_path) &&
    element.type.any? { |type| type.code.casecmp?(target_type) }
end

#element_part_of_slice_discrimination?(element) ⇒ Boolean

Returns:

  • (Boolean)


396
397
398
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 396

def element_part_of_slice_discrimination?(element)
  must_support_slice_elements.any? { |ms_slice| element.id.include?(ms_slice.id) }
end

#extension_discriminator_step?(path) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 154

def extension_discriminator_step?(path)
  path.start_with?('extension(') || path.start_with?('modifierExtension(')
end

#extension_profile_matches?(type, ext_url) ⇒ Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 170

def extension_profile_matches?(type, ext_url)
  type.code == 'Extension' && type.profile.any? { |profile| profile.start_with?(ext_url) }
end

#extension_slice_name(element_id) ⇒ String?

The name of the deepest slice in a FHIR ElementDefinition id, eg "us-core-race" for "Patient.extension:us-core-race" or "ombCategory" for "Patient.extension:us-core-race.extension:ombCategory".

Parameters:

  • element_id (String)

Returns:

  • (String, nil)


104
105
106
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 104

def extension_slice_name(element_id)
  element_id.split('.').reverse.find { |segment| segment.include?(':') }&.split(':', 2)&.last
end

#extract_required_binding_values(pattern_element, metadata) ⇒ Object



268
269
270
271
272
273
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 268

def extract_required_binding_values(pattern_element, )
  value_extractor = ValueExtractor.new(ig_resources, resource, profile_elements)

  value_extractor.codings_from_value_set_binding(pattern_element).presence ||
    value_extractor.([[:path]]).presence || []
end

#extract_supported_types(current_element) ⇒ Object



516
517
518
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 516

def extract_supported_types(current_element)
  current_element.type.select { |type| save_type_code?(type) }.map(&:code)
end

#extract_target_profiles(type) ⇒ Object



453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 453

def extract_target_profiles(type)
  target_profiles = []

  if type.targetProfile&.length == 1
    target_profiles << type.targetProfile.first
  else
    type.source_hash['_targetProfile']&.each_with_index do |hash, index|
      if hash.present?
        element = FHIR::Element.new(hash)
        target_profiles << type.targetProfile[index] if type_must_support_extension?(element.extension)
      end
    end
  end

  target_profiles
end

#find_choice_element(current_element, target_element, target_type) ⇒ Object



213
214
215
216
217
218
219
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 213

def find_choice_element(current_element, target_element, target_type)
  [current_element.id, current_element.path].filter_map do |base_path|
    profile_elements.find do |element|
      element_matches_choice_type?(element, "#{base_path}.#{target_element}", target_type)
    end
  end.first
end

#find_element_by_discriminator_path(current_element, discriminator_path) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 136

def find_element_by_discriminator_path(current_element, discriminator_path)
  target_element = current_element
  remaining_path = discriminator_path

  while remaining_path.present?
    target_element, remaining_path = take_discriminator_step(target_element, remaining_path)
    return nil if target_element.nil?
  end

  target_element
end

#get_type_must_support_metadata(current_metadata, current_element) ⇒ Object



429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 429

def (, current_element)
  current_element.type.map do |type|
    next unless type_must_support_extension?(type.extension)

     =
      {
        path: "#{[:path].delete_suffix('[x]')}#{type.code.upcase_first}",
        original_path: [:path]
      }
    [:types] = [type.code] if save_type_code?(type)
    handle_type_must_support_target_profiles(type, ) if type.code == 'Reference'

    
  end.compact
end

#handle_choice_type_in_sliced_element(current_metadata, must_support_elements_metadata) ⇒ Object



470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 470

def handle_choice_type_in_sliced_element(, )
   = .find do ||
    [:original_path].present? &&
      [:path].include?([:original_path])
  end

  return unless .present?

  [:original_path] = [:path]
  [:path] =
    [:path].sub([:original_path], [:path])
end

#handle_fixed_values(metadata, element) ⇒ Object



404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 404

def handle_fixed_values(, element)
  if value_not_empty?(element.fixed)
    [:fixed_value] = element.fixed
  elsif element.patternCodeableConcept.present? && !element_part_of_slice_discrimination?(element)
    [:fixed_value] = element.patternCodeableConcept.coding.first.code
    [:path] += '.coding.code'
  elsif element.fixedCode.present?
    [:fixed_value] = element.fixedCode
  elsif element.patternIdentifier.present? && !element_part_of_slice_discrimination?(element)
    [:fixed_value] = element.patternIdentifier.system
    [:path] += '.system'
  end
end

#handle_type_must_support_target_profiles(type, metadata) ⇒ Object



445
446
447
448
449
450
451
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 445

def handle_type_must_support_target_profiles(type, )
  target_profiles = extract_target_profiles(type)

  # remove target_profile for FHIR Base resource type.
  target_profiles.delete_if { |reference| reference.start_with?('http://hl7.org/fhir/StructureDefinition') }
  [:target_profiles] = target_profiles if target_profiles.present?
end

#legacy_choice_discriminator_step?(path) ⇒ Boolean

Returns:

  • (Boolean)


190
191
192
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 190

def legacy_choice_discriminator_step?(path)
  path.match?(/\A.+\s+as\s+[[:word:]]+\z/)
end

#must_support_elementsObject



483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 483

def must_support_elements
   = []
  plain_must_support_elements.each do |current_element|
     = {
      path: current_element.id.gsub("#{resource}.", '')
    }
    [:by_requirement_extension_only] = true if by_requirement_extension_only?(current_element)

     = (, current_element)

    if .any?
      .concat()
    else
      handle_choice_type_in_sliced_element(, )

      supported_types = extract_supported_types(current_element)
      [:types] = supported_types if supported_types.present?

      if current_element.type.first&.code == 'Reference'
        handle_type_must_support_target_profiles(current_element.type.first,
                                                 )
      end

      handle_fixed_values(, current_element)

      (, )

       << 
    end
  end
  .uniq
end

#must_support_extension_elementsObject



81
82
83
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 81

def must_support_extension_elements
  all_must_support_elements.select { |element| element.path.end_with? 'xtension' }
end

#must_support_extensionsObject



85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 85

def must_support_extensions
  must_support_extension_elements.map do |element|
    {
      id: element.id,
      slice_name: extension_slice_name(element.id),
      path: element.path.gsub("#{resource}.", ''),
      url: canonical_url_without_version(element.type.first.profile.first),
      modifier_extension: element.path.end_with?('modifierExtension')
    }.tap do ||
      [:by_requirement_extension_only] = true if by_requirement_extension_only?(element)
    end
  end
end

#must_support_slice_elementsObject



112
113
114
115
116
117
118
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 112

def must_support_slice_elements
  profile_elements.select do |element|
    next false if element.sliceName.blank? || element.path.end_with?('xtension')

    all_must_support_elements.include?(element) || slice_has_must_support_descendants?(element)
  end
end

#must_support_slicesObject



388
389
390
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 388

def must_support_slices
  type_slices + value_slices
end

#must_support_type_slice_elementsObject



275
276
277
278
279
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 275

def must_support_type_slice_elements
  must_support_slice_elements.select do |element|
    discriminators(sliced_element(element))&.first&.type == 'type'
  end
end

#must_support_value_slice_elementsObject



326
327
328
329
330
331
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 326

def must_support_value_slice_elements
  must_support_slice_elements.select do |element|
    # discriminator type 'pattern' is deprecated in FHIR R5 and made equivalent to 'value'
    ['value', 'pattern'].include?(discriminators(sliced_element(element))&.first&.type)
  end
end

#must_supportsHash

Retrieval method for the must support metadata

Returns:

  • (Hash)


45
46
47
48
49
50
51
52
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 45

def must_supports
  @must_supports ||= {
    extensions: must_support_extensions,
    slices: must_support_slices,
    elements: must_support_elements,
    recursive_elements: recursive_element_segments
  }
end


281
282
283
284
285
286
287
288
289
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 281

def navigation_compatible_discriminator_path(discriminator_path)
  normalized_path = discriminator_path&.gsub(/(modifierExtension|extension)\('([^']+)'\)/, "\\1.where(url='\\2')")
  normalized_path = normalized_path&.gsub(/([[:word:]\[\]]+)\.ofType\(([^)]+)\)/) do
    "#{Regexp.last_match(1).delete_suffix('[x]')}#{Regexp.last_match(2).upcase_first}"
  end
  normalized_path&.gsub(/([[:word:]\[\]]+)\s+as\s+([[:word:]]+)/) do
    "#{Regexp.last_match(1).delete_suffix('[x]')}#{Regexp.last_match(2).upcase_first}"
  end
end

#plain_must_support_elementsObject



392
393
394
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 392

def plain_must_support_elements
  all_must_support_elements - must_support_extension_elements - must_support_slice_elements
end

#profile_nameString

Returns the human-readable name of the profile, eg for display purposes. Collapses any runs of repeated whitespace, since profile titles occasionally have them.

Returns:

  • (String)

    the human-readable name of the profile, eg for display purposes. Collapses any runs of repeated whitespace, since profile titles occasionally have them.



34
35
36
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 34

def profile_name
  profile.title&.squeeze(' ')
end

#profile_urlString

Returns the canonical URL of the profile.

Returns:

  • (String)

    the canonical URL of the profile



28
29
30
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 28

def profile_url
  profile.url
end

#profile_versionString

Returns the version of the profile itself (distinct from the IG's own version).

Returns:

  • (String)

    the version of the profile itself (distinct from the IG's own version)



39
40
41
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 39

def profile_version
  profile.version
end

#recursive_element_segmentsArray<String>

Names of path segments that are self-referential, ie, they may repeat at arbitrary depth. FHIR marks this with a contentReference back to an ancestor element instead of re-declaring the element's children, eg, Questionnaire.item.item has a contentReference of "#Questionnaire.item". A MustSupport flag on an element under such a segment (eg Questionnaire.item.text) should be considered met if it's populated at any depth of nesting (item.text, item.item.text, item.item.item.text, ...), not just the literal depth at which the flag is declared. NOTE: does not currently handle sliced recursive elements.

Returns:

  • (Array<String>)


63
64
65
66
67
68
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 63

def recursive_element_segments
  profile_elements
    .select { |element| element.contentReference.present? }
    .map { |element| element.id.split('.').last }
    .uniq
end

#remove_conflicting_metadata_without_fixed_value(must_support_elements_metadata, current_metadata) ⇒ Object



520
521
522
523
524
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 520

def (, )
  .delete_if do ||
    [:path] == [:path] && [:fixed_value].blank?
  end
end

#required_binding_pattern?(pattern_element) ⇒ Boolean

Returns:

  • (Boolean)


264
265
266
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 264

def required_binding_pattern?(pattern_element)
  pattern_element.binding&.strength == 'required' && pattern_element.binding&.valueSet
end

#save_pattern_slice(pattern_element, discriminator_path, metadata) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 226

def save_pattern_slice(pattern_element, discriminator_path, )
  runtime_path = navigation_compatible_discriminator_path(discriminator_path)

  if pattern_element.patternCodeableConcept
    {
      type: 'patternCodeableConcept',
      path: runtime_path,
      code: pattern_element.patternCodeableConcept.coding.first.code,
      system: pattern_element.patternCodeableConcept.coding.first.system
    }
  elsif pattern_element.patternCoding
    {
      type: 'patternCoding',
      path: runtime_path,
      code: pattern_element.patternCoding.code,
      system: pattern_element.patternCoding.system
    }
  elsif pattern_element.patternIdentifier
    {
      type: 'patternIdentifier',
      path: runtime_path,
      system: pattern_element.patternIdentifier.system
    }
  elsif required_binding_pattern?(pattern_element)
    {
      type: 'requiredBinding',
      path: runtime_path,
      values: extract_required_binding_values(pattern_element, )
    }
  else
    # prevent errors in case an IG does something different
    {
      type: 'unsupported',
      path: runtime_path
    }
  end
end

#save_type_code?(type) ⇒ Boolean

Returns:

  • (Boolean)


425
426
427
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 425

def save_type_code?(type)
  type.code == 'Reference'
end

#slice_has_must_support_descendants?(slice) ⇒ Boolean

Returns:

  • (Boolean)


130
131
132
133
134
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 130

def slice_has_must_support_descendants?(slice)
  all_must_support_elements.any? do |element|
    element.id.start_with?("#{slice.id}.")
  end
end

#sliced_element(slice) ⇒ Object



120
121
122
123
124
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 120

def sliced_element(slice)
  profile_elements.find do |element|
    element.id == slice.path || element.id == slice.id.sub(":#{slice.sliceName}", '')
  end
end

#take_choice_discriminator_step(current_element, step_path, remaining_path) ⇒ Object



194
195
196
197
198
199
200
201
202
203
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 194

def take_choice_discriminator_step(current_element, step_path, remaining_path)
  target_element = "#{step_path}[x]"
  target_type, remaining_path = remaining_path.delete_prefix('ofType(').split(')', 2)
  next_element = profile_elements.find do |element|
    element.id == "#{current_element.id}.#{target_element}" &&
      element.type.any? { |type| type.code.casecmp?(target_type) }
  end

  [next_element, remaining_path&.delete_prefix('.')]
end

#take_discriminator_step(current_element, path) ⇒ Object



148
149
150
151
152
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 148

def take_discriminator_step(current_element, path)
  return take_extension_discriminator_step(current_element, path) if extension_discriminator_step?(path)

  take_standard_discriminator_step(current_element, path)
end

#take_extension_discriminator_step(current_element, path) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 158

def take_extension_discriminator_step(current_element, path)
  extension_type = path.start_with?('modifierExtension(') ? 'modifierExtension' : 'extension'
  ext_url, remaining_path = path.delete_prefix("#{extension_type}('").split("')", 2)
  next_element = profile_elements.find do |element|
    element.path == "#{current_element.path}.#{extension_type}" &&
      element.id.start_with?("#{current_element.id}.#{extension_type}:") &&
      element.type.any? { |type| extension_profile_matches?(type, ext_url) }
  end

  [next_element, remaining_path&.delete_prefix('.')]
end

#take_legacy_choice_discriminator_step(current_element, step_path, remaining_path) ⇒ Object



205
206
207
208
209
210
211
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 205

def take_legacy_choice_discriminator_step(current_element, step_path, remaining_path)
  target_element, target_type = step_path.split(/\s+as\s+/, 2)
  target_element = "#{target_element}[x]" unless target_element.end_with?('[x]')
  next_element = find_choice_element(current_element, target_element, target_type)

  [next_element, remaining_path&.delete_prefix('.')]
end

#take_standard_discriminator_step(current_element, path) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 174

def take_standard_discriminator_step(current_element, path)
  step_path, remaining_path = path.split('.', 2)
  if remaining_path&.start_with?('ofType(')
    return take_choice_discriminator_step(current_element, step_path, remaining_path)
  end
  if legacy_choice_discriminator_step?(step_path)
    return take_legacy_choice_discriminator_step(current_element, step_path, remaining_path)
  end

  next_element =
    profile_elements.find { |element| element.id == "#{current_element.id}.#{step_path}" } ||
    profile_elements.find { |element| element.id == "#{current_element.path}.#{step_path}" }

  [next_element, remaining_path&.delete_prefix('.')]
end

#type_must_support_extension?(extensions) ⇒ Boolean

Returns:

  • (Boolean)


418
419
420
421
422
423
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 418

def type_must_support_extension?(extensions)
  extensions&.any? do |extension|
    extension.url == 'http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support' &&
      extension.valueBoolean
  end
end

#type_slicesObject



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 301

def type_slices
  must_support_type_slice_elements.map do |current_element|
    discriminator = discriminators(sliced_element(current_element)).first
    type_path = discriminator_path(discriminator)
    type_element = find_element_by_discriminator_path(current_element, type_path)

    type_code = type_element.type.first.code
     = {
      type: 'type',
      code: type_code.upcase_first
    }
    runtime_path = navigation_compatible_discriminator_path(type_path)
    [:path] = runtime_path if runtime_path.present?

    {
      slice_id: current_element.id,
      slice_name: current_element.sliceName,
      path: current_element.path.gsub("#{resource}.", ''),
      discriminator: 
    }.tap do ||
      [:by_requirement_extension_only] = true if by_requirement_extension_only?(current_element)
    end
  end
end

#value_not_empty?(value) ⇒ Boolean

Returns:

  • (Boolean)


400
401
402
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 400

def value_not_empty?(value)
  value.present? || value == false
end

#value_slicesObject

rubocop:disable Metrics/CyclomaticComplexity



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 333

def value_slices # rubocop:disable Metrics/CyclomaticComplexity
  must_support_value_slice_elements.map do |current_element|
    {
      slice_id: current_element.id,
      slice_name: current_element.sliceName,
      path: current_element.path.gsub("#{resource}.", '')
    }.tap do ||
      fixed_values = []
      pattern_value = {}

      element_discriminators = discriminators(sliced_element(current_element))

      element_discriminators.each do |discriminator|
        discriminator_path = discriminator_path(discriminator)
        pattern_element = find_element_by_discriminator_path(current_element, discriminator_path)

        # This is a workaround for a known version of a profile that has a bad discriminator:
        # the discriminator refers to a nested field within a CodeableConcept,
        # but the profile doesn't contain an element definition for it, so there's no way to
        # define a fixed value on the element to define the slice.
        # In this instance the element has a second (good) discriminator on the CodeableConcept field itself,
        # and in subsequent versions of the profile, the bad discriminator was removed.
        next if pattern_element.nil? && element_discriminators.length > 1

        if pattern_element.nil?
          pattern_value = {
            type: 'unsupported',
            path: navigation_compatible_discriminator_path(discriminator_path)
          }
        elsif value_not_empty?(pattern_element.fixed)
          fixed_values << {
            path: navigation_compatible_discriminator_path(discriminator_path),
            value: pattern_element.fixed
          }
        elsif pattern_value.present?
          raise StandardError, "Found more than one pattern slices for the same element #{pattern_element}."
        else
          pattern_value = save_pattern_slice(pattern_element, discriminator_path, )
        end
      end

      if fixed_values.present?
        [:discriminator] = {
          type: 'value',
          values: fixed_values
        }
      elsif pattern_value.present?
        [:discriminator] = pattern_value
      end

      [:by_requirement_extension_only] = true if by_requirement_extension_only?(current_element)
    end
  end
end