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_resources ⇒ Object

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

#profile ⇒ Object

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_elements ⇒ Object

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_url ⇒ Object

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

#resource ⇒ Object

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_elements ⇒ Object



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

def all_must_support_elements
  profile_elements.select do |element|
    (element.mustSupport || by_requirement_extension_only?(element)) && !prohibited_by_cardinality?(element)
  end
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



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

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

#discriminator_path(discriminator) ⇒ Object



304
305
306
307
308
309
310
311
312
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 304

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



139
140
141
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 139

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

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

Returns:

  • (Boolean)


234
235
236
237
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 234

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)


414
415
416
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 414

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)


167
168
169
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 167

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

#extension_profile_matches?(type, ext_url) ⇒ Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 183

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)


117
118
119
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 117

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



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

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



534
535
536
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 534

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

#extract_target_profiles(type) ⇒ Object



471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 471

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



226
227
228
229
230
231
232
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 226

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



149
150
151
152
153
154
155
156
157
158
159
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 149

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



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 447

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



488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 488

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



422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 422

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



463
464
465
466
467
468
469
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 463

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)


203
204
205
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 203

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

#must_support_elements ⇒ Object



501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 501

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_elements ⇒ Object



94
95
96
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 94

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

#must_support_extensions ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 98

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_elements ⇒ Object



125
126
127
128
129
130
131
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 125

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_slices ⇒ Object



406
407
408
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 406

def must_support_slices
  type_slices + value_slices
end

#must_support_type_slice_elements ⇒ Object



288
289
290
291
292
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 288

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_elements ⇒ Object



339
340
341
342
343
344
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 339

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_supports ⇒ Hash

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


294
295
296
297
298
299
300
301
302
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 294

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_elements ⇒ Object



410
411
412
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 410

def plain_must_support_elements
  all_must_support_elements - must_support_extension_elements - must_support_slice_elements
end

#profile_name ⇒ String

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_url ⇒ String

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_version ⇒ String

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

#prohibited_by_cardinality?(element) ⇒ Boolean

A profile can inherit mustSupport flags from a base profile while constraining the element to a max cardinality of 0. Such an element, and its descendants, cannot appear in a conformant resource.

Returns:

  • (Boolean)


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

def prohibited_by_cardinality?(element)
  profile_elements.any? do |profile_element|
    profile_element.max == '0' &&
      (element.id == profile_element.id ||
        element.id.start_with?("#{profile_element.id}.") ||
        element.id.start_with?("#{profile_element.id}:"))
  end
end

#recursive_element_segments ⇒ Array<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



538
539
540
541
542
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 538

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

#required_binding_pattern?(pattern_element) ⇒ Boolean

Returns:

  • (Boolean)


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

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



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 239

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)


443
444
445
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 443

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

#slice_has_must_support_descendants?(slice) ⇒ Boolean

Returns:

  • (Boolean)


143
144
145
146
147
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 143

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



133
134
135
136
137
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 133

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



207
208
209
210
211
212
213
214
215
216
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 207

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



161
162
163
164
165
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 161

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



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 171

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



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

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



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 187

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)


436
437
438
439
440
441
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 436

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_slices ⇒ Object



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 314

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)


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

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

#value_slices ⇒ Object

rubocop:disable Metrics/CyclomaticComplexity



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
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/inferno/dsl/must_support_metadata_extractor.rb', line 346

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_element.patternString
          fixed_values << {
            path: navigation_compatible_discriminator_path(discriminator_path),
            value: pattern_element.patternString
          }
        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