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

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of Validator.



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

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.



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

def name
  @name
end

#requirementsObject (readonly)

Returns the value of attribute requirements.



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

def requirements
  @requirements
end

#session_idObject

Returns the value of attribute session_id.



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

def session_id
  @session_id
end

#test_suite_idObject

Returns the value of attribute test_suite_id.



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

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



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

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.

Parameters:

  • target (FHIR::Model, Hash)

    the object being validated

  • profile_url (String)

    the profile URL being validated against

Returns:



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 436

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



123
124
125
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 123

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.

Parameters:



500
501
502
503
504
505
506
507
508
509
510
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 500

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.

Parameters:



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

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)

Returns:

  • (Boolean)


561
562
563
564
565
566
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 561

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

#call_validator(target, profile_url) ⇒ Object



354
355
356
357
358
359
360
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 354

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

Returns:

  • (Boolean)


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

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)

  # 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.

Returns:

  • (Boolean)


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 165

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

Returns:

  • (Boolean)


549
550
551
552
553
554
555
556
557
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 549

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.

Parameters:

  • raw_issue (Hash)

    the raw issue from validator response

  • target (FHIR::Model, Hash)

    the object being validated

Returns:



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 336

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

#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:



158
159
160
161
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 158

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

#exclude_unresolved_url_messageProc

Filter for excluding unresolved URL validation messages

Returns:

  • (Proc)

    a proc that checks if a message is an unresolved URL message



488
489
490
491
492
493
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 488

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

#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.

Parameters:

  • issues (Array<ValidatorIssue>)

    the complete list of issues

  • base_issue (ValidatorIssue)

    the issue to potentially filter

  • base_index (Integer)

    the index of the base issue in the issues array



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

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.

Parameters:



461
462
463
464
465
466
467
468
469
470
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 461

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.

Parameters:

  • issues (Array<ValidatorIssue>)

    the complete list of issues

  • start_index (Integer)

    the index to start searching from

  • base_location (String)

    the location to match

Returns:



575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 575

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.

Parameters:

  • response (Faraday::Response)

    the HTTP response from the validator

  • target (FHIR::Model, Hash)

    the object being validated

Returns:



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 312

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

Parameters:

  • target (FHIR::Model, Hash)

    the object to validate

  • profile_url (String)

    the profile URL to validate against

  • runnable (Object)

    the runnable context

Returns:

  • (Faraday::Response)

    the HTTP response from the validator



265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 265

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")

Parameters:

  • validator_igs (Array<String>)


72
73
74
75
76
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 72

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

  validation_context.igs
end

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

Joins additional validation messages to the issues list

Parameters:

  • issues (Array<ValidatorIssue>)

    the list of validator issues

  • target (FHIR::Model)

    the object being validated

  • profile_url (String)

    the profile URL being validated against

Returns:

  • (Array<ValidatorIssue>)

    the complete list of issues including additional messages



411
412
413
414
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 411

def join_additional_validation_messages(issues, target, profile_url)
  additional_issues = additional_validation_messages(target, profile_url)
  issues + additional_issues
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.

Parameters:



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

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.



146
147
148
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 146

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

Parameters:

  • string (String)

    the string to clean

Returns:

  • (String)

    the cleaned string



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

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.

Parameters:

  • resource (FHIR::Model)

    the resource to validate

  • profile_url (String)

    the profile URL to validate against

  • runnable (Object)

    the runnable context (test/group/suite)

  • add_messages_to_runnable (Boolean) (defaults to: true)

    whether to add messages to the runnable

  • message_prefix (String) (defaults to: '')

    Prefix to add to the start of logged messages

  • validator_response_details (Array, nil) (defaults to: nil)

    if not nil, the service will populate this array with the detailed response message from the validator service. Can be used by test kits to perform custom handling of error messages.

Returns:

  • (Boolean)

    true if the resource is valid

See Also:

  • Inferno::DSL::FHIRResourceValidation#resource_is_valid?


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 207

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.

Parameters:

Returns:

  • (Boolean)

    true if the message should be filtered out



478
479
480
481
482
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 478

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

#url(validator_url = nil) ⇒ Object

Set the url of the validator service

Parameters:

  • validator_url (String) (defaults to: nil)


60
61
62
63
64
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 60

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.

Parameters:

Returns:

  • (String)

    the body of the validation response



369
370
371
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 369

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

Parameters:

  • definition (Hash) (defaults to: nil)

    raw fields to set, optional



106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 106

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

Parameters:

  • error (Exception)

    An error exception that happened during evaluator connection

Returns:

  • (String)

    A readable error message describing the specific network problem



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 378

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



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

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.

Parameters:

  • target (FHIR::Model, Hash)

    the object to validate

  • profile_url (String)

    the profile URL to validate against



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

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



593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
# File 'lib/inferno/dsl/fhir_resource_validation.rb', line 593

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 => {
      **validation_context.definition,
      profiles: [profile_url]
    },
    filesToValidate: [
      {
        fileName: "#{profile_url.split('/').last}.json",
        fileContent: file_contents,
        fileType: 'json'
      }
    ],
    sessionId: @session_id
  }
  wrapped_resource.to_json
end