Class: Inferno::DSL::FHIRResourceValidation::Validator

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

Constant Summary collapse

VALIDATOR_DEBUG_LOGGING_ENV_VAR =

Environment variable that, when set to a truthy-looking value, enables verbose validation logging (the validationContext sent with each request, and the resulting issues including which were filtered out) through Inferno's normal application logger, tagged with the validator definition and, when available, the test session and test that triggered the request.

'FHIR_RESOURCE_VALIDATOR_DEBUG_LOGGING'.freeze
EXPANSION_PARAMETERS_ENV_VAR =

Environment variable containing the default expansion parameters. Used by #expansion_parameters when no value has been set explicitly. May contain either the raw JSON content of a FHIR Parameters resource (if it starts with {) or a path to a file containing one.

'FHIR_RESOURCE_VALIDATOR_EXPANSION_PARAMETERS'.freeze
TX_LOG_ENV_VAR =

Environment variable that, when set, enables terminology server request logging by the validator itself. Its value is sent as txLog in the validationContext of every validation request, telling the validator where to log the terminology server requests it makes. Note that the log will appear within the container running the validator.

'FHIR_RESOURCE_VALIDATOR_TX_LOG'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, test_suite_id = nil, requirements = nil) ⇒ Validator



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

def initialize(name = nil, test_suite_id = nil, requirements = nil, &)
  @name = name
  @test_suite_id = test_suite_id
  instance_eval(&)
  @requirements = requirements
end

Instance Attribute Details

#nameObject

Returns the value of attribute name.



45
46
47
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 45

def name
  @name
end

#requirementsObject (readonly)

Returns the value of attribute requirements.



44
45
46
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 44

def requirements
  @requirements
end

#session_idObject

Returns the value of attribute session_id.



45
46
47
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 45

def session_id
  @session_id
end

#test_suite_idObject

Returns the value of attribute test_suite_id.



45
46
47
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 45

def test_suite_id
  @test_suite_id
end

Instance Method Details

#add_validation_messages_to_runnable(runnable, filtered_issues, message_prefix: '') ⇒ Object

Adds validation messages to the runnable



420
421
422
423
424
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 420

def add_validation_messages_to_runnable(runnable, filtered_issues, message_prefix: '')
  filtered_issues.each do |issue|
    runnable.add_message(issue.severity, "#{message_prefix}#{issue.message}")
  end
end

#additional_validation_messages(target, profile_url) ⇒ Array<ValidatorIssue>

Gets additional validation messages from custom validation blocks. Converts the message hashes to ValidatorIssue objects.



619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 619

def additional_validation_messages(target, profile_url)
  additional_validations
    .flat_map { |step| step.call(target, profile_url) }
    .select { |message| message.is_a? Hash }
    .map do |message_hash|
      # Create a synthetic raw_issue for additional validation messages
      synthetic_raw_issue = {
        'level' => message_hash[:type].upcase,
        'location' => 'additional_validation',
        'message' => message_hash[:message]
      }
      ValidatorIssue.new(
        raw_issue: synthetic_raw_issue,
        target: target,
        slice_info: [],
        filtered: false
      )
    end
end

#additional_validationsObject

Used internally by perform_additional_validation



260
261
262
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 260

def additional_validations
  @additional_validations ||= []
end

#apply_relationship_filters(issues) ⇒ Object

Performs filtering based on relationships between issues. Processes sub-issues of each issue before processing the top-level issues.



683
684
685
686
687
688
689
690
691
692
693
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 683

def apply_relationship_filters(issues)
  apply_relationship_filters_to_children(issues)

  issues.each_with_index do |issue, index|
    next if issue.filtered # Skip if already filtered

    # Apply conditional filters.
    # As more are needed, split with a "next if issue.filtered" pattern and add the new filter.
    filter_contained_resource(issues, issue, index)
  end
end

#apply_relationship_filters_to_children(issues) ⇒ Object

Performs filtering based on relationships between issues on the sub-issues of a list of issues.



700
701
702
703
704
705
706
707
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 700

def apply_relationship_filters_to_children(issues)
  issues.each do |issue|
    next if issue.filtered # Skip if already filtered

    # Recursively process nested slice_info first (depth-first)
    apply_relationship_filters(issue.slice_info) if issue.slice_info.any?
  end
