Module: DaVinciPASTestKit::PasBundleValidation

Constant Summary collapse

CLAIM_PROFILE =
'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claim-update'
CLAIM_RESPONSE_PROFILE =
'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claimresponse'
CLAIM_INQUIRY_PROFILE =
'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claim-inquiry'
CLAIM_INQUIRY_RESPONSE_PROFILE =
'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claiminquiryresponse'

Constants included from ValidationTest

ValidationTest::DAR_CODE_SYSTEM_URL, ValidationTest::DAR_EXTENSION_URL

Instance Method Summary collapse

Methods included from ValidationTest

#check_for_dar, #check_for_dar_code, #check_for_dar_extension, #perform_validation_test

Instance Method Details

#absolute_url(reference, base_url) ⇒ Object



384
385
386
387
388
389
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 384

def absolute_url(reference, base_url)
  return if reference.blank?
  return reference if base_url.blank? || reference.starts_with?('urn:uuid:') || URI(reference).absolute?

  "#{base_url}/#{reference}"
end

#add_declared_profiles(instance, bundle_entry, version) ⇒ Object

Adds declared profiles from an instance (meta.profile) to the resource target profile map. It recursively processes each entry for further profile extraction.

Parameters:

  • instance (Object)

    The instance from which profiles are extracted.

  • bundle_entry (Array)

    The bundle.entry contents.

  • version (String)

    The IG version.



333
334
335
336
337
338
339
340
341
342
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 333

def add_declared_profiles(instance, bundle_entry, version)
  return unless instance.resource.present?

  instance.resource.meta&.profile&.each do |url|
    next if bundle_resources_target_profile_map[instance.fullUrl][:profile_urls].include?(url)

    bundle_resources_target_profile_map[instance.fullUrl][:profile_urls] << url
    extract_profiles_to_validate_each_entry(bundle_entry, instance, url, version)
  end
end

#add_profile_to_instance(instance, profile_url, bundle_entry, version) ⇒ Object

Adds a specific profile URL to an instance in the resource target profile map. It recursively processes the instance for further profile extraction.

Parameters:

  • instance (Object)

    The instance to which the profile URL is added.

  • profile_url (String)

    The profile URL to be added.

  • bundle_entry (Array)

    The bundle.entry contents.

  • version (String)

    The IG version.



350
351
352
353
354
355
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 350

def add_profile_to_instance(instance, profile_url, bundle_entry, version)
  return if bundle_resources_target_profile_map[instance.fullUrl][:profile_urls].include?(profile_url)

  bundle_resources_target_profile_map[instance.fullUrl][:profile_urls] << profile_url
  extract_profiles_to_validate_each_entry(bundle_entry, instance, profile_url, version)
end

#add_resource_target_profile_to_map(resource_full_url, resource, profile_url = nil) ⇒ Object

Adds a resource and its associated profile URL to the resource target profile map. If the resource is already in the map, it adds the profile URL to the resource’s list of profile URLs.

Parameters:

  • resource_full_url (String)

    The full URL of the resource.

  • resource (Object)

    The resource object.

  • profile_url (String) (defaults to: nil)

    The profile URL to associate with the resource.



199
200
201
202
203
204
205
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 199

def add_resource_target_profile_to_map(resource_full_url, resource, profile_url = nil)
  entry = bundle_resources_target_profile_map[resource_full_url] ||= { resource:, profile_urls: [] }

  return if profile_url.blank? || entry[:profile_urls].include?(profile_url)

  entry[:profile_urls] << profile_url
end

#all_scratch_resourcesObject



14
15
16
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 14

def all_scratch_resources
  scratch_resources[:all] ||= []
end

#bundle_entry_map(bundle_entry) ⇒ Object



366
367
368
369
370
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 366

def bundle_entry_map(bundle_entry)
  @bundle_entry_map ||= bundle_entry.each_with_object({}) do |entry, obj|
    obj[entry.fullUrl] = entry
  end
end

#bundle_resources_target_profile_mapHash

Returns a hash map where the keys are resource full URLs and the values are a hash containing the resource object and an array of associated profile URLs.

Returns:

  • (Hash)

    The resource target profile map.



190
191
192
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 190

def bundle_resources_target_profile_map
  @bundle_resources_target_profile_map ||= {}
end

#check_presence_of_referenced_resources(target_resource, base_url, resources_to_match) ⇒ Object

