davinci_crd_test_kit 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/davinci_crd_test_kit/card_responses/propose_alternate_request.json +2 -52
  3. data/lib/davinci_crd_test_kit/card_responses/request_form_completion.json +46 -31
  4. data/lib/davinci_crd_test_kit/cards_validation.rb +8 -4
  5. data/lib/davinci_crd_test_kit/client_hooks_group.rb +22 -660
  6. data/lib/davinci_crd_test_kit/client_tests/appointment_book_receive_request_test.rb +17 -6
  7. data/lib/davinci_crd_test_kit/client_tests/client_appointment_book_group.rb +70 -0
  8. data/lib/davinci_crd_test_kit/client_tests/client_encounter_discharge_group.rb +71 -0
  9. data/lib/davinci_crd_test_kit/client_tests/client_encounter_start_group.rb +70 -0
  10. data/lib/davinci_crd_test_kit/client_tests/client_order_dispatch_group.rb +70 -0
  11. data/lib/davinci_crd_test_kit/client_tests/client_order_select_group.rb +72 -0
  12. data/lib/davinci_crd_test_kit/client_tests/client_order_sign_group.rb +71 -0
  13. data/lib/davinci_crd_test_kit/client_tests/decode_auth_token_test.rb +43 -23
  14. data/lib/davinci_crd_test_kit/client_tests/encounter_discharge_receive_request_test.rb +19 -6
  15. data/lib/davinci_crd_test_kit/client_tests/encounter_start_receive_request_test.rb +18 -6
  16. data/lib/davinci_crd_test_kit/client_tests/hook_request_optional_fields_test.rb +26 -10
  17. data/lib/davinci_crd_test_kit/client_tests/hook_request_required_fields_test.rb +20 -11
  18. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_context_test.rb +14 -10
  19. data/lib/davinci_crd_test_kit/client_tests/hook_request_valid_prefetch_test.rb +27 -110
  20. data/lib/davinci_crd_test_kit/client_tests/order_dispatch_receive_request_test.rb +18 -6
  21. data/lib/davinci_crd_test_kit/client_tests/order_select_receive_request_test.rb +18 -6
  22. data/lib/davinci_crd_test_kit/client_tests/order_sign_receive_request_test.rb +18 -6
  23. data/lib/davinci_crd_test_kit/client_tests/retrieve_jwks_test.rb +66 -29
  24. data/lib/davinci_crd_test_kit/client_tests/submitted_response_validation.rb +44 -0
  25. data/lib/davinci_crd_test_kit/client_tests/token_header_test.rb +45 -14
  26. data/lib/davinci_crd_test_kit/client_tests/token_payload_test.rb +43 -26
  27. data/lib/davinci_crd_test_kit/crd_client_suite.rb +0 -4
  28. data/lib/davinci_crd_test_kit/hook_request_field_validation.rb +240 -50
  29. data/lib/davinci_crd_test_kit/mock_service_response.rb +134 -120
  30. data/lib/davinci_crd_test_kit/routes/hook_request_endpoint.rb +26 -42
  31. data/lib/davinci_crd_test_kit/server_encounter_discharge_group.rb +24 -0
  32. data/lib/davinci_crd_test_kit/server_encounter_start_group.rb +24 -0
  33. data/lib/davinci_crd_test_kit/server_order_select_group.rb +24 -0
  34. data/lib/davinci_crd_test_kit/server_tests/coverage_information_system_action_received_test.rb +4 -1
  35. data/lib/davinci_crd_test_kit/server_tests/service_request_optional_fields_validation_test.rb +8 -10
  36. data/lib/davinci_crd_test_kit/server_tests/service_request_required_fields_validation_test.rb +5 -10
  37. data/lib/davinci_crd_test_kit/tags.rb +6 -6
  38. data/lib/davinci_crd_test_kit/version.rb +1 -1
  39. metadata +9 -2
@@ -1,5 +1,20 @@
1
1
  module DaVinciCRDTestKit
2
2
  module HookRequestFieldValidation
3
+ def request_number
4
+ if @request_number.blank?
5
+ ''
6
+ else
7
+ "Request #{@request_number}: "
8
+ end
9
+ end
10
+
11
+ def json_parse(json)
12
+ JSON.parse(json)
13
+ rescue JSON::ParserError
14
+ add_message('error', "#{request_number}Invalid JSON.")
15
+ false
16
+ end
17
+
3
18
  def hook_required_fields