end

#at_least_one_profile_without_errors?(details_issues) ⇒ Boolean

Checks if any profile is valid (all error-level slices are filtered)



744
745
746
747
748
749
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 744

def at_least_one_profile_without_errors?(details_issues)
  details_issues.any? do |details_issue|
    error_level_slices = details_issue.slice_info.select { |s| s.severity == 'error' }
    error_level_slices.all?(&:filtered)
  end
end

#build_expansion_parameters(value) ⇒ Object



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

def build_expansion_parameters(value)
  case value
  when Hash
    build_expansion_parameters_from_json_content(value.to_json)
  else
    {
      fileName: File.basename(value),
      fileContent: File.read(value),
      fileType: nil
    }
  end
end

#build_expansion_parameters_from_env(env_value) ⇒ Object

Determines whether the environment variable's content is raw JSON or a file path based on its first non-whitespace character.



197
198
199
200
201
202
203
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 197

def build_expansion_parameters_from_env(env_value)
  if env_value.lstrip.start_with?('{')
    build_expansion_parameters_from_json_content(env_value)
  else
    build_expansion_parameters(env_value)
  end
end

#build_expansion_parameters_from_json_content(json_content) ⇒ Object



220
221
222
223
224
225
226
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 220

def build_expansion_parameters_from_json_content(json_content)
  {
    fileName: 'expansion_parameters.json',
    fileContent: json_content,
    fileType: nil
  }
end

#build_validation_context(profile_url) ⇒ Hash

Builds the validationContext sent with a request for the given profile. Pulled out on its own (rather than inlined in wrap_target_for_hl7_wrapper) so the same, resource-content-free context can also be used for logging in log_validation_result.



249
250
251
252
253
254
255
256
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 249

def build_validation_context(profile_url)
  context = {
    **validation_context.definition,
    profiles: [profile_url]
  }
  context[:txLog] = tx_log if tx_log
  context
end

#call_validator(target, profile_url) ⇒ Object



492
493
494
495
496
497
498
499
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 492

def call_validator(target, profile_url)
  request_body = wrap_target_for_hl7_wrapper(target, profile_url)

  Faraday.new(
    url,
    request: { timeout: 600 }
  ).post('validate', request_body, content_type: 'application/json')
end

#conformant?(target, profile_url, runnable, add_messages_to_runnable: true, message_prefix: '', validator_response_details: nil) ⇒ Boolean



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
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 366

def conformant?(target, profile_url, runnable, add_messages_to_runnable: true,
                message_prefix: '', validator_response_details: nil)

  # 1. Get raw content from validator
  response = get_raw_validator_content(target, profile_url, runnable)

  # 2. Convert to validation issues
  issues = get_issues_from_validator_response(response, target)

  # 3. Add additional validation messages
  issues = join_additional_validation_messages(issues, target, profile_url)

  # 4. Mark resources as filtered
  mark_issues_for_filtering(issues)
  log_validation_result(profile_url, issues, runnable) if debug_logging_enabled?

  # 5. Add error messages to runnable
  filtered_issues = issues.reject(&:filtered)
  add_validation_messages_to_runnable(runnable, filtered_issues, message_prefix:) if add_messages_to_runnable
  validator_response_details&.concat(issues)

  # 6. Return validity
  filtered_issues.none? { |issue| issue.severity == 'error' }
rescue Inferno::Exceptions::ErrorInValidatorException
  raise
rescue StandardError => e
  runnable.add_message('error', e.message)
  raise Inferno::Exceptions::ErrorInValidatorException,
        'Error occurred in the validator. Review Messages tab or validator service logs for more information.'
end

#conforms_to_logical_model?(object, model_url, runnable, add_messages_to_runnable: true, message_prefix: '', validator_response_details: nil) ⇒ Boolean

Validate a FHIR resource and determine if it's valid. Adds validation messages to the runnable if add_messages_to_runnable is true.



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