This method traverses references within a FHIR resource, ensuring that referenced resources are populated in the bundle. It also enforces that a referenced resource appears only once in the bundle, as required by the PAS IG.

Parameters:

  • target_resource (FHIR::Model)

    The FHIR resource to traverse and validate.

  • base_url (String)

    The server base url.

  • resources_to_match (Array<FHIR:Bundle:Entry] The list of FHIR bundle entries to match references against.)

    esources_to_match [Array<FHIR:Bundle:Entry] The list of FHIR bundle entries to match references against.



459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 459

def check_presence_of_referenced_resources(target_resource, base_url, resources_to_match)
  return if target_resource.blank?

  if target_resource.is_a?(FHIR::Reference) && target_resource.reference.present?
    ref = target_resource.reference
    return if ref.blank?

    absolute_ref = absolute_url(ref, base_url)
    matching_resources = resources_to_match.find_all { |res| res.fullUrl == absolute_ref }

    if matching_resources.length != 1
      validation_error_messages << resource_shall_appear_once_message(absolute_ref,
                                                                      matching_resources.length)
    end

    if matching_resources.length.positive?
      check_presence_of_referenced_resources(matching_resources.first, base_url, resources_to_match)
    end
  else
    target_resource.source_hash.each_key do |attr|
      value = target_resource.send(attr.to_sym)
      if value.is_a?(FHIR::Model)
        check_presence_of_referenced_resources(value, base_url, resources_to_match)
      elsif value.is_a?(Array) && value.all? { |elmt| elmt.is_a?(FHIR::Model) }
        value.each { |elmt| check_presence_of_referenced_resources(elmt, base_url, resources_to_match) }
      end
    end
  end
end

#extract_base_url(absolute_url) ⇒ String

Extracts the base URL from an absolute URL by removing the resource type and ID.

Parameters:

  • absolute_url (String)

    The absolute URL.

Returns:

  • (String)

    The base URL, or an empty string if the URL format is not as expected.



394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 394

def extract_base_url(absolute_url)
  return '' if absolute_url.blank?

  uri = URI(absolute_url)
  return '' unless uri.scheme && uri.host

  # Split the path segments and remove the last two segments (resource type and id)
  path_segments = uri.path.split('/')
  base_path = path_segments[0...-2].join('/')

  "#{uri.scheme}://#{uri.host}#{base_path}"
end

#extract_profiles_to_validate_each_entry(bundle_entry, current_entry, current_entry_profile_url, version) ⇒ Object

Processes each entry in a FHIR bundle to extract resource and possible profiles to validate against. It recursively evaluates referenced instances and their profiles, expanding the validation scope.

Parameters:

  • bundle_entry (Object)

    The current bundle entry being processed.

  • current_entry (Object)

    The current entry within the bundle.

  • current_entry_profile_url (String)

    The profile URL associated with the current entry.

  • version (String)

    The IG version.



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 235

def extract_profiles_to_validate_each_entry(bundle_entry, current_entry, current_entry_profile_url, version)
  return if current_entry.blank?

  # NOTE: the IG does not have the metadata for us-core profiles.
   = ("v#{version}")[current_entry_profile_url]
  return if .blank?

  bundle_map = bundle_entry_map(bundle_entry)
  reference_elements = .references

  # Special handling for Claim submit profile
  if current_entry_profile_url == CLAIM_PROFILE
    handle_claim_profile(reference_elements,
                         current_entry_profile_url)
  end

  reference_elements.each do |reference_element|
    process_reference_element(reference_element, current_entry, bundle_entry, bundle_map, version)
  end
end

#extract_resources_from_bundle(bundle: nil, response: nil, reply_handler: nil, max_pages: 20) ⇒ Object

Extracts resources from a bundle while following “next” links.

This method extracts resources from a FHIR bundle, following “next” links in the bundle until the specified maximum number of pages is reached. It collects resources and invokes the reply_handler for each response.

Parameters:

  • bundle (FHIR::Bundle) (defaults to: nil)

    The initial FHIR bundle to extract resources from.

  • response (Object) (defaults to: nil)

    The HTTP response object for the bundle retrieval.

  • reply_handler (Proc) (defaults to: nil)

    A callback function to handle responses.

  • max_pages (Integer) (defaults to: 20)

    The maximum number of pages to process.



499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 499