4
19
  {
5
20
  'hook' => String,
@@ -82,23 +97,61 @@ module DaVinciCRDTestKit
82
97
  'Medication' => 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medication',
83
98
  'Device' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-device',
84
99
  'CommunicationRequest' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-communicationrequest',
85
- 'Task' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-taskquestionnaire'
100
+ 'Task' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-taskquestionnaire',
101
+ 'Coverage' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-coverage',
102
+ 'Location' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-location',
103
+ 'Organization' => 'http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-organization'
86
104
  }.freeze
87
105
  end
88
106
 
89
107
  def hook_request_required_fields_check(request_body, hook_name)
90
108
  hook_required_fields.each do |field, type|
91
- assert(request_body[field], "Hook request did not contain required field: `#{field}`")
92
- assert(request_body[field].is_a?(type), "Hook request field #{field} is not of type #{type}")
109
+ if request_body[field].blank?
110
+ add_message('error', "#{request_number}Hook request did not contain required field: `#{field}`")
111
+ next
112
+ end
113
+
114
+ unless request_body[field].is_a?(type)
115
+ add_message('error', "#{request_number}Hook request field #{field} is not of type #{type}")
116
+ next
117
+ end
118
+ end
119
+
120
+ if request_body['hook'] != hook_name
121
+ add_message('error',
122
+ "#{request_number}The `hook` field should be #{hook_name}, but was #{request_body['hook']}")
123
+ return
93
124
  end
94
125
 
95
- assert(request_body['hook'] == hook_name,
96
- "The `hook` field should be #{hook_name}, but was #{request_body['hook']}")
126
+ return unless request_body['fhirAuthorization'].present? && request_body['fhirServer'].blank?
97
127
 