def conforms_to_logical_model?(object, model_url, runnable, add_messages_to_runnable: true,
                               message_prefix: '', validator_response_details: nil)

  unless model_url.present?
    raise Inferno::Exceptions::TestSuiteImplementationException.new(
      'Logical Model Validation',
      'The profile of the logical model must be provided.'
    )
  end

  unless object.present?
    if add_messages_to_runnable
      runnable.add_message(:error,
                           "#{message_prefix}No object to check for conformance.")
    end
    return false
  end

  unless object.is_a?(Hash)
    raise Inferno::Exceptions::TestSuiteImplementationException.new(
      'Logical Model Validation',
      "Expected a Hash, got a #{object.class}."
    )
  end

  conformant?(object, model_url, runnable, add_messages_to_runnable:, message_prefix:,
                                           validator_response_details:)
end

#contained_resource_profile_issue?(base_issue) ⇒ Boolean

Checks if a base issue should be processed for contained resource filtering



732
733
734
735
736
737
738
739
740
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 732

def contained_resource_profile_issue?(base_issue)
  return false if base_issue.filtered # Skip if already filtered

  message_id = base_issue.raw_issue['messageId']
  return false unless message_id == 'Reference_REF_CantMatchChoice'
  return false unless base_issue.severity == 'error' || base_issue.severity == 'warning'

  true
end

#convert_raw_issue_to_validator_issue(raw_issue, target) ⇒ ValidatorIssue

Converts a single raw issue hash to a ValidatorIssue object. Recursively processes sliceInfo if present.



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 474

def convert_raw_issue_to_validator_issue(raw_issue, target)
  # Recursively process sliceInfo
  slice_info = []
  if raw_issue['sliceInfo']&.any?
    slice_info = raw_issue['sliceInfo'].map do |slice_issue|
      convert_raw_issue_to_validator_issue(slice_issue, target)
    end
  end

  ValidatorIssue.new(
    raw_issue: raw_issue,
    target: target,
    slice_info: slice_info,
    filtered: false
  )
end

#debug_logging_enabled?Boolean



67
68
69
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 67

def debug_logging_enabled?
  ENV.fetch(VALIDATOR_DEBUG_LOGGING_ENV_VAR, nil).present?
end

#exclude_message {|message| ... } ⇒ Object

Filter out unwanted validation messages. Any messages for which the block evalutates to a truthy value will be excluded.

Examples:

validator do
  exclude_message { |message| message.type == 'info' }
end

Yield Parameters:



295
296
297
298
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 295

def exclude_message(&block)
  @exclude_message = block if block_given?
  @exclude_message
end

#exclude_unresolved_url_messageProc

Filter for excluding unresolved URL validation messages



671
672
673
674
675
676
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 671

def exclude_unresolved_url_message
  @exclude_unresolved_url_message ||= proc do |message|
    message.message.match?(/\A\S+: [^:]+: URL value '.*' does not resolve/) ||
      message.message.match?(/\A\S+: [^:]+: No definition could be found for URL value '.*'/)
  end
end

#expansion_parameters(value = nil) ⇒ Object

Set the expansion parameters to be sent with each validation request. This configures how the validator's terminology engine expands value sets during validation (e.g. designation preferences, forcing the use of the latest terminology versions, etc.). The content is sent inline with every validation request made by this validator, since the validator does not have access to Inferno's filesystem.

Accepts either a Hash containing the contents of a FHIR Parameters resource, or a String path to a file (JSON or XML) containing one. The file is read once, the first time it's needed.

If never set explicitly, this falls back to the FHIR_RESOURCE_VALIDATOR_EXPANSION_PARAMETERS environment variable, if present. This allows a shared set of expansion parameters to be configured once for every test kit that uses a given validator instance, while still letting individual test kits opt out or override it by calling this method themselves. The environment variable's content is treated as raw JSON if it starts with {, and otherwise as a file path.

Examples:

# Passing a Hash
fhir_resource_validator do
  url 'http://example.com/validator'
  expansion_parameters({
    resourceType: 'Parameters',
    parameter: [{ name: 'excludeNested', valueBoolean: true }]
  })
end
# Passing a file path
fhir_resource_validator do
  url 'http://example.com/validator'
  expansion_parameters 'path/to/expansion_parameters.json'
end


182
183
184
185
186
187
188
189
190
191
192
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 182

def expansion_parameters(value = nil)
  if value
    @expansion_parameters = build_expansion_parameters(value)
  elsif !@expansion_parameters_resolved
    env_value = ENV.fetch(EXPANSION_PARAMETERS_ENV_VAR, nil)
    @expansion_parameters = build_expansion_parameters_from_env(env_value) if env_value
  end
  @expansion_parameters_resolved = true

  @expansion_parameters