def extract_resources_from_bundle(
  bundle: nil,
  response: nil,
  reply_handler: nil,
  max_pages: 20
)
  page_count = 1
  resources = []

  until bundle.nil? || page_count == max_pages
    resources += bundle&.entry&.map { |entry| entry&.resource }
    next_bundle_link = bundle&.link&.find { |link| link.relation == 'next' }&.url
    reply_handler&.call(response)

    break if next_bundle_link.blank?

    page_count += 1
  end

  resources
end

#find_profile_url(request_type) ⇒ Object

Resource Types to validate in request/ response bundle



408
409
410
411
412
413
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 408

def find_profile_url(request_type)
  {
    'Claim' => request_type == 'submit' ? CLAIM_PROFILE : CLAIM_INQUIRY_PROFILE,
    'ClaimResponse' => request_type == 'submit' ? CLAIM_RESPONSE_PROFILE : CLAIM_INQUIRY_RESPONSE_PROFILE
  }
end

#find_referenced_instance_in_bundle(reference, enclosing_entry_fullurl, bundle_map) ⇒ Object

Finds a referenced instance in a FHIR bundle based on a reference and the full URL of the enclosing entry.

Parameters:

  • reference (String)

    The reference to find.

  • enclosing_entry_fullurl (String)

    The full URL of the enclosing entry.

  • bundle_map (Hash)

    A map of the bundle contents.

Returns:

  • (Object)

    The found instance, or nil if not found.



377
378
379
380
381
382
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 377

def find_referenced_instance_in_bundle(reference, enclosing_entry_fullurl, bundle_map)
  base_url = extract_base_url(enclosing_entry_fullurl)
  key = absolute_url(reference, base_url)

  bundle_map[key]
end

#generate_non_conformance_message(item) ⇒ Object



224
225
226
227
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 224

def generate_non_conformance_message(item)
  "#{item[:resource].resourceType}/#{item[:resource].id} is not conformant to any of the " \
    "target profiles: #{item[:profile_urls]}."
end

#handle_claim_profile(reference_elements, current_entry_profile_url) ⇒ Object

Handles the special case for the Claim profile in a FHIR bundle. It adds missing reference elements for the Claim profile. Claim.item.extension:requestedService value is a reference, but somehow not included in the metadata references.

Parameters:

  • reference_elements (Array)

    The array of reference elements to be updated.

  • current_entry_profile_url (String)

    The profile URL of the current entry being processed.



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 261

def handle_claim_profile(reference_elements, current_entry_profile_url)
  return unless current_entry_profile_url == CLAIM_PROFILE

  claim_ref_element = {
    path: 'Claim.item.extension.value',
    profiles: [
      'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-medicationrequest',
      'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-servicerequest',
      'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-devicerequest',
      'http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-nutritionorder'
    ]
  }

  reference_elements << claim_ref_element
end

#metadata_map(version) ⇒ Object

Mapping profile url to metadata



358
359
360
361
362
363
364
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 358

def (version)
   ||= YAML.load_file(File.join(__dir__, "generated/#{version}/metadata.yml"),
                               aliases: true)
   ||= [:groups].each_with_object({}) do |group, obj|
    obj[group[:profile_url]] = Generator::.new(group)
  end
end

#perform_request_validation(bundle, profile_url, version, request_type) ⇒ Object



30
31
32
33
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 30

def perform_request_validation(bundle, profile_url, version, request_type)
  validate_pa_request_payload_structure(bundle, request_type)
  validate_resources_conformance_against_profile(bundle, profile_url, version, request_type)
end

#perform_response_validation(response_bundle, profile_url, version, request_type, request_bundle = nil) ⇒ Object



35
36
37
38
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 35

def perform_response_validation(response_bundle, profile_url, version, request_type, request_bundle = nil)
  validate_pa_response_body_structure(response_bundle, request_bundle) if request_type == 'submit'
  validate_resources_conformance_against_profile(response_bundle, profile_url, version, request_type)
end

#process_instance_profiles(instance, bundle_entry, reference_element, version) ⇒ Object

Processes the profiles associated with a given instance in a FHIR bundle. It adds the instance’s profiles to the resource target profile map and handles recursive profile extraction. The profiles collected here are possible profiles the given instance may conform to. The conformance validation will ensure that the resource is conformant to at least one of the target profiles.

Parameters:

  • instance (Object)

    The instance whose profiles are to be processed.

  • bundle_entry (Array)

    The bundle.entry contents.

  • reference_element (Hash)

    The reference element related to the instance.

  • version (String)

    The IG version.



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 307