98
- return unless request_body['fhirAuthorization']
128
+ add_message('error', %(
129
+ #{request_number}Missing `fhirServer` field: If `fhirAuthorization` is provided, this field is
130
+ #REQUIRED.))
131
+ end
132
+
133
+ def fhir_auth_fields_valid?(fhir_authorization_required_fields, fhir_authorization)
134
+ fhir_auth_valid = true
135
+ fhir_authorization_required_fields.each do |field, type|
136
+ if fhir_authorization[field].blank?
137
+ add_message('error', "#{request_number}`fhirAuthorization` did not contain required field: `#{field}`")
138
+ fhir_auth_valid = false
139
+ end
140
+ unless fhir_authorization[field].is_a?(type)
141
+ add_message('error', "#{request_number}`fhirAuthorization` field #{field} is not of type #{type}")
142
+ fhir_auth_valid = false
143
+ end
144
+ end
145
+ fhir_auth_valid
146
+ end
99
147
 
100
- assert(request_body['fhirServer'],
101
- 'Missing `fhirServer` field: If `fhirAuthorization` is provided, this field is REQUIRED.')
148
+ def check_patient_scope_requirement(scopes, fhir_authorization)
149
+ if scopes.any? { |scope| scope.start_with?('patient/') } &&
150
+ !(fhir_authorization['patient'] && fhir_authorization['patient'].is_a?(String))
151
+ info %(
152
+ #{request_number}The `patient` field for request SHOULD be populated to identify the FHIR id of that
153
+ patient when the granted SMART scopes include patient scopes)
154
+ end
102
155
  end
103
156
 
104
157
  def hook_request_fhir_auth_check(request_body)
@@ -106,36 +159,27 @@ module DaVinciCRDTestKit
106
159
 
107
160
  fhir_authorization = request_body['fhirAuthorization']
108
161
 
109
- fhir_authorization_required_fields.each do |field, type|
110
- assert(fhir_authorization[field], "`fhirAuthorization` did not contain required field: `#{field}`")
111
- assert(fhir_authorization[field].is_a?(type), "`fhirAuthorization` field #{field} is not of type #{type}")
112
- end
162
+ return unless fhir_auth_fields_valid?(fhir_authorization_required_fields, fhir_authorization)
113
163
 
114
- assert(fhir_authorization['token_type'] == 'Bearer',
115
- "`fhirAuthorization` `token_type` field is not set to 'Bearer'")
164
+ if fhir_authorization['token_type'] != 'Bearer'
165
+ add_message('error', "#{request_number}`fhirAuthorization` `token_type` field is not set to 'Bearer'")
166
+ end
116
167
 
117
168
  access_token = fhir_authorization['access_token']
118
169
 
119
170
  scopes = fhir_authorization['scope'].split
120
171
 
121
- if scopes.any? { |scope| scope.start_with?('patient/') }
122
- info do
123
- assert(fhir_authorization['patient'] && fhir_authorization['patient'].is_a?(String),
124
- %(The `patient` field SHOULD be populated to identify the FHIR id of that patient when the granted
125
- SMART scopes include patient scopes))
126
- end
127
- end
172
+ check_patient_scope_requirement(scopes, fhir_authorization)
128
173
  end
129
174
  { fhir_server_uri: request_body['fhirServer'], fhir_access_token: access_token }
130
175
  end
131
176
 
132
177
  def hook_request_optional_fields_check(request_body)
133
178
  hook_optional_fields.each do |field, type|
134
- info do
135
- assert(request_body[field], "Hook request did not contain optional field: `#{field}`")
136
- end
137
- if request_body[field]
138
- assert(request_body[field].is_a?(type), "Hook request field #{field} is not of type #{type}")
179
+ info "#{request_number}Hook request did not contain optional field: `#{field}`" if request_body[field].blank?
180
+
181
+ if request_body[field] && !request_body[field].is_a?(type)
182
+ add_message('error', "#{request_number}Hook request field #{field} is not of type #{type}")
139
183
  end
140
184
  end
141
185
  hook_request_fhir_auth_check(request_body)
@@ -165,7 +209,8 @@ module DaVinciCRDTestKit
165
209
  def hook_request_context_check(context, hook_name)
166
210
  required_fields = context_required_fields_by_hook[hook_name]
167
211
  required_fields.each do |field, type|
168
- validate_presence_and_type(context, field, type, "#{hook_name} request context")
212
+ validate_presence_and_type(context, field, type,
213
+ "#{request_number}#{hook_name} request context")
169
214
  end
170
215
  context_validate_optional_fields(context, hook_name)
171
216
  hook_specific_context_check(context, hook_name)
@@ -195,7 +240,7 @@ module DaVinciCRDTestKit
195
240
  resource_type, resource_id = reference.split('/')
196
241
 
197
242
  if supported_resource_types && !supported_resource_types.include?(resource_type)
198
- error_msg = "Unsupported resource type: `#{field_name}` type should be one " \
243
+ error_msg = "#{request_number}Unsupported resource type: `#{field_name}` type should be one " \
199
244
  "of the following: #{supported_resource_types.to_sentence}, but " \
200
245
  "received #{resource_type}."
201
246
 
@@ -210,7 +255,10 @@ module DaVinciCRDTestKit
210
255
  resource_type, resource_id = reference.split('/')
211
256
  return true if resource_type.present? && resource_id.present?
212
257
 
213
- add_message('error', "Invalid `#{field_name}` format. Expected `{resourceType}/{id}`, received `#{reference}`.")
258
+ add_message('error', %(
259
+ #{request_number}Invalid `#{field_name}` format. Expected `{resourceType}/{id}`,
260
+ received `#{reference}`.
261
+ ))
214
262
  false
215
263
  end
216
264
 
@@ -228,7 +276,9 @@ module DaVinciCRDTestKit
228
276
 
229
277
  def valid_id_format?(field, hook_name, resource_id)
230
278
  if resource_id.include?('/')
231
- error_msg = "`#{field}` in #{hook_name} context should be a plain ID, not a reference. Got: `#{resource_id}`."
279
+ error_msg = %(
280
+ #{request_number}`#{field}` in #{hook_name} context should be a plain ID, not a reference.
281
+ Got: `#{resource_id}`.)
232
282
  add_message('error', error_msg)
233
283
  false
234
284
  end
@@ -237,9 +287,9 @@ module DaVinciCRDTestKit
237
287
 
238
288
  def bundle_entries_check(context, context_field_name, bundle, resource_types, status = nil)
239
289
  target_resources = bundle.entry.map(&:resource).select { |r| resource_types.include?(r.resourceType) }
240
- unless target_resources.present?
241
- error_msg = "`#{context_field_name}` bundle must contain at least one of the expected resource types: " \
242
- "#{resource_types.to_sentence}. In Context `#{context}`"
290
+ if target_resources.blank?
291
+ error_msg = "#{request_number}`#{context_field_name}` bundle must contain at least one of the " \
292
+ "expected resource types: #{resource_types.to_sentence}. In Context `#{context}`"
243
293
  add_message('error', error_msg)
244
294
  return
245
295
  end
@@ -254,24 +304,24 @@ module DaVinciCRDTestKit
254
304
  def status_check(context, context_field_name, status, resources)
255
305
  return unless status && !resources.all? { |resource| resource.status == status }
256
306
 
257
- error_msg = "All #{resources.map(&:resourceType).uniq.to_sentence} resources in `#{context_field_name}` " \
258
- "bundle must have a `#{status}` status. In Context `#{context}`"
307
+ error_msg = "#{request_number}All #{resources.map(&:resourceType).uniq.to_sentence} resources in " \
308
+ "`#{context_field_name}` bundle must have a `#{status}` status. In Context `#{context}`"
259
309
  add_message('error', error_msg)
260
310
  end
261
311
 
262
312
  def parse_fhir_bundle_from_context(context_field_name, context)
263
313
  fhir_bundle = FHIR.from_contents(context[context_field_name].to_json)
264
- unless fhir_bundle
265
- error_msg = "`#{context_field_name}` field is not a FHIR resource: `#{context[context_field_name]}`. " \
266
- "In Context `#{context}`"
314
+ if fhir_bundle.blank?
315
+ error_msg = "#{request_number}`#{context_field_name}` field is not a FHIR resource: " \
316
+ "`#{context[context_field_name]}`. In Context `#{context}`"
267
317
  add_message('error', error_msg)
268
318
  return
269
319
  end
270
320
 
271
321
  return fhir_bundle if fhir_bundle.is_a?(FHIR::Bundle)
272
322
 
273
- error_msg = "Wrong context resource type: Expected `Bundle`, got `#{fhir_bundle.resourceType}`. " \
274
- "In Context `#{context}`"
323
+ error_msg = "#{request_number}Wrong context resource type: Expected `Bundle`, got " \
324
+ "`#{fhir_bundle.resourceType}`. In Context `#{context}`"
275
325
  add_message('error', error_msg)
276
326
  nil
277
327
  end
@@ -283,7 +333,7 @@ module DaVinciCRDTestKit
283
333
  resource_reference_check(reference, 'selections item', supported_resource_types: expected_resource_types)
284
334
  next if order_refs.include?(reference)
285
335
 
286
- error_msg = '`selections` field must reference FHIR resources in `draftOrders`. ' \
336
+ error_msg = "#{request_number}`selections` field must reference FHIR resources in `draftOrders`. " \
287
337
  "#{reference} is not in `draftOrders`. In Context `#{context}`"
288
338
  add_message('error', error_msg)
289
339
  end
@@ -294,7 +344,7 @@ module DaVinciCRDTestKit
294
344
  id_only_fields_check('appointment-book', context, ['patientId'])
295
345
 
296
346
  appointment_bundle = parse_fhir_bundle_from_context('appointments', context)
297
- return unless appointment_bundle
347
+ return if appointment_bundle.blank?
298
348
 
299
349
  expected_resource_types = ['Appointment']
300
350
  bundle_entries_check(context, 'appointments', appointment_bundle, expected_resource_types, 'proposed')
@@ -310,7 +360,7 @@ module DaVinciCRDTestKit
310
360
  id_only_fields_check(hook_name, context, ['patientId'])
311
361
 
312
362
  draft_orders_bundle = parse_fhir_bundle_from_context('draftOrders', context)
313
- return unless draft_orders_bundle
363
+ return if draft_orders_bundle.blank?
314
364
 
315
365
  expected_resource_types = [
316
366
  'DeviceRequest', 'MedicationRequest', 'NutritionOrder',
@@ -352,15 +402,20 @@ module DaVinciCRDTestKit
352
402
  fhir_read(resource_type, resource_id)
353
403
  status = request.response[:status]
354
404
  unless status == 200
355
- add_message('error', "Unexpected response status: expected 200, but received #{status}")
405
+ add_message('error', "#{request_number}Unexpected response status: expected 200, but received #{status}")
356
406
  return
357
407
  end
358
408
  unless resource.resourceType == resource_type
359
- add_message('error', "Unexpected resource type: Expected `#{resource_type}`. Got `#{resource.resourceType}`.")
409
+ add_message('error', %(
410
+ #{request_number}Unexpected resource type: Expected `#{resource_type}`. Got
411
+ `#{resource.resourceType}`.
412
+ ))
360
413
  return
361
414
  end
362
415
  unless resource.id == resource_id
363
- add_message('error', "Requested resource with id #{resource_id}, received resource with id #{resource.id}")
416
+ add_message('error', %(
417
+ #{request_number}Requested resource with id #{resource_id}, received resource with id #{resource.id}
418
+ ))
364
419
  return
365
420
  end
366
421
 
@@ -370,7 +425,7 @@ module DaVinciCRDTestKit
370
425
 
371
426
  def context_validate_optional_fields(hook_context, hook_name)
372
427
  hook_optional_context_fields = context_optional_fields_by_hook[hook_name]
373
- return unless hook_optional_context_fields.present?
428
+ return if hook_optional_context_fields.blank?
374
429
 
375
430
  hook_optional_context_fields.each do |field, type|
376
431
  validate_presence_and_type(hook_context, field, type, "#{hook_name} request context") if hook_context[field]
@@ -394,17 +449,152 @@ module DaVinciCRDTestKit
394
449
  hash_context_fields.each do |field, entry|
395
450
  resource_json = entry.to_json
396
451
  fhir_resource = FHIR.from_contents(resource_json)
397
- unless fhir_resource
398
- add_message('error', "Field `#{field}` is not a FHIR resource.")
452
+ if fhir_resource.blank?
453
+ add_message('error', "#{request_number}Field `#{field}` is not a FHIR resource.")
399
454
  next
400
455
  end
401
456
  resource_type = optional_field_resource_types[field]
402
457
  unless fhir_resource.resourceType == resource_type
403
- add_message('error', "Field `#{field}` must be a `#{resource_type}`. Got `#{fhir_resource.resourceType}`.")
458
+ add_message('error', %(
459
+ #{request_number}Field `#{field}` must be a `#{resource_type}`. Got
460
+ `#{fhir_resource.resourceType}`.
461
+ ))
404
462
  next
405
463
  end
406
464
  resource_is_valid?(resource: fhir_resource)
407
465
  end
408
466
  end
467
+
468
+ def hook_request_prefetch_check(advertised_prefetch_fields, received_prefetch, received_context)
469
+ advertised_prefetch_fields.each do |advertised_prefetch_key, advertised_prefetch_template|
470
+ next if received_prefetch[advertised_prefetch_key].blank?
471
+
472
+ unless received_prefetch[advertised_prefetch_key].is_a?(Hash)
473
+ add_message('error', "#{request_number}Prefetch field `#{advertised_prefetch_key}` is not of type `Hash`.")
474
+ next
475
+ end
476
+
477
+ received_prefetch_resource = FHIR.from_contents(received_prefetch[advertised_prefetch_key].to_json)
478
+
479
+ if advertised_prefetch_template.include?('?')
480
+ advertised_prefetch_fhir_search = advertised_prefetch_template.gsub(/{|}/, '').split('?')
481
+ advertised_prefetch_resource_type = advertised_prefetch_fhir_search.first
482
+
483
+ if advertised_prefetch_resource_type == 'Coverage'
484
+ advertised_coverage_query_params = Rack::Utils.parse_nested_query(advertised_prefetch_fhir_search.last)
485
+
486
+ advertised_patient_token = advertised_coverage_query_params['patient']
487
+ advertised_context_patient_id_key = advertised_patient_token.split('.').last
488
+ received_context_patient_id = received_context[advertised_context_patient_id_key]
489
+
490
+ advertised_status_param = advertised_coverage_query_params['status']
491
+
492
+ validate_prefetch_coverage(received_prefetch_resource, advertised_prefetch_key, received_context_patient_id,
493
+ advertised_status_param)
494
+ end
495
+ else
496
+ advertised_prefetch_token = advertised_prefetch_template.gsub(/{|}/, '').split('/')
497
+ advertised_context_id = advertised_prefetch_token.last.split('.').last
498
+
499
+ if advertised_prefetch_token.length == 1
500
+ received_context_reference = FHIR::Reference.new(reference: received_context[advertised_context_id])
501
+ received_context_resource_type = received_context_reference.resource_type
502
+ received_context_id = received_context_reference.reference_id
503
+ else
504
+ received_context_id = received_context[advertised_context_id]
505
+ received_context_resource_type = advertised_prefetch_token.first
506
+ end
507
+ validate_prefetch_resource(received_prefetch_resource, advertised_prefetch_key,
508
+ received_context_resource_type, received_context_id)
509
+ end
510
+ end
511
+ end
512
+
513
+ def validate_prefetch_coverage(received_resource, advertised_prefetch_key,
514
+ received_context_patient_id, advertised_status)
515
+ unless received_resource.resourceType == 'Bundle'
516
+ add_message('error', %(
517
+ #{request_number}Unexpected resource type: Expected `Bundle`. Got
518
+ `#{received_resource.resourceType}`.
519
+ ))
520
+ return
521
+ end
522
+
523
+ if received_resource.entry.empty?
524
+ add_message('error', "#{request_number}Bundle of coverage resources received from prefetch is empty")
525
+ return
526
+ end
527
+
528
+ coverage_resource = received_resource.entry.first.resource
529
+ unless coverage_resource.resourceType == 'Coverage'
530
+ add_message('error', %(
531
+ #{request_number}Unexpected resource type: Expected `Coverage`. Got
532
+ `#{coverage_resource.resourceType}`.
533
+ ))
534
+ return
535
+ end
536
+
537
+ resource_is_valid?(resource: coverage_resource,
538
+ profile_url: structure_definition_map['Coverage'])
539
+
540
+ coverage_beneficiary_reference = coverage_resource.beneficiary
541
+ coverage_beneficiary_patient_id = coverage_beneficiary_reference.reference_id
542
+ if coverage_beneficiary_patient_id.blank?
543
+ add_message('error', %(
544
+ #{request_number}Could not get beneficiary reference id from `#{advertised_prefetch_key}` field's Coverage
545
+ resource
546
+ ))
547
+ return
548
+ end
549
+
550
+ if coverage_beneficiary_patient_id != received_context_patient_id
551
+ add_message('error', %(
552
+ #{request_number}Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `beneficiary`
553
+ reference id of '#{received_context_patient_id}', instead was '#{coverage_beneficiary_patient_id}'
554
+ ))
555
+ return
556
+ end
557
+
558
+ coverage_status = coverage_resource.status
559
+ return unless coverage_status != advertised_status
560
+
561
+ add_message('error', %(
562
+ #{request_number}Expected `#{advertised_prefetch_key}` field's Coverage resource to have a `status` of
563
+ '#{advertised_status}', instead was '#{coverage_status}'
564
+ ))
565
+ end
566
+
567
+ def validate_prefetch_resource(received_resource, advertised_prefetch_key, context_field_resource_type,
568
+ context_field_id)
569
+ unless received_resource.resourceType == context_field_resource_type
570
+ add_message('error', %(
571
+ #{request_number}Unexpected resource type: Expected `#{context_field_resource_type}`. Got
572
+ `#{received_resource.resourceType}`.
573
+ ))
574
+ return
575
+ end
576
+
577
+ if hook_name == 'order-dispatch'
578
+ resource_is_valid?(resource: received_resource)
579
+ else
580
+ resource_is_valid?(resource: received_resource,
581
+ profile_url: structure_definition_map[context_field_resource_type])
582
+ end
583
+
584
+ received_prefetch_resource_id = received_resource.id
585
+ if received_prefetch_resource_id.blank?
586
+ add_message('error', %(
587
+ #{request_number}#{advertised_prefetch_key}` field's FHIR resource does not contain the `id` field
588
+ ))
589
+ return
590
+ end
591
+
592
+ return unless received_prefetch_resource_id != context_field_id
593
+
594
+ add_message('error', %(
595
+ #{request_number}Expected `#{advertised_prefetch_key}` field's FHIR resource to have an `id` of
596
+ '#{context_field_id}', instead was '#{received_prefetch_resource_id}'
597
+ ))
598
+ end
409
599
  end
410
600
  end