end

#filter_contained_resource(issues, base_issue, base_index) ⇒ Object

Filters Reference_REF_CantMatchChoice errors for contained resources. If a resource matches at least one profile (all slices filtered), marks the base error as filtered.



716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 716

def filter_contained_resource(issues, base_issue, base_index)
  return unless contained_resource_profile_issue?(base_issue)

  base_location = base_issue.location
  profile_detail_issues = find_following_profile_details_issues(issues, base_index, base_location)

  return if profile_detail_issues.empty?
  return unless at_least_one_profile_without_errors?(profile_detail_issues)

  base_issue.filtered = true
  # Also filter all the Details messages
  profile_detail_issues.each { |details_issue| details_issue.filtered = true }
end

#filter_individual_messages(issues) ⇒ Object

Recursively filters validation issues by setting the filtered flag. Applies filtering to both the issue itself and all nested slice_info.



644
645
646
647
648
649
650
651
652
653
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 644

def filter_individual_messages(issues)
  issues.each do |issue|
    # Create a mock message entity to check filtering rules
    mock_message = Entities::Message.new(type: issue.severity, message: issue.message)
    issue.filtered = should_filter_message?(mock_message)

    # Recursively filter slice_info
    filter_individual_messages(issue.slice_info) if issue.slice_info.any?
  end
end

#find_following_profile_details_issues(issues, start_index, base_location) ⇒ Array<ValidatorIssue>

Finds consecutive Details messages following a base issue at the same location.



758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 758

def find_following_profile_details_issues(issues, start_index, base_location)
  details_issues = []
  index = start_index + 1

  while index < issues.length
    issue = issues[index]

    # Check if this is a Details message for the same location
    break unless issue.message.include?('Details for #') && issue.location == base_location

    details_issues << issue
    index += 1
  end

  details_issues
end

#get_issues_from_validator_response(response, target) ⇒ Array<ValidatorIssue>

Converts raw validator response into a list of ValidatorIssue objects. Recursively processes slice information.



450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 450

def get_issues_from_validator_response(response, target)
  response_body = remove_invalid_characters(response.body)
  response_hash = JSON.parse(response_body)

  if response_hash['sessionId'].present? && response_hash['sessionId'] != @session_id
    validator_session_repo.save(test_suite_id:, validator_session_id: response_hash['sessionId'],
                                validator_name: name.to_s, suite_options: requirements)
    @session_id = response_hash['sessionId']
  end

  raw_issues = response_hash.dig('outcomes', 0, 'issues') || []

  raw_issues.map do |raw_issue|
    convert_raw_issue_to_validator_issue(raw_issue, target)
  end
end

#get_raw_validator_content(target, profile_url, runnable) ⇒ Faraday::Response

Gets raw content from validator including error handling



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

def get_raw_validator_content(target, profile_url, runnable)
  response = call_validator(target, profile_url)

  unless response.status == 200
    raise Inferno::Exceptions::ErrorInValidatorException,
          'Error occurred in the validator. Review Messages tab or validator service logs for more information.'
  end

  response
rescue StandardError => e
  runnable.add_message('error', e.message)
  Application[:logger].error(e.message)
  raise Inferno::Exceptions::ErrorInValidatorException, validator_error_message(e)
end

#igs(*validator_igs) ⇒ Object

Set the IGs that the validator will need to load

Examples:

igs "hl7.fhir.us.core#4.0.0"
igs("hl7.fhir.us.core#3.1.1", "hl7.fhir.us.core#6.0.0")


86
87
88
89
90
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 86

def igs(*validator_igs)
  validation_context(igs: validator_igs) if validator_igs.any?

  validation_context.igs
end

#issue_summary(issue) ⇒ Hash

Recursively builds a loggable summary of a validation issue, including nested slice_info, without any resource content.



535
536
537
538
539
540
541
542
543
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 535

def issue_summary(issue)
  {
    severity: issue.severity,
    location: issue.location,
    message: issue.message,
    filtered: issue.filtered,
    slice_info: issue.slice_info.any? ? issue.slice_info.map { |nested| issue_summary(nested) } : nil
  }.compact