def process_instance_profiles(instance, bundle_entry, reference_element, version)
  add_resource_target_profile_to_map(instance.fullUrl, instance.resource)
  # Add the declared profile conformance
  add_declared_profiles(instance, bundle_entry, version)

  reference_element[:profiles].each do |profile_url|
    # NOTE: the IG does not have the metadata for us-core profiles.
     = ("v#{version}")[profile_url]
    resource_type = instance.resource.resourceType
    next unless &.resource == resource_type || profile_url.include?(resource_type)

    add_profile_to_instance(instance, profile_url, bundle_entry, version)
    # NOTE: The algorithm assumes OR semantics for profile conformance, where the resource needs to conform to
    # at least one of the collected profiles. However, it may not cover all scenarios, such as cases
    # where AND semantics are required for multiple profile conformance. Also, it does not address complex
    # situations where profile requirements may conflict or have dependencies across referenced instances.
    # Therefore, this algorithm may not provide complete validation for all scenarios, and additional checks may be
    # necessary depending on the use case.
  end
end

#process_reference_element(reference_element, current_entry, bundle_entry, bundle_map, version) ⇒ Object

Processes a given reference element definition from a FHIR bundle entry. It evaluates FHIRPath expressions and processes each referenced instance and its profiles.

Parameters:

  • reference_element (Hash)

    The reference element to process.

  • current_entry (Object)

    The current entry within the FHIR bundle.

  • bundle_entry (Array)

    The bundle.entry.

  • bundle_map (Hash)

    A map of the bundle contents.

  • version (String)

    The FHIR version.



284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 284

def process_reference_element(reference_element, current_entry, bundle_entry, bundle_map, version)
  fhirpath_result = evaluate_fhirpath(resource: current_entry.resource, path: reference_element[:path])
  reference_element_values = fhirpath_result.filter_map do |entry|
    entry['element']&.reference if entry['type'] == 'Reference'
  end

  referenced_instances = reference_element_values.filter_map do |value|
    find_referenced_instance_in_bundle(value, current_entry.fullUrl, bundle_map)
  end

  referenced_instances.each do |instance|
    process_instance_profiles(instance, bundle_entry, reference_element, version)
  end
end

#resource_present_in_pa_request_and_response_msg(resource) ⇒ Object

Generates a message for a resource present in both the PA request and response bundles.

This method generates an error message when a resource appears in both the PA request and response bundles but does not have the same fullUrl or identifiers.

Parameters:

  • resource (FHIR::Model)

    The resource present in both bundles.



542
543
544
545
546
547
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 542

def resource_present_in_pa_request_and_response_msg(resource)
  "
    Resource #{resource.resourceType}/#{resource.id} is an entry in both the PA Request Bundle
    and the Response Bundle, but they do not have the same fullUrl or identifiers
  "
end

#resource_shall_appear_once_message(absolute_ref, total_matches) ⇒ Object

Generates a message for a resource that appears more than once in a bundle.

This method generates an error message when a referenced resource appears more than once in a FHIR bundle, which is not allowed.

Parameters:

  • reference_resource_type (String)

    The resource type being referenced.

  • reference_resource_id (String)

    The resource ID being referenced.

  • total_matches (Integer)

    The total number of matches found in the bundle.



529
530
531
532
533
534
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 529

def resource_shall_appear_once_message(absolute_ref, total_matches)
  "
    The referenced #{absolute_ref} resource
    SHALL appear exactly once in the Bundle, but found #{total_matches}.
  "
end

#save_bundles_and_entries_to_scratch(bundles) ⇒ Object



18
19
20
21
22
23
24
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 18

def save_bundles_and_entries_to_scratch(bundles)
  bundles.each do |bundle|
    all_scratch_resources << bundle
    all_scratch_resources.concat(bundle.entry.map(&:resource))
    all_scratch_resources.uniq!
  end
end

#valid_url_or_urn_uuid?(string) ⇒ Boolean

Checks if a string is a valid url or in the form “urn:uuid:[some guid]”

Parameters:

  • string (String)

    The url string to check

Returns:

  • (Boolean)

    true if valid url or urn_uuid, otherwise false



446
447
448
449
450
451
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 446

def valid_url_or_urn_uuid?(string)
  url_regex = /\A#{URI::DEFAULT_PARSER.make_regexp(%w[http https])}\z/
  urn_uuid_regex = /\Aurn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/i

  string&.match?(url_regex) || string&.match?(urn_uuid_regex)
end

#validate_bundle_entries_against_profiles(version) ⇒ Object

