davinci_pas_test_kit 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/presets/g33_br_provider.json +13 -0
- data/config/presets/pas_client_example.json +2 -2
- data/config/presets/pas_client_v201_run_against_pas_server.json +2 -2
- data/config/presets/pas_client_v221_run_against_pas_server.json +3 -3
- data/config/presets/pas_ri.json +6 -6
- data/config/presets/pas_server_v201_run_against_pas_client.json.erb +6 -6
- data/config/presets/pas_server_v221_run_against_pas_client.json.erb +10 -10
- data/lib/davinci_pas_test_kit/client/abstract_approval_submit_test.rb +28 -17
- data/lib/davinci_pas_test_kit/client/abstract_denial_submit_test.rb +26 -16
- data/lib/davinci_pas_test_kit/client/abstract_gather_must_support_test.rb +45 -74
- data/lib/davinci_pas_test_kit/client/abstract_modification_submit_test.rb +29 -19
- data/lib/davinci_pas_test_kit/client/abstract_response_attest.rb +75 -6
- data/lib/davinci_pas_test_kit/client/abstract_subscription_create_test.rb +26 -9
- data/lib/davinci_pas_test_kit/client/abstract_subscription_update_delete_test.rb +91 -0
- data/lib/davinci_pas_test_kit/client/client_bundle_validation_helper.rb +245 -0
- data/lib/davinci_pas_test_kit/client/client_input_descriptions.rb +96 -1
- data/lib/davinci_pas_test_kit/client/endpoints/claim_endpoint.rb +128 -72
- data/lib/davinci_pas_test_kit/client/endpoints/endpoint_session_identification.rb +24 -0
- data/lib/davinci_pas_test_kit/client/endpoints/expired_token.rb +40 -0
- data/lib/davinci_pas_test_kit/client/endpoints/subscription_create_endpoint.rb +7 -12
- data/lib/davinci_pas_test_kit/client/endpoints/subscription_delete_endpoint.rb +13 -0
- data/lib/davinci_pas_test_kit/client/endpoints/subscription_read_endpoint.rb +13 -0
- data/lib/davinci_pas_test_kit/client/endpoints/subscription_status_endpoint.rb +15 -20
- data/lib/davinci_pas_test_kit/client/endpoints/subscription_update_endpoint.rb +13 -0
- data/lib/davinci_pas_test_kit/client/endpoints/token_endpoint.rb +16 -0
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_inquire_request_must_support_beneficiary_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_inquire_response_must_support_beneficiary_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_submit_request_must_support_beneficiary_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/beneficiary/client_submit_response_must_support_beneficiary_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claim_inquiry/client_inquire_request_must_support_claim_inquiry_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claim_update/client_submit_request_must_support_claim_update_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claiminquiryresponse/client_inquire_response_must_support_claiminquiryresponse_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/claimresponse/client_submit_response_must_support_claimresponse_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/communication_request/client_submit_response_must_support_communication_request_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/coverage/client_inquire_request_must_support_coverage_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/coverage/client_submit_request_must_support_coverage_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/encounter/client_submit_request_must_support_encounter_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_inquire_request_must_support_insurer_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_inquire_response_must_support_insurer_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_submit_request_must_support_insurer_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/insurer/client_submit_response_must_support_insurer_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_inquire_must_support_group.rb +1 -1
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_inquire_response_must_support_group.rb +9 -31
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_submit_must_support_group.rb +1 -1
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_client_submit_response_must_support_group.rb +10 -32
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_inquiry_request_bundle/client_inquire_request_must_support_pas_inquiry_request_bundle_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_inquiry_response_bundle/client_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_request_bundle/client_submit_request_must_support_pas_request_bundle_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/pas_response_bundle/client_submit_response_must_support_pas_response_bundle_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_inquire_request_must_support_practitioner_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_inquire_response_must_support_practitioner_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_submit_request_must_support_practitioner_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner/client_submit_response_must_support_practitioner_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_inquire_request_must_support_practitioner_role_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_inquire_response_must_support_practitioner_role_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_submit_request_must_support_practitioner_role_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/practitioner_role/client_submit_response_must_support_practitioner_role_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_inquire_request_must_support_requestor_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_inquire_response_must_support_requestor_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_submit_request_must_support_requestor_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/requestor/client_submit_response_must_support_requestor_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/subscriber/client_inquire_request_must_support_subscriber_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/subscriber/client_submit_request_must_support_subscriber_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/beneficiary/client_inquire_response_must_support_beneficiary_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/beneficiary/client_submit_response_must_support_beneficiary_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claim_inquiry/client_inquire_request_must_support_claim_inquiry_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claim_update/client_submit_request_must_support_claim_update_test.rb +3 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claiminquiryresponse/client_inquire_response_must_support_claiminquiryresponse_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/claimresponse/client_submit_response_must_support_claimresponse_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/communication_request/client_submit_response_must_support_communication_request_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/insurer/client_inquire_response_must_support_insurer_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/insurer/client_submit_response_must_support_insurer_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_inquire_must_support_group.rb +8 -6
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_inquire_response_must_support_group.rb +9 -31
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_submit_must_support_group.rb +13 -9
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_client_submit_response_must_support_group.rb +10 -32
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_inquiry_response_bundle/client_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/pas_response_bundle/client_submit_response_must_support_pas_response_bundle_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner/client_inquire_response_must_support_practitioner_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner/client_submit_response_must_support_practitioner_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner_role/client_inquire_response_must_support_practitioner_role_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/practitioner_role/client_submit_response_must_support_practitioner_role_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/requestor/client_inquire_response_must_support_requestor_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/requestor/client_submit_response_must_support_requestor_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/jobs/send_pas_subscription_notification.rb +64 -3
- data/lib/davinci_pas_test_kit/client/pas_client_subscription_pas_conformance_test.rb +4 -3
- data/lib/davinci_pas_test_kit/client/registration/abstract_configuration_other_display_test.rb +6 -4
- data/lib/davinci_pas_test_kit/client/registration/abstract_configuration_smart_display_test.rb +8 -4
- data/lib/davinci_pas_test_kit/client/registration/abstract_configuration_udap_display_test.rb +9 -4
- data/lib/davinci_pas_test_kit/client/registration/abstract_other_auth_attest_test.rb +5 -4
- data/lib/davinci_pas_test_kit/client/request_count_gating.rb +14 -0
- data/lib/davinci_pas_test_kit/client/response_generator.rb +50 -9
- data/lib/davinci_pas_test_kit/client/session_identification.rb +26 -7
- data/lib/davinci_pas_test_kit/client/subscription_notification_trigger.rb +80 -0
- data/lib/davinci_pas_test_kit/client/subscription_setup_requirement.rb +59 -0
- data/lib/davinci_pas_test_kit/client/user_input_response.rb +50 -10
- data/lib/davinci_pas_test_kit/client/v2.0.1/client_suite.rb +30 -15
- data/lib/davinci_pas_test_kit/client/v2.0.1/must_support/pas_client_must_support_request_profiles_test.rb +2 -2
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_approval_group.rb +8 -7
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_auth_smart_group.rb +4 -4
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_auth_udap_group.rb +4 -4
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_denial_group.rb +8 -7
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_must_support_group.rb +38 -52
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_pended_group.rb +28 -22
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_registration_group.rb +2 -2
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_subscription_setup_group.rb +2 -2
- data/lib/davinci_pas_test_kit/client/v2.0.1/pas_client_workflows_group.rb +2 -2
- data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_inquire_request_bundle_validation_test.rb +15 -21
- data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_inquire_response_bundle_validation_test.rb +29 -29
- data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_pended_submit_test.rb +49 -40
- data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_request_bundle_validation_test.rb +15 -21
- data/lib/davinci_pas_test_kit/client/v2.0.1/workflows/pas_client_response_bundle_validation_test.rb +29 -29
- data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/attestation_instructions.rb +10 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_data_element_expectations_attestation_test.rb +44 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_provider_review_attestation_test.rb +45 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_security_attestation_test.rb +42 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/attestations/client_subscription_error_recovery_attestation_test.rb +42 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/client_suite.rb +64 -12
- data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_operation_failure_submit_test.rb +20 -10
- data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_operation_outcome_validation_test.rb +11 -5
- data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_processing_error_response_validation_test.rb +21 -12
- data/lib/davinci_pas_test_kit/client/v2.2.1/error_handling/pas_client_processing_error_submit_test.rb +19 -8
- data/lib/davinci_pas_test_kit/client/v2.2.1/must_support/must_support_with_attestation_option.rb +90 -40
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_approval_group.rb +12 -11
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_attestations_group.rb +32 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_auth_smart_group.rb +4 -4
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_auth_udap_group.rb +4 -4
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_claim_updates_group.rb +24 -6
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_denial_group.rb +12 -10
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_modification_group.rb +13 -14
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_must_support_group.rb +55 -59
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_operation_failure_group.rb +8 -5
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_pended_group.rb +53 -41
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_processing_error_group.rb +9 -7
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_registration_group.rb +2 -2
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_subscription_setup_group.rb +9 -4
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_subscription_update_delete_group.rb +57 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_workflows_group.rb +22 -2
- data/lib/davinci_pas_test_kit/client/v2.2.1/subscription/pas_client_subscription_create_test.rb +2 -1
- data/lib/davinci_pas_test_kit/client/v2.2.1/subscription/pas_client_subscription_put_update_pas_conformance_test.rb +49 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/subscription/pas_client_subscription_update_delete_test.rb +12 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_claim_update_referenced_resources_test.rb +5 -6
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_claim_update_submit_tests.rb +82 -56
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_inquire_request_bundle_validation_test.rb +17 -20
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_inquire_response_bundle_validation_test.rb +59 -27
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_modification_verification_test.rb +0 -3
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_pended_send_notification_test.rb +138 -0
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_pended_submit_test.rb +32 -85
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_request_bundle_validation_test.rb +16 -21
- data/lib/davinci_pas_test_kit/client/v2.2.1/workflows/pas_client_response_bundle_validation_test.rb +27 -27
- data/lib/davinci_pas_test_kit/cross_suite/fhirpath_utils.rb +13 -3
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/beneficiary/metadata.yml +4 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/claim_inquiry/metadata.yml +15 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/claim_update/metadata.yml +17 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/claiminquiryresponse/metadata.yml +15 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/claimresponse/metadata.yml +15 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/communication_request/metadata.yml +4 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/coverage/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/device_request/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/encounter/metadata.yml +3 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/insurer/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/medication_request/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/metadata.yml +143 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/nutrition_order/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/pas_inquiry_request_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/pas_inquiry_response_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/pas_request_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/pas_response_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/practitioner/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/practitioner_role/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/requestor/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/service_request/metadata.yml +3 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/subscriber/metadata.yml +4 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.0.1/task/metadata.yml +1 -4
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/beneficiary/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/claim_inquiry/metadata.yml +23 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/claim_update/metadata.yml +31 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/claiminquiryresponse/metadata.yml +42 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/claimresponse/metadata.yml +42 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/communication_request/metadata.yml +4 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/coverage/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/device_request/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/encounter/metadata.yml +3 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/insurer/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/medication_request/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/metadata.yml +265 -1
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/nutrition_order/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/pas_inquiry_request_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/pas_inquiry_response_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/pas_request_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/pas_response_bundle/metadata.yml +2 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/practitioner/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/practitioner_role/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/requestor/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/service_request/metadata.yml +3 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/subscriber/metadata.yml +1 -0
- data/lib/davinci_pas_test_kit/cross_suite/generated/v2.2.1/task/metadata.yml +4 -4
- data/lib/davinci_pas_test_kit/cross_suite/pas_bundle_validation.rb +570 -312
- data/lib/davinci_pas_test_kit/cross_suite/pas_constants.rb +13 -0
- data/lib/davinci_pas_test_kit/cross_suite/pas_datatype_constraints.rb +166 -0
- data/lib/davinci_pas_test_kit/cross_suite/pas_subscription_verification.rb +8 -4
- data/lib/davinci_pas_test_kit/cross_suite/referenced_resource_presence_validation.rb +141 -0
- data/lib/davinci_pas_test_kit/cross_suite/response_selection_utils.rb +39 -21
- data/lib/davinci_pas_test_kit/cross_suite/tags.rb +11 -6
- data/lib/davinci_pas_test_kit/cross_suite/validator_configuration.rb +16 -0
- data/lib/davinci_pas_test_kit/cross_suite/validator_suppressions.rb +210 -146
- data/lib/davinci_pas_test_kit/docs/demo/PAS Client Suite Demonstration.postman_collection.json +7 -7
- data/lib/davinci_pas_test_kit/ext/inferno_core/runnable.rb +1 -0
- data/lib/davinci_pas_test_kit/generator/client_must_support_group_generator.rb +47 -16
- data/lib/davinci_pas_test_kit/generator/must_support_target_profiles.rb +17 -0
- data/lib/davinci_pas_test_kit/generator/must_support_test_generator.rb +14 -2
- data/lib/davinci_pas_test_kit/generator/pas_must_support_metadata_extractor.rb +1 -2
- data/lib/davinci_pas_test_kit/generator/profile_metadata.rb +1 -1
- data/lib/davinci_pas_test_kit/generator/server_must_support_group_generator.rb +23 -0
- data/lib/davinci_pas_test_kit/generator/server_suite_generator.rb +8 -7
- data/lib/davinci_pas_test_kit/generator/templates/client_must_support_group.rb.erb +22 -3
- data/lib/davinci_pas_test_kit/generator/templates/client_must_support_response_group.rb.erb +7 -16
- data/lib/davinci_pas_test_kit/generator/templates/must_support.rb.erb +11 -10
- data/lib/davinci_pas_test_kit/generator/templates/server_must_support_group.rb.erb +22 -0
- data/lib/davinci_pas_test_kit/generator/templates/server_suite.rb.erb +9 -11
- data/lib/davinci_pas_test_kit/generator/templates/use_case_group.rb.erb +3 -0
- data/lib/davinci_pas_test_kit/generator/use_case_group_generator.rb +19 -13
- data/lib/davinci_pas_test_kit/parameters_helper.rb +13 -5
- data/lib/davinci_pas_test_kit/pas_notification_conformance_test.rb +2 -2
- data/lib/davinci_pas_test_kit/requirements/davinci_pas_test_kit_requirements.csv +14 -7
- data/lib/davinci_pas_test_kit/requirements/generated/davinci_pas_client_suite_v201_requirements_coverage.csv +21 -21
- data/lib/davinci_pas_test_kit/requirements/generated/davinci_pas_client_suite_v221_requirements_coverage.csv +30 -26
- data/lib/davinci_pas_test_kit/requirements/generated/davinci_pas_server_suite_v221_requirements_coverage.csv +39 -33
- data/lib/davinci_pas_test_kit/requirements/hl7.fhir.us.davinci-pas_2.2.1_requirements.xlsx +0 -0
- data/lib/davinci_pas_test_kit/server/claim_operation_logic.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_inquire_request_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_inquire_response_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_submit_request_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/beneficiary/server_submit_response_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claim_inquiry/server_inquire_request_must_support_claim_inquiry_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claim_update/server_submit_request_must_support_claim_update_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claiminquiryresponse/server_inquire_response_must_support_claiminquiryresponse_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/claimresponse/server_submit_response_must_support_claimresponse_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/communication_request/server_submit_response_must_support_communication_request_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/coverage/server_inquire_request_must_support_coverage_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/coverage/server_submit_request_must_support_coverage_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/encounter/server_submit_request_must_support_encounter_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_inquire_request_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_inquire_response_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_submit_request_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/insurer/server_submit_response_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_inquiry_request_bundle/server_inquire_request_must_support_pas_inquiry_request_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_inquiry_response_bundle/server_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_request_bundle/server_submit_request_must_support_pas_request_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_response_bundle/server_submit_response_must_support_pas_response_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_server_approval_use_case_group.rb +4 -3
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_server_denial_use_case_group.rb +4 -3
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/pas_server_pended_use_case_group.rb +7 -6
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_inquire_request_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_inquire_response_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_submit_request_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner/server_submit_response_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_inquire_request_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_inquire_response_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_submit_request_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/practitioner_role/server_submit_response_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_inquire_request_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_inquire_response_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_submit_request_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/requestor/server_submit_response_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/server_suite.rb +9 -11
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/subscriber/server_inquire_request_must_support_subscriber_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/subscriber/server_submit_request_must_support_subscriber_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/task/server_inquire_response_must_support_task_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.0.1/task/server_submit_response_must_support_task_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_inquire_request_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_inquire_response_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_submit_request_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/beneficiary/server_submit_response_must_support_beneficiary_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claim_inquiry/server_inquire_request_must_support_claim_inquiry_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claim_update/server_submit_request_must_support_claim_update_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claiminquiryresponse/server_inquire_response_must_support_claiminquiryresponse_test.rb +3 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/claimresponse/server_submit_response_must_support_claimresponse_test.rb +3 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/communication_request/server_submit_response_must_support_communication_request_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/coverage/server_inquire_request_must_support_coverage_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/coverage/server_submit_request_must_support_coverage_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/encounter/server_submit_request_must_support_encounter_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_inquire_request_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_inquire_response_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_submit_request_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/insurer/server_submit_response_must_support_insurer_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_inquiry_request_bundle/server_inquire_request_must_support_pas_inquiry_request_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_inquiry_response_bundle/server_inquire_response_must_support_pas_inquiry_response_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_request_bundle/server_submit_request_must_support_pas_request_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_response_bundle/server_submit_response_must_support_pas_response_bundle_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_approval_use_case_group.rb +5 -3
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_denial_use_case_group.rb +5 -3
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_must_support_group.rb +12 -0
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/pas_server_pended_use_case_group.rb +9 -4
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_inquire_request_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_inquire_response_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_submit_request_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner/server_submit_response_must_support_practitioner_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_inquire_request_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_inquire_response_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_submit_request_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/practitioner_role/server_submit_response_must_support_practitioner_role_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_inquire_request_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_inquire_response_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_submit_request_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/requestor/server_submit_response_must_support_requestor_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/server_suite.rb +9 -11
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/subscriber/server_inquire_request_must_support_subscriber_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/subscriber/server_submit_request_must_support_subscriber_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/task/server_inquire_response_must_support_task_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/generated/v2.2.1/task/server_submit_response_must_support_task_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/pas_claim_response_decision_test.rb +2 -2
- data/lib/davinci_pas_test_kit/server/server_request_bundle_validation_test.rb +4 -6
- data/lib/davinci_pas_test_kit/server/server_response_bundle_validation_test.rb +17 -10
- data/lib/davinci_pas_test_kit/server/v2.0.1/must_support/pas_server_must_support_request_profiles_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/v2.0.1/pas_server_subscription_setup.rb +1 -1
- data/lib/davinci_pas_test_kit/server/v2.2.1/claim_inquiry/server_claim_inquiry_response_tests.rb +89 -0
- data/lib/davinci_pas_test_kit/server/v2.2.1/must_support/pas_server_must_support_request_profiles_test.rb +1 -1
- data/lib/davinci_pas_test_kit/server/v2.2.1/pas_error_group.rb +2 -0
- data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_claim_inquiry_responses_group.rb +17 -0
- data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_claim_updates_group.rb +1 -1
- data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_no_custom_extensions_test.rb +241 -0
- data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_subscription_capability_statement_test.rb +40 -0
- data/lib/davinci_pas_test_kit/server/v2.2.1/pas_server_subscription_setup.rb +15 -10
- data/lib/davinci_pas_test_kit/server/v2.2.1/server_replaced_pa_inquire_reference_input_test.rb +63 -0
- data/lib/davinci_pas_test_kit/server/v2.2.1/server_replaced_pa_inquire_reference_test.rb +109 -0
- data/lib/davinci_pas_test_kit/version.rb +2 -2
- metadata +38 -13
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/task/client_inquire_response_must_support_task_test.rb +0 -45
- data/lib/davinci_pas_test_kit/client/generated/v2.0.1/task/client_submit_response_must_support_task_test.rb +0 -45
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/task/client_inquire_response_must_support_task_test.rb +0 -55
- data/lib/davinci_pas_test_kit/client/generated/v2.2.1/task/client_submit_response_must_support_task_test.rb +0 -55
- data/lib/davinci_pas_test_kit/client/v2.2.1/pas_client_error_handling_group.rb +0 -20
|
@@ -3,151 +3,297 @@
|
|
|
3
3
|
require_relative '../parameters_helper'
|
|
4
4
|
require_relative 'validation_test'
|
|
5
5
|
require_relative 'pas_constants'
|
|
6
|
+
require_relative 'pas_datatype_constraints'
|
|
7
|
+
require_relative 'referenced_resource_presence_validation'
|
|
8
|
+
require_relative 'validator_configuration'
|
|
6
9
|
|
|
7
10
|
module DaVinciPASTestKit
|
|
8
11
|
module PasBundleValidation
|
|
9
12
|
include DaVinciPASTestKit::ValidationTest
|
|
13
|
+
include DaVinciPASTestKit::PasDatatypeConstraints
|
|
14
|
+
include DaVinciPASTestKit::ReferencedResourcePresenceValidation
|
|
10
15
|
include ParametersHelper
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'Location' => 'us-core-location',
|
|
32
|
-
'Medication' => 'us-core-medication',
|
|
33
|
-
'MedicationDispense' => 'us-core-medicationdispense',
|
|
34
|
-
'MedicationRequest' => 'us-core-medicationrequest',
|
|
35
|
-
'Organization' => 'us-core-organization',
|
|
36
|
-
'Patient' => 'us-core-patient',
|
|
37
|
-
'Practitioner' => 'us-core-practitioner',
|
|
38
|
-
'PractitionerRole' => 'us-core-practitionerrole',
|
|
39
|
-
'Procedure' => 'us-core-procedure',
|
|
40
|
-
'Provenance' => 'us-core-provenance',
|
|
41
|
-
'QuestionnaireResponse' => 'us-core-questionnaireresponse',
|
|
42
|
-
'RelatedPerson' => 'us-core-relatedperson',
|
|
43
|
-
'ServiceRequest' => 'us-core-servicerequest',
|
|
44
|
-
'Specimen' => 'us-core-specimen'
|
|
45
|
-
}.freeze
|
|
46
|
-
|
|
47
|
-
US_CORE_CONDITION_ENCOUNTER_DIAGNOSIS_PROFILE_ID = 'us-core-condition-encounter-diagnosis'
|
|
48
|
-
US_CORE_CONDITION_PROBLEMS_HEALTH_CONCERNS_PROFILE_ID = 'us-core-condition-problems-health-concerns'
|
|
49
|
-
US_CORE_DIAGNOSTIC_REPORT_LAB_PROFILE_ID = 'us-core-diagnosticreport-lab'
|
|
50
|
-
US_CORE_DIAGNOSTIC_REPORT_NOTE_PROFILE_ID = 'us-core-diagnosticreport-note'
|
|
51
|
-
US_CORE_OBSERVATION_CLINICAL_RESULT_PROFILE_ID = 'us-core-observation-clinical-result'
|
|
52
|
-
US_CORE_OBSERVATION_LAB_PROFILE_ID = 'us-core-observation-lab'
|
|
53
|
-
US_CORE_OBSERVATION_SCREENING_ASSESSMENT_PROFILE_ID = 'us-core-observation-screening-assessment'
|
|
54
|
-
US_CORE_SIMPLE_OBSERVATION_PROFILE_ID = 'us-core-simple-observation'
|
|
55
|
-
US_CORE_SMOKING_STATUS_PROFILE_ID = 'us-core-smokingstatus'
|
|
56
|
-
US_CORE_VITAL_SIGNS_PROFILE_ID = 'us-core-vital-signs'
|
|
57
|
-
|
|
58
|
-
US_CORE_OBSERVATION_CODE_PROFILE_IDS = {
|
|
59
|
-
'11341-5' => 'us-core-observation-occupation',
|
|
60
|
-
'86645-9' => 'us-core-observation-pregnancyintent',
|
|
61
|
-
'82810-3' => 'us-core-observation-pregnancystatus',
|
|
62
|
-
'76690-7' => 'us-core-observation-sexual-orientation',
|
|
63
|
-
'8289-1' => 'head-occipital-frontal-circumference-percentile',
|
|
64
|
-
'59576-9' => 'pediatric-bmi-for-age',
|
|
65
|
-
'77606-2' => 'pediatric-weight-for-height',
|
|
66
|
-
'85354-9' => 'us-core-blood-pressure',
|
|
67
|
-
'39156-5' => 'us-core-bmi',
|
|
68
|
-
'8302-2' => 'us-core-body-height',
|
|
69
|
-
'8310-5' => 'us-core-body-temperature',
|
|
70
|
-
'29463-7' => 'us-core-body-weight',
|
|
71
|
-
'9843-4' => 'us-core-head-circumference',
|
|
72
|
-
'8867-4' => 'us-core-heart-rate',
|
|
73
|
-
'59408-5' => 'us-core-pulse-oximetry',
|
|
74
|
-
'2708-6' => 'us-core-pulse-oximetry',
|
|
75
|
-
'9279-1' => 'us-core-respiratory-rate',
|
|
76
|
-
'72166-2' => 'us-core-smokingstatus'
|
|
77
|
-
}.freeze
|
|
78
|
-
|
|
79
|
-
def validation_error_messages
|
|
80
|
-
@validation_error_messages ||= []
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def perform_bundle_validation(bundle, operation, type, ig_version)
|
|
17
|
+
###########################################################################
|
|
18
|
+
# Public API
|
|
19
|
+
###########################################################################
|
|
20
|
+
|
|
21
|
+
# @return [Array<Hash>] The messages (of every severity) found while validating this
|
|
22
|
+
# bundle, each a { type:, message: } hash. Callers that only care about failure should
|
|
23
|
+
# check for a message with type: 'error' rather than just whether this is present -
|
|
24
|
+
# see validation_messages.
|
|
25
|
+
# @param validator [Symbol] the name of the FHIR resource validator to use for profile
|
|
26
|
+
# validation. Use perform_no_custom_extensions_validation for Bundles Inferno sends.
|
|
27
|
+
# @param echoed_from [FHIR::Bundle, String, nil] the request Bundle the response answers. When
|
|
28
|
+
# given, entry resources echoed back from it that carry no extensions beyond those they had in
|
|
29
|
+
# it are validated with the :default validator rather than `validator`, since the extensions
|
|
30
|
+
# came from the system under test.
|
|
31
|
+
def perform_bundle_validation(bundle, operation, type, ig_version, request_bundle = nil, validator: :default,
|
|
32
|
+
echoed_from: nil)
|
|
33
|
+
@validation_messages = []
|
|
34
|
+
@profile_validator = validator
|
|
35
|
+
@echoed_from_bundle = parse_bundle(echoed_from)
|
|
84
36
|
target_profile = PASConstants.bundle_profile_url_for_operation_and_type(operation, type)
|
|
85
37
|
request_type = "#{operation}_#{type}"
|
|
86
38
|
if type == 'request'
|
|
87
39
|
perform_request_validation(bundle, target_profile, ig_version.delete_prefix('v'), request_type)
|
|
88
40
|
else
|
|
89
|
-
perform_response_validation(bundle, target_profile, ig_version.delete_prefix('v'), request_type)
|
|
41
|
+
perform_response_validation(bundle, target_profile, ig_version.delete_prefix('v'), request_type, request_bundle)
|
|
90
42
|
end
|
|
43
|
+
validation_messages
|
|
91
44
|
end
|
|
92
45
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
46
|
+
# Validates a Bundle that Inferno sends to the system under test, which may be tester-provided,
|
|
47
|
+
# using NO_CUSTOM_EXTENSIONS_VALIDATOR so that custom extensions aren't allowed. Resources echoed
|
|
48
|
+
# back from the system under test's Bundle may keep the extensions they had in it.
|
|
49
|
+
# @param type [String] 'request' or 'response' - see perform_bundle_validation.
|
|
50
|
+
# @param echoed_from [FHIR::Bundle, String, nil] the Bundle from the system under test that this
|
|
51
|
+
# one answers, e.g. the request for a response, or nil if there is none - see
|
|
52
|
+
# perform_bundle_validation.
|
|
53
|
+
# @return [Array<Hash>] see perform_bundle_validation.
|
|
54
|
+
def perform_no_custom_extensions_validation(bundle, operation, type, ig_version, echoed_from:)
|
|
55
|
+
perform_bundle_validation(bundle, operation, type, ig_version,
|
|
56
|
+
validator: NO_CUSTOM_EXTENSIONS_VALIDATOR, echoed_from:)
|
|
96
57
|
end
|
|
97
58
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
59
|
+
# Validates a Parameters resource that Inferno sends to wrap Bundles (e.g., a v2.2.1 $inquire
|
|
60
|
+
# response) against the base Parameters definition, since PAS doesn't profile it, using
|
|
61
|
+
# NO_CUSTOM_EXTENSIONS_VALIDATOR. Issues within the wrapped resources are dropped, since those are
|
|
62
|
+
# validated separately - see perform_no_custom_extensions_validation.
|
|
63
|
+
# @param parameters [FHIR::Parameters]
|
|
64
|
+
# @return [Array<Hash>] the messages found, each a { type:, message: } hash.
|
|
65
|
+
def perform_no_custom_extensions_parameters_validation(parameters)
|
|
66
|
+
response_details = []
|
|
67
|
+
resource_is_valid?(resource: parameters, validator: NO_CUSTOM_EXTENSIONS_VALIDATOR,
|
|
68
|
+
add_messages_to_runnable: false,
|
|
69
|
+
validator_response_details: response_details)
|
|
70
|
+
validator_issues_to_messages(response_details.reject do |issue|
|
|
71
|
+
issue.filtered || issue.location&.match?(/\AParameters\.parameter\[\d+\](\.part\[\d+\])*\.resource/)
|
|
72
|
+
end)
|
|
101
73
|
end
|
|
102
74
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
75
|
+
###########################################################################
|
|
76
|
+
# Internal Validation Methods
|
|
77
|
+
###########################################################################
|
|
78
|
+
|
|
79
|
+
# Every message (of every severity, not just errors) collected while validating the
|
|
80
|
+
# current bundle. Passing-profile messages and per-issue profile-conformance messages
|
|
81
|
+
# (see validate_bundle_entries_against_profiles) may be warnings or info, not just
|
|
82
|
+
# errors, which is why this isn't error-only despite most entries being added via
|
|
83
|
+
# add_validation_error.
|
|
84
|
+
def validation_messages
|
|
85
|
+
@validation_messages ||= []
|
|
86
|
+
end
|
|
107
87
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
88
|
+
# Appends a plain error string to validation_messages as a { type:, message: } hash,
|
|
89
|
+
# for the many checks in this file that only ever produce errors (as opposed to
|
|
90
|
+
# validate_bundle_entries_against_profiles, which already has typed messages from the
|
|
91
|
+
# validator and concats them directly).
|
|
92
|
+
def add_validation_error(message)
|
|
93
|
+
validation_messages << { type: 'error', message: }
|
|
94
|
+
end
|
|
113
95
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
96
|
+
# The name of the FHIR resource validator used for profile validation of the current
|
|
97
|
+
# bundle - see perform_bundle_validation.
|
|
98
|
+
def profile_validator
|
|
99
|
+
@profile_validator || :default
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# How to validate one entry resource in the current bundle. Resources echoed back from the request
|
|
103
|
+
# (see perform_bundle_validation's echoed_from) may keep the extensions they had in it: those that
|
|
104
|
+
# add no extensions of their own use the :default validator, and the others use the specified
|
|
105
|
+
# validator with the issues it reports about their extensions explained or dropped - see
|
|
106
|
+
# echoed_extension_issue_messages.
|
|
107
|
+
# @param full_url [String] the fullUrl of the entry containing the resource.
|
|
108
|
+
# @param resource [FHIR::Model] the entry resource.
|
|
109
|
+
# @return [Array(Symbol, Array, FHIR::Bundle::Entry)] the name of the validator to use, the
|
|
110
|
+
# [element path, extension] pairs (see extensions_in) of the request resource the resource echoes,
|
|
111
|
+
# and the request entry it echoes. The last two are nil if it doesn't echo one (or the validator is
|
|
112
|
+
# :default, which allows any extension).
|
|
113
|
+
def entry_validation_settings(full_url, resource)
|
|
114
|
+
return [profile_validator, nil, nil] if profile_validator == :default
|
|
115
|
+
|
|
116
|
+
request_entry = echoed_request_entry(full_url, resource)
|
|
117
|
+
return [profile_validator, nil, nil] if request_entry.blank?
|
|
118
|
+
|
|
119
|
+
echoed_extensions = extensions_in(validated_hash(request_entry.resource))
|
|
120
|
+
all_echoed = extensions_in(validated_hash(resource)).all? { |extension| echoed_extensions.include?(extension) }
|
|
121
|
+
[all_echoed ? :default : profile_validator, echoed_extensions, request_entry]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def echoed_request_entry(full_url, resource)
|
|
125
|
+
return if @echoed_from_bundle.blank?
|
|
126
|
+
|
|
127
|
+
response_entry = FHIR::Bundle::Entry.new(fullUrl: full_url, resource:)
|
|
128
|
+
@echoed_from_bundle.entry.find { |entry| echoed_resource?(entry, response_entry) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# The resource as the validator receives it (Inferno sends its source_contents), as a hash. Unlike
|
|
132
|
+
# to_hash, this keeps the extensions on primitive elements (e.g., `_birthDate`).
|
|
133
|
+
def validated_hash(resource)
|
|
134
|
+
JSON.parse(resource.source_contents)
|
|
135
|
+
rescue StandardError
|
|
136
|
+
resource.to_hash
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# An extension is echoed if it appears, with the same content and at the same element path, in the
|
|
140
|
+
# request resource. Paths leave out array indexes so reordered repeating elements still match.
|
|
141
|
+
# Reference values are ignored because Inferno rewrites them when it echoes resources in responses
|
|
142
|
+
# it generates.
|
|
143
|
+
# @return [Array<Array(String, Hash)>] [element path, extension] pairs for every extension (at any
|
|
144
|
+
# depth, including modifierExtensions) in the value.
|
|
145
|
+
def extensions_in(value, path = '')
|
|
146
|
+
case value
|
|
147
|
+
when Hash
|
|
148
|
+
value.flat_map do |key, child|
|
|
149
|
+
child_path = path.empty? ? key : "#{path}.#{key}"
|
|
150
|
+
own = if %w[extension modifierExtension].include?(key)
|
|
151
|
+
Array(child).map { |extension| [child_path, without_references(extension)] }
|
|
152
|
+
else
|
|
153
|
+
[]
|
|
154
|
+
end
|
|
155
|
+
own + extensions_in(child, child_path)
|
|
126
156
|
end
|
|
157
|
+
when Array
|
|
158
|
+
value.flat_map { |child| extensions_in(child, path) }
|
|
127
159
|
else
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
160
|
+
[]
|
|
161
|
+
end
|
|
162
|
+
end
|
|
131
163
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
164
|
+
def without_references(value)
|
|
165
|
+
case value
|
|
166
|
+
when Hash
|
|
167
|
+
value.except('reference').transform_values { |child| without_references(child) }
|
|
168
|
+
when Array
|
|
169
|
+
value.map { |child| without_references(child) }
|
|
170
|
+
else
|
|
171
|
+
value
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# An info message identifying an echoed resource, which of its identifying fields matched the request
|
|
176
|
+
# entry it echoes (see echoed_resource?), and the extensions it is allowed to keep because they are
|
|
177
|
+
# unchanged from the request - see entry_validation_settings.
|
|
178
|
+
def echoed_resource_message(full_url, resource, echoed_extensions, request_entry)
|
|
179
|
+
kept_urls = extensions_in(validated_hash(resource))
|
|
180
|
+
.select { |extension| echoed_extensions.include?(extension) }
|
|
181
|
+
.map { |_path, extension| extension['url'] }.uniq
|
|
182
|
+
message = "#{resource.resourceType} #{full_url} echoes the #{resource.resourceType} in the request " \
|
|
183
|
+
"(#{echo_match_details(full_url, resource, request_entry)})"
|
|
184
|
+
message += if kept_urls.present?
|
|
185
|
+
", so these extensions unchanged from the request are allowed in it: #{kept_urls.join(', ')}."
|
|
186
|
+
else
|
|
187
|
+
'.'
|
|
188
|
+
end
|
|
189
|
+
{ type: 'info', message: }
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Which identifying fields of an echoed resource match the request entry it echoes and which differ,
|
|
193
|
+
# e.g., "matched by id; fullUrl differs". Fields blank in both are left out.
|
|
194
|
+
def echo_match_details(full_url, resource, request_entry)
|
|
195
|
+
fields = {
|
|
196
|
+
'fullUrl' => [request_entry.fullUrl, full_url],
|
|
197
|
+
'id' => [request_entry.resource.id, resource.id],
|
|
198
|
+
'identifiers' => [identifiers_of(request_entry.resource), identifiers_of(resource)]
|
|
199
|
+
}.reject { |_name, values| values.all?(&:blank?) }
|
|
200
|
+
matched, differing = fields.keys.partition do |name|
|
|
201
|
+
request_value, response_value = fields[name]
|
|
202
|
+
request_value == response_value
|
|
203
|
+
end
|
|
204
|
+
details = "matched by #{matched.to_sentence}"
|
|
205
|
+
details += "; #{differing.to_sentence} #{differing.one? ? 'differs' : 'differ'}" if differing.present?
|
|
206
|
+
details
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Messages for the validator issues about an echoed resource. An issue that an extension isn't allowed
|
|
210
|
+
# (see unknown_extension_issue?) is:
|
|
211
|
+
# - dropped if the extension is echoed, i.e., in echoed_extensions.
|
|
212
|
+
# - reworded otherwise, to say whether the request resource has an extension with that url at the
|
|
213
|
+
# same element, but with different content, or doesn't.
|
|
214
|
+
# Other issues are kept as is.
|
|
215
|
+
# @param resource_hash [Hash] the validated resource, as a hash.
|
|
216
|
+
def echoed_extension_issue_messages(issues, resource_hash, echoed_extensions)
|
|
217
|
+
issues.filter_map do |issue|
|
|
218
|
+
path, extension = extension_at_location(resource_hash, issue.location)
|
|
219
|
+
url = extension&.dig('url')
|
|
220
|
+
next validator_issue_message(issue) unless unknown_extension_issue?(issue, url)
|
|
221
|
+
next if echoed_extensions.include?([path, without_references(extension)])
|
|
222
|
+
|
|
223
|
+
details =
|
|
224
|
+
if echoed_extensions.any? { |echoed_path, echoed| echoed_path == path && echoed['url'] == url }
|
|
225
|
+
"echoed extension #{url} contained a different value than in the source resource."
|
|
226
|
+
else
|
|
227
|
+
"extension #{url} in an echoed resource is not in the source resource at this element."
|
|
228
|
+
end
|
|
229
|
+
validator_issue_message(issue, details:)
|
|
137
230
|
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# True if the issue is the validator's error for an extension it doesn't know, so doesn't allow
|
|
234
|
+
# (Extension_EXT_Unknown_NotHere), as the strict validator reports for custom extensions. Other
|
|
235
|
+
# issues about extensions, e.g., known extensions with invalid values, are reported as is.
|
|
236
|
+
def unknown_extension_issue?(issue, url)
|
|
237
|
+
url.present? && issue.raw_issue['message'] == "The extension #{url} could not be found so is not allowed here"
|
|
238
|
+
end
|
|
138
239
|
|
|
139
|
-
|
|
140
|
-
|
|
240
|
+
# True if the issue is the :default validator's note that an echoed extension is unknown
|
|
241
|
+
# (Extension_EXT_Unknown) - see echoed_resource_message.
|
|
242
|
+
def echoed_unknown_extension_note?(issue, resource_hash, echoed_extensions)
|
|
243
|
+
path, extension = extension_at_location(resource_hash, issue.location)
|
|
244
|
+
url = extension&.dig('url')
|
|
245
|
+
url.present? && issue.raw_issue['message'] == "Unknown extension #{url}" &&
|
|
246
|
+
echoed_extensions.include?([path, without_references(extension)])
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Resolves a validator issue location such as `Patient.name[0].extension[1]` against the resource.
|
|
250
|
+
# Extensions on primitive elements are located by the element's name (e.g.,
|
|
251
|
+
# `Patient.birthDate.extension[0]`) but held in its `_` sibling (e.g., `_birthDate`) in JSON.
|
|
252
|
+
# @return [Array(String, Hash), nil] the extension's index-free element path (as in extensions_in)
|
|
253
|
+
# and the extension, or nil if the location isn't an extension in the resource.
|
|
254
|
+
def extension_at_location(resource_hash, location)
|
|
255
|
+
value = resource_hash
|
|
256
|
+
names = location.to_s.split('.').drop(1).map do |segment|
|
|
257
|
+
name, index = segment.match(/\A(\w+)(?:\[(\d+)\])?\z/)&.captures
|
|
258
|
+
return nil if name.nil? || !value.is_a?(Hash)
|
|
259
|
+
|
|
260
|
+
name = "_#{name}" if primitive_element?(value, name)
|
|
261
|
+
value = value[name]
|
|
262
|
+
value = value[index.to_i] if index.present? && value.is_a?(Array)
|
|
263
|
+
name
|
|
141
264
|
end
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
265
|
+
return unless %w[extension modifierExtension].include?(names.last) && value.is_a?(Hash)
|
|
266
|
+
|
|
267
|
+
[names.join('.'), value]
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# True if the element is a primitive (or repeating primitive) with a `_` sibling holding its
|
|
271
|
+
# extensions, in the JSON object `parent`.
|
|
272
|
+
def primitive_element?(parent, name)
|
|
273
|
+
parent.key?("_#{name}") && Array.wrap(parent[name]).none?(Hash)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def parse_bundle(bundle)
|
|
277
|
+
bundle = FHIR.from_contents(bundle) if bundle.is_a?(String)
|
|
278
|
+
bundle if bundle.is_a?(FHIR::Bundle)
|
|
279
|
+
rescue StandardError
|
|
280
|
+
nil
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def perform_request_validation(bundle, profile_url, version, request_type)
|
|
284
|
+
validate_pa_request_payload_structure(bundle, request_type)
|
|
285
|
+
validate_resources_conformance_against_profile(bundle, profile_url, version, request_type)
|
|
286
|
+
end
|
|
147
287
|
|
|
148
|
-
|
|
288
|
+
def perform_response_validation(response_bundle, profile_url, version, request_type, request_bundle = nil)
|
|
289
|
+
validate_pa_response_body_structure(response_bundle, request_bundle) if request_type.start_with?('submit')
|
|
290
|
+
validate_resources_conformance_against_profile(response_bundle, profile_url, version, request_type)
|
|
149
291
|
end
|
|
150
292
|
|
|
293
|
+
###########################################################################
|
|
294
|
+
# Structure Validation
|
|
295
|
+
###########################################################################
|
|
296
|
+
|
|
151
297
|
# Validates the structure of a Prior Authorization (PA) request Bundle.
|
|
152
298
|
#
|
|
153
299
|
# @param bundle [FHIR::Bundle] The FHIR Bundle of the PA request.
|
|
@@ -165,18 +311,22 @@ module DaVinciPASTestKit
|
|
|
165
311
|
base_url = extract_base_url(bundle.entry.first&.fullUrl)
|
|
166
312
|
|
|
167
313
|
check_presence_of_referenced_resources(first_entry, base_url, bundle.entry)
|
|
314
|
+
.each { |msg| add_validation_error(msg) }
|
|
168
315
|
|
|
169
|
-
|
|
316
|
+
# request_type is the compound 'submit_request' / 'inquire_request' built by
|
|
317
|
+
# perform_bundle_validation - start_with? also accepts a bare 'submit', consistent with
|
|
318
|
+
# find_profile_url and validate_resources_conformance_against_profile below.
|
|
319
|
+
if request_type.start_with?('submit')
|
|
170
320
|
unless first_entry.is_a?(FHIR::Claim)
|
|
171
|
-
|
|
321
|
+
add_validation_error("[Bundle/#{bundle.id}]: The first bundle entry must be a Claim")
|
|
172
322
|
end
|
|
173
323
|
|
|
174
324
|
validate_uniqueness_of_supporting_info_sequences(first_entry)
|
|
175
325
|
validate_bundle_entries_full_url(bundle)
|
|
176
326
|
else
|
|
177
|
-
claim_resource = bundle_entry_resources.find { |resource| resource
|
|
327
|
+
claim_resource = bundle_entry_resources.find { |resource| resource&.resourceType == 'Claim' }
|
|
178
328
|
if claim_resource.blank?
|
|
179
|
-
|
|
329
|
+
add_validation_error("[Bundle/#{bundle.id}]: Claim must be present for inquiry request")
|
|
180
330
|
end
|
|
181
331
|
|
|
182
332
|
# The inquiry operation must contain a requesting provider organization,
|
|
@@ -186,16 +336,15 @@ module DaVinciPASTestKit
|
|
|
186
336
|
payer_reference = claim_resource&.insurer&.reference
|
|
187
337
|
|
|
188
338
|
if patient_reference.blank?
|
|
189
|
-
|
|
190
|
-
"[Bundle/#{bundle.id}]: The Claim for inquiry operation must reference a patient."
|
|
339
|
+
add_validation_error("[Bundle/#{bundle.id}]: The Claim for inquiry operation must reference a patient.")
|
|
191
340
|
end
|
|
192
341
|
if provider_reference.blank?
|
|
193
|
-
|
|
194
|
-
|
|
342
|
+
add_validation_error("[Bundle/#{bundle.id}]: The claim for inquiry operation must reference " \
|
|
343
|
+
'a requesting provider organization.')
|
|
195
344
|
end
|
|
196
345
|
if payer_reference.blank?
|
|
197
|
-
|
|
198
|
-
|
|
346
|
+
add_validation_error("[Bundle/#{bundle.id}]: The Claim for inquiry operation must contain " \
|
|
347
|
+
'a payer organization.')
|
|
199
348
|
end
|
|
200
349
|
end
|
|
201
350
|
end
|
|
@@ -203,7 +352,7 @@ module DaVinciPASTestKit
|
|
|
203
352
|
# Validates the response body structure of a Prior Authorization (PA) response.
|
|
204
353
|
#
|
|
205
354
|
# @param pa_response_bundle [FHIR::Bundle] The FHIR bundle representing the PA response.
|
|
206
|
-
# @param pa_request_bundle [String] The
|
|
355
|
+
# @param pa_request_bundle [FHIR::Bundle, String, nil] The submitted PA request Bundle, either parsed or JSON.
|
|
207
356
|
#
|
|
208
357
|
# This method performs validation of the PA response bundle structure.
|
|
209
358
|
# It follows the PAS IG requirement that the FHIR Bundle generated
|
|
@@ -214,34 +363,74 @@ module DaVinciPASTestKit
|
|
|
214
363
|
# a given combination of content. Resources echoed back from the request are validated
|
|
215
364
|
# to ensure the same fullUrl and resource identifiers as in the
|
|
216
365
|
# request are used.
|
|
217
|
-
def validate_pa_response_body_structure(pa_response_bundle,
|
|
218
|
-
first_entry = pa_response_bundle.entry.first
|
|
366
|
+
def validate_pa_response_body_structure(pa_response_bundle, pa_request_bundle)
|
|
367
|
+
first_entry = pa_response_bundle.entry.first&.resource
|
|
219
368
|
unless first_entry.is_a?(FHIR::ClaimResponse)
|
|
220
|
-
|
|
221
|
-
"[Bundle/#{pa_response_bundle.id}]: The first bundle entry must be a ClaimResponse"
|
|
369
|
+
add_validation_error("[Bundle/#{pa_response_bundle.id}]: The first bundle entry must be a ClaimResponse")
|
|
222
370
|
end
|
|
223
371
|
|
|
224
372
|
base_url = extract_base_url(pa_response_bundle.entry.last&.fullUrl)
|
|
225
373
|
check_presence_of_referenced_resources(first_entry, base_url, pa_response_bundle.entry)
|
|
374
|
+
.each { |msg| add_validation_error(msg) }
|
|
375
|
+
|
|
376
|
+
validate_echoed_response_resources(pa_response_bundle, pa_request_bundle)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def validate_echoed_response_resources(pa_response_bundle, pa_request_bundle)
|
|
380
|
+
return if pa_request_bundle.blank?
|
|
381
|
+
|
|
382
|
+
request_bundle = pa_request_bundle.is_a?(FHIR::Bundle) ? pa_request_bundle : FHIR.from_contents(pa_request_bundle)
|
|
383
|
+
return unless request_bundle.is_a?(FHIR::Bundle)
|
|
384
|
+
|
|
385
|
+
pa_response_bundle.entry.each do |response_entry|
|
|
386
|
+
response_resource = response_entry.resource
|
|
387
|
+
next if response_resource.blank?
|
|
388
|
+
|
|
389
|
+
request_entry = request_bundle.entry.find do |entry|
|
|
390
|
+
echoed_resource?(entry, response_entry)
|
|
391
|
+
end
|
|
392
|
+
next if request_entry.blank?
|
|
393
|
+
|
|
394
|
+
next if echoed_resource_identifiers_match?(request_entry, response_entry)
|
|
395
|
+
|
|
396
|
+
add_validation_error(resource_present_in_pa_request_and_response_msg(response_resource))
|
|
397
|
+
end
|
|
398
|
+
rescue StandardError
|
|
399
|
+
add_validation_error('Unable to compare PAS request and response Bundle resources for echoed identifiers.')
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def echoed_resource?(request_entry, response_entry)
|
|
403
|
+
request_resource = request_entry.resource
|
|
404
|
+
response_resource = response_entry.resource
|
|
405
|
+
return false if request_resource.blank? || response_resource.blank?
|
|
406
|
+
return false unless request_resource.resourceType == response_resource.resourceType
|
|
407
|
+
|
|
408
|
+
# True if any identifying field is present on both sides and matches, so we can later report if any
|
|
409
|
+
# do not have all matching identifiers. Blank fields do not identify echoed resources.
|
|
410
|
+
(request_entry.fullUrl.present? && request_entry.fullUrl == response_entry.fullUrl) ||
|
|
411
|
+
(request_resource.id.present? && request_resource.id == response_resource.id) ||
|
|
412
|
+
(identifiers_of(request_resource).present? &&
|
|
413
|
+
identifiers_of(request_resource) == identifiers_of(response_resource))
|
|
414
|
+
end
|
|
226
415
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
# pa_request_bundle = FHIR.from_contents(pa_request_bundle)
|
|
231
|
-
# pa_response_bundle.entry.each do |entry|
|
|
232
|
-
# res = entry.resource
|
|
233
|
-
# request_entry = pa_request_bundle.entry.find do |ent|
|
|
234
|
-
# ent.resource.resourceType == res.resourceType && ent.resource.id == res.id
|
|
235
|
-
# end
|
|
236
|
-
# next unless request_entry.present?
|
|
416
|
+
def echoed_resource_identifiers_match?(request_entry, response_entry)
|
|
417
|
+
request_resource = request_entry.resource
|
|
418
|
+
response_resource = response_entry.resource
|
|
237
419
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# )
|
|
242
|
-
# end
|
|
420
|
+
request_entry.fullUrl == response_entry.fullUrl &&
|
|
421
|
+
request_resource.id == response_resource.id &&
|
|
422
|
+
identifiers_of(request_resource) == identifiers_of(response_resource)
|
|
243
423
|
end
|
|
244
424
|
|
|
425
|
+
# Nil for resource types without an identifier element (e.g., Binary, Parameters).
|
|
426
|
+
def identifiers_of(resource)
|
|
427
|
+
resource.identifier if resource.respond_to?(:identifier)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
###########################################################################
|
|
431
|
+
# Bundle entry profile validation
|
|
432
|
+
###########################################################################
|
|
433
|
+
|
|
245
434
|
# Profile conformance of Prior Authorization (PA) resources.
|
|
246
435
|
#
|
|
247
436
|
# @param bundle [FHIR::Bundle] The FHIR bundle representing the PA request/response.
|
|
@@ -259,7 +448,7 @@ module DaVinciPASTestKit
|
|
|
259
448
|
bundle_entry = bundle.entry
|
|
260
449
|
|
|
261
450
|
root_entry = bundle_entry.find do |entry|
|
|
262
|
-
['Claim', 'ClaimResponse'].include?(entry.resource
|
|
451
|
+
['Claim', 'ClaimResponse'].include?(entry.resource&.resourceType)
|
|
263
452
|
end
|
|
264
453
|
|
|
265
454
|
if root_entry.present?
|
|
@@ -299,34 +488,63 @@ module DaVinciPASTestKit
|
|
|
299
488
|
end
|
|
300
489
|
|
|
301
490
|
# Validates bundle resource and each entry in the bundle against its target profiles.
|
|
302
|
-
#
|
|
303
|
-
#
|
|
491
|
+
# Validation messages are collected per profile rather than logged directly, so a
|
|
492
|
+
# resource with multiple candidate profiles only reports errors when it fails all of
|
|
493
|
+
# them. When a profile passes, only that profile's (non-error) messages are kept. Either
|
|
494
|
+
# way, messages go into validation_messages rather than straight to the runnable's
|
|
495
|
+
# `messages` - see perform_bundle_validation - so callers see every message this method
|
|
496
|
+
# finds, not just the summary line for a resource that conformed to nothing.
|
|
304
497
|
# @param version [String] The version of the IG.
|
|
305
498
|
def validate_bundle_entries_against_profiles(version)
|
|
306
499
|
bundle_resources_target_profile_map.each do |key, item|
|
|
307
500
|
resource = item[:resource]
|
|
308
501
|
base_profile = FHIR::Definitions.resource_definition(resource.resourceType).url
|
|
502
|
+
unless key == 'bundle'
|
|
503
|
+
validator, echoed_extensions, request_entry = entry_validation_settings(key, resource)
|
|
504
|
+
if request_entry.present?
|
|
505
|
+
validation_messages << echoed_resource_message(key, resource, echoed_extensions, request_entry)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
messages_by_profile = {}
|
|
309
509
|
|
|
310
510
|
success_profile = item[:profile_urls].find do |url|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
511
|
+
profile_messages =
|
|
512
|
+
if key == 'bundle'
|
|
513
|
+
collect_bundle_profile_messages(resource, profile_url_for_validation(url, base_profile, version))
|
|
514
|
+
elsif url == BASE_R4_PROFILE
|
|
515
|
+
collect_resource_profile_messages(resource, validator:, echoed_extensions:)
|
|
516
|
+
else
|
|
517
|
+
collect_resource_profile_messages(resource, profile_url_for_validation(url, base_profile, version),
|
|
518
|
+
validator:, echoed_extensions:) +
|
|
519
|
+
datatype_constraint_messages(resource, profile_url_without_version(url), version)
|
|
520
|
+
end
|
|
521
|
+
messages_by_profile[url] = profile_messages
|
|
522
|
+
profile_messages.none? { |message| message[:type] == 'error' }
|
|
320
523
|
end
|
|
321
524
|
|
|
322
|
-
|
|
525
|
+
if success_profile
|
|
526
|
+
validation_messages.concat(messages_by_profile[success_profile])
|
|
527
|
+
else
|
|
528
|
+
messages_by_profile.each_value { |profile_messages| validation_messages.concat(profile_messages) }
|
|
529
|
+
add_validation_error(generate_non_conformance_message(item))
|
|
530
|
+
end
|
|
323
531
|
end
|
|
324
532
|
end
|
|
325
533
|
|
|
326
534
|
def profile_url_for_validation(url, base_profile, version)
|
|
327
535
|
url = url.to_s
|
|
328
536
|
return url if url.include?('|') || profile_url_without_version(url) == base_profile
|
|
329
|
-
|
|
537
|
+
|
|
538
|
+
if us_core_profile_url?(url)
|
|
539
|
+
profile_id = url.delete_prefix("#{US_CORE_PROFILE_BASE}/")
|
|
540
|
+
# US Core 3.1.1's single Condition profile was split by category in later versions,
|
|
541
|
+
# so only 3.1.1 defines it.
|
|
542
|
+
return "#{url}|#{PASConstants::US_CORE_VERSION_3_1_1}" if profile_id == US_CORE_CONDITION_PROFILE_ID
|
|
543
|
+
|
|
544
|
+
# A declared US Core profile gets the selected version only when that version defines it;
|
|
545
|
+
# otherwise it stays unversioned so the validator resolves it to a version that does.
|
|
546
|
+
return us_core_profile_available?(profile_id) ? "#{url}|#{us_core_version}" : url
|
|
547
|
+
end
|
|
330
548
|
|
|
331
549
|
"#{url}|#{version}"
|
|
332
550
|
end
|
|
@@ -365,6 +583,12 @@ module DaVinciPASTestKit
|
|
|
365
583
|
version.to_s.delete_prefix('v').match?(/\A2\.2(\.|\z)/)
|
|
366
584
|
end
|
|
367
585
|
|
|
586
|
+
# The US Core version whose profiles the fallback validates against: the session's
|
|
587
|
+
# US Core Version suite option when the suite offers it, otherwise 6.1.0.
|
|
588
|
+
def us_core_version
|
|
589
|
+
suite_options[:us_core_version].presence || PASConstants::DEFAULT_US_CORE_VERSION
|
|
590
|
+
end
|
|
591
|
+
|
|
368
592
|
def us_core_profile_urls_for_resource(resource)
|
|
369
593
|
profile_ids =
|
|
370
594
|
case resource.resourceType
|
|
@@ -378,10 +602,20 @@ module DaVinciPASTestKit
|
|
|
378
602
|
Array(US_CORE_SINGLE_PROFILE_IDS_BY_RESOURCE[resource.resourceType])
|
|
379
603
|
end
|
|
380
604
|
|
|
381
|
-
us_core_profile_urls(profile_ids)
|
|
605
|
+
profile_urls = us_core_profile_urls(profile_ids.select { |profile_id| us_core_profile_available?(profile_id) })
|
|
606
|
+
# US Core 3.1.1 has no Simple Observation profile, so the base resource takes its place as the
|
|
607
|
+
# catch-all at the end of the Observation candidates.
|
|
608
|
+
if profile_ids.include?(US_CORE_SIMPLE_OBSERVATION_PROFILE_ID) &&
|
|
609
|
+
!us_core_profile_available?(US_CORE_SIMPLE_OBSERVATION_PROFILE_ID)
|
|
610
|
+
profile_urls << BASE_R4_PROFILE
|
|
611
|
+
end
|
|
612
|
+
profile_urls
|
|
382
613
|
end
|
|
383
614
|
|
|
384
615
|
def us_core_condition_profile_ids(resource)
|
|
616
|
+
# US Core 3.1.1 has a single Condition profile; later versions split it by category.
|
|
617
|
+
return [US_CORE_CONDITION_PROFILE_ID] if us_core_version == PASConstants::US_CORE_VERSION_3_1_1
|
|
618
|
+
|
|
385
619
|
if category_code_present?(resource, 'encounter-diagnosis', system: TERMINOLOGY_CONDITION_CATEGORY_SYSTEM)
|
|
386
620
|
[US_CORE_CONDITION_ENCOUNTER_DIAGNOSIS_PROFILE_ID]
|
|
387
621
|
else
|
|
@@ -398,8 +632,9 @@ module DaVinciPASTestKit
|
|
|
398
632
|
end
|
|
399
633
|
|
|
400
634
|
def us_core_observation_profile_ids(resource)
|
|
401
|
-
code_profile_id = US_CORE_OBSERVATION_CODE_PROFILE_IDS.find do |code,
|
|
402
|
-
|
|
635
|
+
code_profile_id = US_CORE_OBSERVATION_CODE_PROFILE_IDS.find do |code, profile_id|
|
|
636
|
+
us_core_profile_available?(profile_id) &&
|
|
637
|
+
codeable_concept_has_code?(resource.code, code, system: LOINC_SYSTEM)
|
|
403
638
|
end&.last
|
|
404
639
|
return [code_profile_id] if code_profile_id.present?
|
|
405
640
|
|
|
@@ -422,7 +657,16 @@ module DaVinciPASTestKit
|
|
|
422
657
|
end
|
|
423
658
|
|
|
424
659
|
def us_core_profile_urls(profile_ids)
|
|
425
|
-
Array(profile_ids).map { |profile_id| "#{US_CORE_PROFILE_BASE}/#{profile_id}|#{
|
|
660
|
+
Array(profile_ids).map { |profile_id| "#{US_CORE_PROFILE_BASE}/#{profile_id}|#{us_core_version}" }
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# Whether the selected US Core version defines the profile.
|
|
664
|
+
def us_core_profile_available?(profile_id)
|
|
665
|
+
# The only profile a later supported version dropped; see profile_url_for_validation.
|
|
666
|
+
return us_core_version == PASConstants::US_CORE_VERSION_3_1_1 if profile_id == US_CORE_CONDITION_PROFILE_ID
|
|
667
|
+
|
|
668
|
+
added_in = US_CORE_PROFILE_IDS_ADDED_IN_VERSION.find { |_, profile_ids| profile_ids.include?(profile_id) }&.first
|
|
669
|
+
added_in.nil? || Gem::Version.new(us_core_version) >= Gem::Version.new(added_in)
|
|
426
670
|
end
|
|
427
671
|
|
|
428
672
|
def profile_url_without_version(url)
|
|
@@ -446,19 +690,59 @@ module DaVinciPASTestKit
|
|
|
446
690
|
end
|
|
447
691
|
end
|
|
448
692
|
|
|
449
|
-
# Validates a
|
|
450
|
-
#
|
|
693
|
+
# Validates a resource against a profile without logging, returning the unfiltered
|
|
694
|
+
# issues as message hashes for the caller to log once profile selection is resolved.
|
|
695
|
+
# @param resource [FHIR::Model] The resource to validate.
|
|
696
|
+
# @param profile_url [String, nil] The profile URL (with version) to validate against,
|
|
697
|
+
# or nil to validate against the base resource definition.
|
|
698
|
+
# @param validator [Symbol] The name of the FHIR resource validator to use.
|
|
699
|
+
# @param echoed_extensions [Array, nil] Extensions of the request resource the resource echoes, or
|
|
700
|
+
# nil if it doesn't echo one - see entry_validation_settings and echoed_extension_issue_messages.
|
|
701
|
+
# @return [Array<Hash>] Message hashes for the issues found.
|
|
702
|
+
def collect_resource_profile_messages(resource, profile_url = nil, validator: profile_validator,
|
|
703
|
+
echoed_extensions: nil)
|
|
704
|
+
response_details = []
|
|
705
|
+
resource_is_valid?(resource:, profile_url:, validator:,
|
|
706
|
+
add_messages_to_runnable: false,
|
|
707
|
+
validator_response_details: response_details)
|
|
708
|
+
issues = response_details.reject(&:filtered)
|
|
709
|
+
return validator_issues_to_messages(issues) if echoed_extensions.nil?
|
|
710
|
+
|
|
711
|
+
resource_hash = validated_hash(resource)
|
|
712
|
+
if validator == :default
|
|
713
|
+
# The :default validator allows every extension, so its only issues to drop are its notes that
|
|
714
|
+
# echoed extensions are unknown, which the echoed resource message already covers.
|
|
715
|
+
return validator_issues_to_messages(issues.reject do |issue|
|
|
716
|
+
echoed_unknown_extension_note?(issue, resource_hash, echoed_extensions)
|
|
717
|
+
end)
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
echoed_extension_issue_messages(issues, resource_hash, echoed_extensions)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
# Same as collect_resource_profile_messages for a Bundle, additionally dropping
|
|
724
|
+
# entry-resource-level issues that are covered by individual resource validation
|
|
725
|
+
# to avoid duplicate messages.
|
|
451
726
|
# @param bundle [FHIR::Bundle] The Bundle resource to validate.
|
|
452
727
|
# @param profile_url [String] The profile URL (with version) to validate against.
|
|
453
|
-
# @return [
|
|
454
|
-
def
|
|
728
|
+
# @return [Array<Hash>] Message hashes for the Bundle-structural issues found.
|
|
729
|
+
def collect_bundle_profile_messages(bundle, profile_url)
|
|
455
730
|
response_details = []
|
|
456
|
-
resource_is_valid?(resource: bundle, profile_url:,
|
|
731
|
+
resource_is_valid?(resource: bundle, profile_url:, validator: profile_validator,
|
|
457
732
|
add_messages_to_runnable: false,
|
|
458
733
|
validator_response_details: response_details)
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
734
|
+
validator_issues_to_messages(reject_entry_resource_issues(response_details))
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def validator_issues_to_messages(issues)
|
|
738
|
+
issues.map { |issue| validator_issue_message(issue) }
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# @param details [String, nil] replaces the validator's text, keeping the location the message starts with.
|
|
742
|
+
def validator_issue_message(issue, details: nil)
|
|
743
|
+
message = issue.message
|
|
744
|
+
message = message.delete_suffix(issue.raw_issue['message'].to_s) + details if details.present?
|
|
745
|
+
{ type: issue.severity, message: }
|
|
462
746
|
end
|
|
463
747
|
|
|
464
748
|
# Rejects validator issues that are already filtered or are located at/below
|
|
@@ -652,29 +936,6 @@ module DaVinciPASTestKit
|
|
|
652
936
|
bundle_map[key]
|
|
653
937
|
end
|
|
654
938
|
|
|
655
|
-
def absolute_url(reference, base_url)
|
|
656
|
-
return if reference.blank?
|
|
657
|
-
return reference if base_url.blank? || reference.starts_with?('urn:uuid:') || URI(reference).absolute?
|
|
658
|
-
|
|
659
|
-
"#{base_url}/#{reference}"
|
|
660
|
-
end
|
|
661
|
-
|
|
662
|
-
# Extracts the base URL from an absolute URL by removing the resource type and ID.
|
|
663
|
-
# @param absolute_url [String] The absolute URL.
|
|
664
|
-
# @return [String] The base URL, or an empty string if the URL format is not as expected.
|
|
665
|
-
def extract_base_url(absolute_url)
|
|
666
|
-
return '' if absolute_url.blank?
|
|
667
|
-
|
|
668
|
-
uri = URI(absolute_url)
|
|
669
|
-
return '' unless uri.scheme && uri.host
|
|
670
|
-
|
|
671
|
-
# Split the path segments and remove the last two segments (resource type and id)
|
|
672
|
-
path_segments = uri.path.split('/')
|
|
673
|
-
base_path = path_segments[0...-2].join('/')
|
|
674
|
-
|
|
675
|
-
"#{uri.scheme}://#{uri.host}#{base_path}"
|
|
676
|
-
end
|
|
677
|
-
|
|
678
939
|
# Resource Types to validate in request/ response bundle
|
|
679
940
|
def find_profile_url(request_type)
|
|
680
941
|
{
|
|
@@ -693,7 +954,7 @@ module DaVinciPASTestKit
|
|
|
693
954
|
|
|
694
955
|
# Determines the target profile URL for a Claim resource in a submit request bundle.
|
|
695
956
|
#
|
|
696
|
-
# In v2.2.1, profile-pas-request-bundle permits either profile-claim or profile-claim-update.
|
|
957
|
+
# In PAS v2.2.1, profile-pas-request-bundle permits either profile-claim or profile-claim-update.
|
|
697
958
|
# The structural discriminator is Claim.related: profile-claim-update requires it (must-support)
|
|
698
959
|
# while profile-claim disallows it (max: 0). For all other versions, profile-claim-update is
|
|
699
960
|
# used unconditionally.
|
|
@@ -727,15 +988,15 @@ module DaVinciPASTestKit
|
|
|
727
988
|
is_unique = sequences.uniq.length == sequences.length
|
|
728
989
|
return if is_unique
|
|
729
990
|
|
|
730
|
-
|
|
731
|
-
|
|
991
|
+
add_validation_error("[Claim/#{claim.id}]: The sequence element for each supportingInfo entry SHALL be " \
|
|
992
|
+
'unique within the Claim.')
|
|
732
993
|
end
|
|
733
994
|
|
|
734
995
|
def validate_bundle_entries_full_url(bundle)
|
|
735
996
|
msg = "[Bundle/#{bundle.id}]: Bundle.entry.fullUrl values SHALL be a valid url or in the form " \
|
|
736
997
|
"'urn:uuid:[some guid]'."
|
|
737
998
|
bundle.entry.each do |entry|
|
|
738
|
-
|
|
999
|
+
add_validation_error(msg) unless valid_url_or_urn_uuid?(entry.fullUrl)
|
|
739
1000
|
end
|
|
740
1001
|
end
|
|
741
1002
|
|
|
@@ -749,119 +1010,6 @@ module DaVinciPASTestKit
|
|
|
749
1010
|
string&.match?(url_regex) || string&.match?(urn_uuid_regex)
|
|
750
1011
|
end
|
|
751
1012
|
|
|
752
|
-
# This method traverses references within a FHIR resource, ensuring that referenced resources
|
|
753
|
-
# are populated in the bundle. It also enforces that a referenced resource appears only once in the bundle,
|
|
754
|
-
# as required by the PAS IG.
|
|
755
|
-
# @param target_resource [FHIR::Model] The FHIR resource to traverse and validate.
|
|
756
|
-
# @param base_url [String] The server base url.
|
|
757
|
-
# @param resources_to_match [Array<FHIR:Bundle:Entry] The list of FHIR bundle entries to match references against.
|
|
758
|
-
# @param skip_claim_related [Boolean] When true, a Claim's `related` element is not traversed. This is set
|
|
759
|
-
# for any Claim reached by following a reference (i.e. a non-primary Claim in a Claim Update chain), whose
|
|
760
|
-
# own Claim.related.claim (the grandparent) is deliberately omitted from the Bundle per spec-65/66. The
|
|
761
|
-
# primary Claim passed in by the caller keeps `skip_claim_related: false`, so its parent reference - and the
|
|
762
|
-
# parent's own referenced resources - are still checked.
|
|
763
|
-
def check_presence_of_referenced_resources(target_resource, base_url, resources_to_match,
|
|
764
|
-
skip_claim_related: false)
|
|
765
|
-
return if target_resource.blank?
|
|
766
|
-
|
|
767
|
-
if target_resource.is_a?(FHIR::Reference) && target_resource.reference.present?
|
|
768
|
-
ref = target_resource.reference
|
|
769
|
-
absolute_ref = absolute_url(ref, base_url)
|
|
770
|
-
matching_resources = resources_to_match.find_all { |res| res.fullUrl == absolute_ref }
|
|
771
|
-
|
|
772
|
-
if matching_resources.length != 1
|
|
773
|
-
validation_error_messages << resource_shall_appear_once_message(absolute_ref,
|
|
774
|
-
matching_resources.length)
|
|
775
|
-
end
|
|
776
|
-
|
|
777
|
-
if matching_resources.length.positive?
|
|
778
|
-
# A resource reached by following a reference is an included resource, not the primary Claim
|
|
779
|
-
# being validated; if it is itself a Claim Update, its referenced grandparent Claim is omitted.
|
|
780
|
-
check_presence_of_referenced_resources(matching_resources.first.resource, base_url, resources_to_match,
|
|
781
|
-
skip_claim_related: true)
|
|
782
|
-
end
|
|
783
|
-
else
|
|
784
|
-
target_resource.source_hash.each_key do |attr|
|
|
785
|
-
next if claim_response_request_attr?(target_resource, attr)
|
|
786
|
-
next if skip_claim_related && claim_related_attr?(target_resource, attr)
|
|
787
|
-
|
|
788
|
-
value = target_resource.send(attr.to_sym)
|
|
789
|
-
if value.is_a?(FHIR::Model)
|
|
790
|
-
check_presence_of_referenced_resources(value, base_url, resources_to_match, skip_claim_related:)
|
|
791
|
-
elsif value.is_a?(Array) && value.all?(FHIR::Model)
|
|
792
|
-
value.each do |elmt|
|
|
793
|
-
check_presence_of_referenced_resources(elmt, base_url, resources_to_match, skip_claim_related:)
|
|
794
|
-
end
|
|
795
|
-
end
|
|
796
|
-
end
|
|
797
|
-
end
|
|
798
|
-
end
|
|
799
|
-
|
|
800
|
-
# ClaimResponse.request is a back-reference to the submitted Claim. The PAS IG response bundle
|
|
801
|
-
# profile (profile-pas-response-bundle) has no required Claim entry slice, so the Claim need
|
|
802
|
-
# not be present in the response bundle. Skipping here matches the identical guard in
|
|
803
|
-
# ResponseGenerator#referenced_entities, which also skips ClaimResponse.request when
|
|
804
|
-
# building mock response bundles.
|
|
805
|
-
def claim_response_request_attr?(resource, attr)
|
|
806
|
-
attr.to_s == 'request' &&
|
|
807
|
-
resource.respond_to?(:resourceType) &&
|
|
808
|
-
resource.resourceType == 'ClaimResponse'
|
|
809
|
-
end
|
|
810
|
-
|
|
811
|
-
# Claim.related.claim points to the Claim being updated. For a non-primary Claim in a Claim Update
|
|
812
|
-
# chain (one reached by following a reference), that prior Claim is the grandparent, which spec-65/66
|
|
813
|
-
# require to be omitted from the Bundle. Used with the `skip_claim_related` flag so the generic
|
|
814
|
-
# reference-presence check does not flag the deliberately-omitted grandparent as missing.
|
|
815
|
-
def claim_related_attr?(resource, attr)
|
|
816
|
-
attr.to_s == 'related' &&
|
|
817
|
-
resource.respond_to?(:resourceType) &&
|
|
818
|
-
resource.resourceType == 'Claim'
|
|
819
|
-
end
|
|
820
|
-
|
|
821
|
-
# Extracts resources from a bundle while following "next" links.
|
|
822
|
-
#
|
|
823
|
-
# @param bundle [FHIR::Bundle] The initial FHIR bundle to extract resources from.
|
|
824
|
-
# @param response [Object] The HTTP response object for the bundle retrieval.
|
|
825
|
-
# @param reply_handler [Proc] A callback function to handle responses.
|
|
826
|
-
# @param max_pages [Integer] The maximum number of pages to process.
|
|
827
|
-
#
|
|
828
|
-
# This method extracts resources from a FHIR bundle, following "next" links in the bundle
|
|
829
|
-
# until the specified maximum number of pages is reached. It collects resources and
|
|
830
|
-
# invokes the reply_handler for each response.
|
|
831
|
-
def extract_resources_from_bundle(
|
|
832
|
-
bundle: nil,
|
|
833
|
-
response: nil,
|
|
834
|
-
reply_handler: nil,
|
|
835
|
-
max_pages: 20
|
|
836
|
-
)
|
|
837
|
-
page_count = 1
|
|
838
|
-
resources = []
|
|
839
|
-
|
|
840
|
-
until bundle.nil? || page_count == max_pages
|
|
841
|
-
resources += bundle&.entry&.map { |entry| entry&.resource }
|
|
842
|
-
next_bundle_link = bundle&.link&.find { |link| link.relation == 'next' }&.url
|
|
843
|
-
reply_handler&.call(response)
|
|
844
|
-
|
|
845
|
-
break if next_bundle_link.blank?
|
|
846
|
-
|
|
847
|
-
page_count += 1
|
|
848
|
-
end
|
|
849
|
-
|
|
850
|
-
resources
|
|
851
|
-
end
|
|
852
|
-
|
|
853
|
-
# Generates a message for a resource that appears more than once in a bundle.
|
|
854
|
-
#
|
|
855
|
-
# @param reference_resource_type [String] The resource type being referenced.
|
|
856
|
-
# @param reference_resource_id [String] The resource ID being referenced.
|
|
857
|
-
# @param total_matches [Integer] The total number of matches found in the bundle.
|
|
858
|
-
#
|
|
859
|
-
# This method generates an error message when a referenced resource appears more than once
|
|
860
|
-
# in a FHIR bundle, which is not allowed.
|
|
861
|
-
def resource_shall_appear_once_message(absolute_ref, total_matches)
|
|
862
|
-
" The referenced #{absolute_ref} resource SHALL appear exactly once in the Bundle, but found #{total_matches}."
|
|
863
|
-
end
|
|
864
|
-
|
|
865
1013
|
# Generates a message for a resource present in both the PA request and response bundles.
|
|
866
1014
|
#
|
|
867
1015
|
# @param resource [FHIR::Model] The resource present in both bundles.
|
|
@@ -872,5 +1020,115 @@ module DaVinciPASTestKit
|
|
|
872
1020
|
"Resource #{resource.resourceType}/#{resource.id} is an entry in both the PA Request Bundle and the Response " \
|
|
873
1021
|
'Bundle, but they do not have the same fullUrl or identifiers'
|
|
874
1022
|
end
|
|
1023
|
+
|
|
1024
|
+
###########################################################################
|
|
1025
|
+
# US Core Profile Constants
|
|
1026
|
+
###########################################################################
|
|
1027
|
+
|
|
1028
|
+
US_CORE_PROFILE_BASE = 'http://hl7.org/fhir/us/core/StructureDefinition'
|
|
1029
|
+
BASE_R4_PROFILE = :base_r4
|
|
1030
|
+
CLAIM_ENCOUNTER_EXTENSION_URL = 'http://hl7.org/fhir/5.0/StructureDefinition/extension-Claim.encounter'
|
|
1031
|
+
LOINC_SYSTEM = 'http://loinc.org'
|
|
1032
|
+
TERMINOLOGY_CONDITION_CATEGORY_SYSTEM = 'http://terminology.hl7.org/CodeSystem/condition-category'
|
|
1033
|
+
OBSERVATION_CATEGORY_SYSTEM = 'http://terminology.hl7.org/CodeSystem/observation-category'
|
|
1034
|
+
DIAGNOSTIC_REPORT_CATEGORY_SYSTEM = 'http://terminology.hl7.org/CodeSystem/v2-0074'
|
|
1035
|
+
|
|
1036
|
+
US_CORE_SINGLE_PROFILE_IDS_BY_RESOURCE = {
|
|
1037
|
+
'AllergyIntolerance' => 'us-core-allergyintolerance',
|
|
1038
|
+
'CarePlan' => 'us-core-careplan',
|
|
1039
|
+
'CareTeam' => 'us-core-careteam',
|
|
1040
|
+
'Coverage' => 'us-core-coverage',
|
|
1041
|
+
'Device' => 'us-core-implantable-device',
|
|
1042
|
+
'DocumentReference' => 'us-core-documentreference',
|
|
1043
|
+
'Encounter' => 'us-core-encounter',
|
|
1044
|
+
'Goal' => 'us-core-goal',
|
|
1045
|
+
'Immunization' => 'us-core-immunization',
|
|
1046
|
+
'Location' => 'us-core-location',
|
|
1047
|
+
'Medication' => 'us-core-medication',
|
|
1048
|
+
'MedicationDispense' => 'us-core-medicationdispense',
|
|
1049
|
+
'MedicationRequest' => 'us-core-medicationrequest',
|
|
1050
|
+
'Organization' => 'us-core-organization',
|
|
1051
|
+
'Patient' => 'us-core-patient',
|
|
1052
|
+
'Practitioner' => 'us-core-practitioner',
|
|
1053
|
+
'PractitionerRole' => 'us-core-practitionerrole',
|
|
1054
|
+
'Procedure' => 'us-core-procedure',
|
|
1055
|
+
'Provenance' => 'us-core-provenance',
|
|
1056
|
+
'QuestionnaireResponse' => 'us-core-questionnaireresponse',
|
|
1057
|
+
'RelatedPerson' => 'us-core-relatedperson',
|
|
1058
|
+
'ServiceRequest' => 'us-core-servicerequest',
|
|
1059
|
+
'Specimen' => 'us-core-specimen'
|
|
1060
|
+
}.freeze
|
|
1061
|
+
|
|
1062
|
+
US_CORE_CONDITION_PROFILE_ID = 'us-core-condition'
|
|
1063
|
+
US_CORE_CONDITION_ENCOUNTER_DIAGNOSIS_PROFILE_ID = 'us-core-condition-encounter-diagnosis'
|
|
1064
|
+
US_CORE_CONDITION_PROBLEMS_HEALTH_CONCERNS_PROFILE_ID = 'us-core-condition-problems-health-concerns'
|
|
1065
|
+
US_CORE_DIAGNOSTIC_REPORT_LAB_PROFILE_ID = 'us-core-diagnosticreport-lab'
|
|
1066
|
+
US_CORE_DIAGNOSTIC_REPORT_NOTE_PROFILE_ID = 'us-core-diagnosticreport-note'
|
|
1067
|
+
US_CORE_OBSERVATION_CLINICAL_RESULT_PROFILE_ID = 'us-core-observation-clinical-result'
|
|
1068
|
+
US_CORE_OBSERVATION_LAB_PROFILE_ID = 'us-core-observation-lab'
|
|
1069
|
+
US_CORE_OBSERVATION_SCREENING_ASSESSMENT_PROFILE_ID = 'us-core-observation-screening-assessment'
|
|
1070
|
+
US_CORE_SIMPLE_OBSERVATION_PROFILE_ID = 'us-core-simple-observation'
|
|
1071
|
+
US_CORE_SMOKING_STATUS_PROFILE_ID = 'us-core-smokingstatus'
|
|
1072
|
+
US_CORE_VITAL_SIGNS_PROFILE_ID = 'us-core-vital-signs'
|
|
1073
|
+
|
|
1074
|
+
US_CORE_OBSERVATION_CODE_PROFILE_IDS = {
|
|
1075
|
+
'11341-5' => 'us-core-observation-occupation',
|
|
1076
|
+
'86645-9' => 'us-core-observation-pregnancyintent',
|
|
1077
|
+
'82810-3' => 'us-core-observation-pregnancystatus',
|
|
1078
|
+
'76690-7' => 'us-core-observation-sexual-orientation',
|
|
1079
|
+
'95541-9' => 'us-core-care-experience-preference',
|
|
1080
|
+
'75773-2' => 'us-core-treatment-intervention-preference',
|
|
1081
|
+
'8289-1' => 'head-occipital-frontal-circumference-percentile',
|
|
1082
|
+
'59576-9' => 'pediatric-bmi-for-age',
|
|
1083
|
+
'77606-2' => 'pediatric-weight-for-height',
|
|
1084
|
+
'85354-9' => 'us-core-blood-pressure',
|
|
1085
|
+
'96607-7' => 'us-core-average-blood-pressure',
|
|
1086
|
+
'39156-5' => 'us-core-bmi',
|
|
1087
|
+
'8302-2' => 'us-core-body-height',
|
|
1088
|
+
'8310-5' => 'us-core-body-temperature',
|
|
1089
|
+
'29463-7' => 'us-core-body-weight',
|
|
1090
|
+
'9843-4' => 'us-core-head-circumference',
|
|
1091
|
+
'8867-4' => 'us-core-heart-rate',
|
|
1092
|
+
'59408-5' => 'us-core-pulse-oximetry',
|
|
1093
|
+
'2708-6' => 'us-core-pulse-oximetry',
|
|
1094
|
+
'9279-1' => 'us-core-respiratory-rate',
|
|
1095
|
+
'72166-2' => 'us-core-smokingstatus'
|
|
1096
|
+
}.freeze
|
|
1097
|
+
|
|
1098
|
+
# Fallback profiles that US Core 3.1.1 does not define, keyed by the earliest supported
|
|
1099
|
+
# version that does. Profiles not listed here exist in every supported version.
|
|
1100
|
+
US_CORE_PROFILE_IDS_ADDED_IN_VERSION = {
|
|
1101
|
+
PASConstants::US_CORE_VERSION_6_1_0 => [
|
|
1102
|
+
'us-core-blood-pressure',
|
|
1103
|
+
'us-core-bmi',
|
|
1104
|
+
'us-core-body-height',
|
|
1105
|
+
'us-core-body-temperature',
|
|
1106
|
+
'us-core-body-weight',
|
|
1107
|
+
'us-core-condition-encounter-diagnosis',
|
|
1108
|
+
'us-core-condition-problems-health-concerns',
|
|
1109
|
+
'us-core-coverage',
|
|
1110
|
+
'us-core-head-circumference',
|
|
1111
|
+
'us-core-heart-rate',
|
|
1112
|
+
'us-core-medicationdispense',
|
|
1113
|
+
'us-core-observation-clinical-result',
|
|
1114
|
+
'us-core-observation-occupation',
|
|
1115
|
+
'us-core-observation-pregnancyintent',
|
|
1116
|
+
'us-core-observation-pregnancystatus',
|
|
1117
|
+
'us-core-observation-screening-assessment',
|
|
1118
|
+
'us-core-observation-sexual-orientation',
|
|
1119
|
+
'us-core-questionnaireresponse',
|
|
1120
|
+
'us-core-relatedperson',
|
|
1121
|
+
'us-core-respiratory-rate',
|
|
1122
|
+
'us-core-servicerequest',
|
|
1123
|
+
'us-core-simple-observation',
|
|
1124
|
+
'us-core-specimen',
|
|
1125
|
+
'us-core-vital-signs'
|
|
1126
|
+
],
|
|
1127
|
+
PASConstants::US_CORE_VERSION_7_0_0 => [
|
|
1128
|
+
'us-core-average-blood-pressure',
|
|
1129
|
+
'us-core-care-experience-preference',
|
|
1130
|
+
'us-core-treatment-intervention-preference'
|
|
1131
|
+
]
|
|
1132
|
+
}.freeze
|
|
875
1133
|
end
|
|
876
1134
|
end
|