end

#join_additional_validation_messages(issues, target, profile_url) ⇒ Array<ValidatorIssue>

Joins additional validation messages to the issues list



594
595
596
597
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 594

def join_additional_validation_messages(issues, target, profile_url)
  additional_issues = additional_validation_messages(target, profile_url)
  issues + additional_issues
end

#log_validation_result(profile_url, issues, runnable) ⇒ Object

Logs the validationContext and expansionParameters sent with a request together with the resulting issues (including which were filtered out) in a single entry, tagged with enough context to trace it back to the validator definition and triggering test run.

Deliberately omits the resource content itself (sent separately as filesToValidate): only the small, non-PHI-bearing validationContext and expansionParameters are logged, not the full request body.



515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 515

def log_validation_result(profile_url, issues, runnable)
  payload = {
    validator_name: name,
    test_suite_id: test_suite_id,
    test_session_id: runnable.respond_to?(:test_session_id) ? runnable.test_session_id : nil,
    test_id: runnable.id,
    validation_context: build_validation_context(profile_url),
    expansion_parameters:,
    issues: issues.map { |issue| issue_summary(issue) }
  }.compact

  Application[:logger].info("FHIR validation result: #{payload.to_json}")
end

#mark_issues_for_filtering(issues) ⇒ Object

Marks validation issues for filtering by setting the filtered flag on issues that should be excluded. Recursively marks issues in slice_info.



604
605
606
607
608
609
610
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 604

def mark_issues_for_filtering(issues)
  # Recursively mark all issues for filtering
  filter_individual_messages(issues)

  # Perform conditional filtering based on special cases
  apply_relationship_filters(issues)
end

#perform_additional_validation {|resource, profile_url| ... } ⇒ Object

Perform validation steps in addition to FHIR validation.

Examples:

perform_additional_validation do |resource, profile_url|
  if something_is_wrong
    { type: 'error', message: 'something is wrong' }
  else
    { type: 'info', message: 'everything is ok' }
  end
end

Yield Parameters:

  • resource (FHIR::Model)

    the resource being validated

  • profile_url (String)

    the profile the resource is being validated against

Yield Returns:

  • (Array<Hash<Symbol, String>>, Hash<Symbol, String>)

    The block should return a Hash or an Array of Hashes if any validation messages should be added. The Hash must contain two keys: :type and :message. :type can have a value of 'info', 'warning', or 'error'. A type of 'error' means the resource is invalid. :message contains the message string itself.



283
284
285
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 283

def perform_additional_validation(&block)
  additional_validations << block
end

#remove_invalid_characters(string) ⇒ String

Removes invalid characters from a string to prepare for JSON parsing



583
584
585
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 583

def remove_invalid_characters(string)
  string.gsub(/[^[:print:]\r\n]+/, '')
end

#resource_is_valid?(resource, profile_url, runnable, add_messages_to_runnable: true, message_prefix: '', validator_response_details: nil) ⇒ Boolean

Validate a FHIR resource and determine if it's valid. Adds validation messages to the runnable if add_messages_to_runnable is true.

See Also:

  • Inferno::DSL::FHIRResourceValidation#resource_is_valid?


344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 344

def resource_is_valid?(resource, profile_url, runnable, add_messages_to_runnable: true,
                       message_prefix: '', validator_response_details: nil)

  unless resource.present?
    runnable.add_message(:error, "#{message_prefix}No resource to validate.") if add_messages_to_runnable
    return false
  end

  unless resource.is_a?(FHIR::Model)
    raise Inferno::Exceptions::TestSuiteImplementationException.new(
      'FHIR Resource Validation',
      "Expected a FHIR::Model, got a #{resource.class}."
    )

  end
  profile_url ||= FHIR::Definitions.resource_definition(resource.resourceType).url

  conformant?(resource, profile_url, runnable, add_messages_to_runnable:, message_prefix:,
                                               validator_response_details:)
end

#should_filter_message?(message) ⇒ Boolean

Determines if a message should be filtered based on exclusion rules. Applies both the unresolved URL filter and any custom exclude_message filter.



661
662
663
664
665
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 661

def should_filter_message?(message)
  should_filter = exclude_unresolved_url_message.call(message) ||
                  exclude_message&.call(message)
  should_filter || false