Validates bundle resource and each entry in the bundle against its target profiles. It logs a message for each conformant entry and collects error messages for non-conformant entries. Asserts that there are no validation errors.

Parameters:

  • version (String)

    The version of the IG.



211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 211

def validate_bundle_entries_against_profiles(version)
  bundle_resources_target_profile_map.each_value do |item|
    resource = item[:resource]
    base_profile = FHIR::Definitions.resource_definition(resource.resourceType).url
    success_profile = item[:profile_urls].find do |url|
      profile_to_validate = url == base_profile ? url : "#{url}|#{version}"
      resource_is_valid?(resource:, profile_url: profile_to_validate)
    end

    validation_error_messages << generate_non_conformance_message(item) unless success_profile
  end
end

#validate_bundle_entries_full_url(bundle) ⇒ Object



435
436
437
438
439
440
441
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 435

def validate_bundle_entries_full_url(bundle)
  msg = "[Bundle/#{bundle.id}]: Bundle.entry.fullUrl values SHALL be a valid url or in the form " \
        "'urn:uuid:[some guid]'."
  bundle.entry.each do |entry|
    validation_error_messages << msg unless valid_url_or_urn_uuid?(entry.fullUrl)
  end
end

#validate_pa_request_payload_structure(bundle, request_type) ⇒ Object

Validates the structure of a Prior Authorization (PA) request Bundle.

This method performs various checks on the PA request payload, including validating the FHIR bundle structure, checking the resource type, and validating the resources referenced in the Claim resource are included in the bundle. It ensures that the first entry in the Bundle is a Claim resource and additional entries are populated with referenced resources, following the traversal of references. Duplicate resources are handled as required (appearing only once in the bundle entry).

Parameters:

  • bundle (FHIR::Bundle)

    The FHIR Bundle of the PA request.



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
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 75

def validate_pa_request_payload_structure(bundle, request_type)
  bundle_entry_resources = bundle.entry.map(&:resource)
  first_entry = bundle_entry_resources.first
  base_url = extract_base_url(bundle.entry.first&.fullUrl)

  check_presence_of_referenced_resources(first_entry, base_url, bundle.entry)

  if request_type == 'submit'
    unless first_entry.is_a?(FHIR::Claim)
      validation_error_messages << "[Bundle/#{bundle.id}]: The first bundle entry must be a Claim"
    end

    validate_uniqueness_of_supporting_info_sequences(first_entry)
    validate_bundle_entries_full_url(bundle)
  else
    claim_resource = bundle_entry_resources.find { |resource| resource.resourceType == 'Claim' }
    if claim_resource.blank?
      validation_error_messages << "[Bundle/#{bundle.id}]: Claim must be present for inquiry request"
    end

    # The inquiry operation must contain a requesting provider organization,
    # a payer organization, and a patient for the inquiry
    patient_reference = claim_resource&.patient&.reference
    provider_reference = claim_resource&.provider&.reference
    payer_reference = claim_resource&.insurer&.reference

    if patient_reference.blank?
      validation_error_messages <<
        "[Bundle/#{bundle.id}]: The Claim for inquiry operation must reference a patient."
    end
    if provider_reference.blank?
      validation_error_messages << "[Bundle/#{bundle.id}]: The claim for inquiry operation must reference " \
                                   'a requesting provider organization.'
    end
    if payer_reference.blank?
      validation_error_messages << "[Bundle/#{bundle.id}]: The Claim for inquiry operation must contain " \
                                   'a payer organization.'
    end
  end
end

#validate_pa_response_body_structure(pa_response_bundle, pa_request_bundle) ⇒ Object

Validates the response body structure of a Prior Authorization (PA) response.

This method performs validation of the PA response bundle structure. It follows the PAS IG requirement that the FHIR Bundle generated from the response starts with a ClaimResponse entry. For response of $submit request: Additional Bundle entries are populated with resources referenced by the ClaimResponse or descendant references, ensuring that only one resource is created for a given combination of content. Resources echoed back from the request are validated to ensure the same fullUrl and resource identifiers as in the request are used.

Parameters:

  • pa_response_bundle (FHIR::Bundle)

    The FHIR bundle representing the PA response.

  • pa_request_bundle (String)

    The JSON payload of the PA request bundle.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 130

def validate_pa_response_body_structure(pa_response_bundle, pa_request_bundle)
  first_entry = pa_response_bundle.entry.first.resource
  unless first_entry.is_a?(FHIR::ClaimResponse)
    validation_error_messages <<
      "[Bundle/#{pa_response_bundle.id}]: The first bundle entry must be a ClaimResponse"
  end

  base_url = extract_base_url(pa_response_bundle.entry.last&.fullUrl)
  check_presence_of_referenced_resources(first_entry, base_url, pa_response_bundle.entry)

  # Testing: When echoing back resources that are the same as were present in the prior authorization request,
  # the system SHALL ensure that the same fullUrl and resource identifiers are used in the response as appeared
  # in the request
  # pa_request_bundle = FHIR.from_contents(pa_request_bundle)
  # pa_response_bundle.entry.each do |entry|
  #   res = entry.resource
  #   request_entry = pa_request_bundle.entry.find do |ent|
  #     ent.resource.resourceType == res.resourceType && ent.resource.id == res.id
  #   end
  #   next unless request_entry.present?

  #   assert(
  #     request_entry.fullUrl == entry.fullUrl && request_entry.resource.identifier == res.identifier,
  #     resource_present_in_pa_request_and_response_msg(res)
  #   )
  # end
end

#validate_pas_bundle_json(json, profile_url, version, request_type, bundle_type, skips: false, message: '') ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 40

def validate_pas_bundle_json(json, profile_url, version, request_type, bundle_type, skips: false, message: '')
  assert_valid_json(json)
  bundle = FHIR.from_contents(json)
  assert bundle.present?, 'Not a FHIR resource'
  assert_resource_type(:bundle, resource: bundle)

  if bundle_type == 'request_bundle'
    perform_request_validation(bundle, profile_url, version, request_type)
  else
    perform_response_validation(bundle, profile_url, version, request_type)
  end

  validation_error_messages.each do |msg|
    messages << { type: 'error', message: msg }
  end
  msg = 'Bundle response returned and/or entry resources are not conformant. Check messages for issues found.'
  assert validation_error_messages.blank?, msg
rescue Inferno::Exceptions::AssertionException => e
  msg = "#{message} #{e.message}".strip
  raise e.class, msg unless skips

  skip msg
end

#validate_resources_conformance_against_profile(bundle, profile_url, version, request_type) ⇒ Object

Profile conformance of Prior Authorization (PA) resources.

This method performs conformance validation on the PA bundle and bundle entries. The request/response bundle and includes resources are validated against their respective profile.

Parameters:

  • bundle (FHIR::Bundle)

    The FHIR bundle representing the PA request/response.

  • profile_url (String)

    The URL of the FHIR profile to validate against.

  • version (String)

    The version of the profile.

  • request_type (String)

    the request operation: submit or inquiry



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 168

def validate_resources_conformance_against_profile(bundle, profile_url, version, request_type)
  add_resource_target_profile_to_map('bundle', bundle, profile_url)

  bundle_entry = bundle.entry

  root_entry = bundle_entry.find do |entry|
    ['Claim', 'ClaimResponse'].include?(entry.resource.resourceType)
  end

  if root_entry.present?
    root_resource_profile_url = find_profile_url(request_type)[root_entry.resource.resourceType]

    add_resource_target_profile_to_map(root_entry.fullUrl, root_entry.resource, root_resource_profile_url)
    extract_profiles_to_validate_each_entry(bundle_entry, root_entry, root_resource_profile_url, version)
  end

  validate_bundle_entries_against_profiles(version)
end

#validate_uniqueness_of_supporting_info_sequences(claim) ⇒ Object

Checks the following requirement: The Claim.supportingInfo.sequence for each entry SHALL be unique within the Claim.

Since the cardinality for Claim.supportingInfo is 0..*, we will check the uniqueness if Array not empty.

Parameters:

  • claim (FHIR::Claim)

    The FHIR Claim resource.



421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 421

def validate_uniqueness_of_supporting_info_sequences(claim)
  return unless claim.is_a?(FHIR::Claim)

  supporting_info = claim.supportingInfo
  return unless supporting_info.present?

  sequences = supporting_info.map(&:sequence)
  is_unique = sequences.uniq.length == sequences.length
  return if is_unique

  validation_error_messages << "[Claim/#{claim.id}]: The sequence element for each supportingInfo entry SHALL be " \
                               'unique within the Claim.'
end

#validation_error_messagesObject



26
27
28
# File 'lib/davinci_pas_test_kit/pas_bundle_validation.rb', line 26

def validation_error_messages
  @validation_error_messages ||= []
end