end

#tx_logObject



237
238
239
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 237

def tx_log
  ENV.fetch(TX_LOG_ENV_VAR, nil).presence
end

#url(validator_url = nil) ⇒ Object

Set the url of the validator service



74
75
76
77
78
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 74

def url(validator_url = nil)
  @url = validator_url if validator_url
  @url ||= ENV.fetch('FHIR_RESOURCE_VALIDATOR_URL')
  @url
end

#validate(target, profile_url) ⇒ String

Post an object to the validation service for validating. Returns the raw validator response body.



552
553
554
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 552

def validate(target, profile_url)
  call_validator(target, profile_url).body
end

#validation_context(definition = nil) ⇒ Object Also known as: cli_context

Set the validationContext used as part of each validation request. Fields may be passed as either a Hash or block. Note that all fields included here will be sent directly in requests, there is no check that the fields are correct.

Examples:

# Passing fields in a block
fhir_resource_validator do
  url 'http://example.com/validator'
  validation_context do
    noExtensibleBindingMessages true
    allowExampleUrls true
    txServer nil
  end
end
# Passing fields in a Hash
fhir_resource_validator do
  url 'http://example.org/validator'
  validation_context({
    noExtensibleBindingMessages: true,
    allowExampleUrls: true,
    txServer: nil
  })
end


120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 120

def validation_context(definition = nil, &)
  if @validation_context
    if definition
      @validation_context.definition.merge!(definition.deep_symbolize_keys)
    elsif block_given?
      @validation_context.instance_eval(&)
    end
  else
    @validation_context = ValidationContext.new(definition || {}, &)
  end
  @validation_context
end

#validator_error_message(error) ⇒ String

Add a specific error message for specific network problems to help the user



561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 561

def validator_error_message(error)
  case error
  when Faraday::ConnectionFailed
    "Connection failed to validator at #{url}."
  when Faraday::TimeoutError
    "Timeout while connecting to validator at #{url}."
  when Faraday::SSLError
    "SSL error connecting to validator at #{url}."
  when Faraday::ClientError  # these are 400s
    "Client error (4xx) connecting to validator at #{url}."
  when Faraday::ServerError  # these are 500s
    "Server error (5xx) from validator at #{url}."
  else
    "Unable to connect to validator at #{url}."
  end
end

#validator_session_repoObject



54
55
56
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 54

def validator_session_repo
  @validator_session_repo ||= Inferno::Repositories::ValidatorSessions.new
end

#warm_up(target, profile_url) ⇒ Object

Warm up the validator session by sending a test validation request. This initializes the validator session and persists it for future use.



431
432
433
434
435
436
437
438
439
440
441
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 431

def warm_up(target, profile_url)
  response_body = validate(target, profile_url)
  res = JSON.parse(response_body)
  session_id = res['sessionId']
  validator_session_repo.save(test_suite_id:, validator_session_id: session_id,
                              validator_name: name.to_s, suite_options: requirements)
  self.session_id = session_id
rescue JSON::ParserError
  Application[:logger]
    .error("Validator warm_up - error unexpected response format from validator: #{response_body}")
end

#wrap_target_for_hl7_wrapper(target, profile_url) ⇒ Object



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 776

def wrap_target_for_hl7_wrapper(target, profile_url)
  validator_session_id =
    validator_session_repo.find_validator_session_id(test_suite_id,
                                                     name.to_s, requirements)

  @session_id = validator_session_id if validator_session_id

  # HL7 Validator Core 6.5.19+ renamed `cliContext` to `validationContext`.
  # This allows backward compatibility until the validator-wrapper is updated.
  context_key = Feature.use_validation_context_key? ? :validationContext : :cliContext

  file_contents =
    if target.is_a?(Hash)
      target.to_json
    else
      target.source_contents
    end

  wrapped_resource = {
    context_key => build_validation_context(profile_url),
    filesToValidate: [
      {
        fileName: "#{profile_url.split('/').last}.json",
        fileContent: file_contents,
        fileType: 'json'
      }
    ],
    sessionId: @session_id
  }
  wrapped_resource[:expansionParameters] = expansion_parameters if expansion_parameters

  wrapped_resource.to_json
end