From 218508c4530098e117e5eac194ed1d20e559d58a Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Thu, 25 Apr 2024 22:53:28 -0400 Subject: [PATCH 01/19] add `claims_api_local_bgs_refactor` feature toggle --- config/features.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/features.yml b/config/features.yml index 4f147f2f588..2574d4c72de 100644 --- a/config/features.yml +++ b/config/features.yml @@ -164,6 +164,10 @@ features: actor_type: user description: Enables users to access the claim letters page enable_in_development: true + claims_api_local_bgs_refactor: + actor_type: user + description: Diverts codepath to LocalBGSRefactored + enable_in_development: true cst_use_lighthouse_5103: actor_type: user description: When enabled, claims status tool uses the Lighthouse API for the 5103 endpoint From e8b1d188ec802b459f5b9cd3557681296e9049b4 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Thu, 18 Apr 2024 15:58:54 -0400 Subject: [PATCH 02/19] extract miscellaneous LocalBGS operations Move miscellaneous operations out so that: - it is easier to look at base-level functionality without all of these various operation implementations intermingled - these various operation implementations can eventually be organized according to the standards we've used for other operations --- .../bgs_service/claim_management_service.rb | 4 + .../claims_api/lib/bgs_service/local_bgs.rb | 195 +---------------- .../local_bgs/miscellaneous_operations.rb | 198 ++++++++++++++++++ 3 files changed, 203 insertions(+), 194 deletions(-) create mode 100644 modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb diff --git a/modules/claims_api/lib/bgs_service/claim_management_service.rb b/modules/claims_api/lib/bgs_service/claim_management_service.rb index 2dd7981c084..39fce5ccff0 100644 --- a/modules/claims_api/lib/bgs_service/claim_management_service.rb +++ b/modules/claims_api/lib/bgs_service/claim_management_service.rb @@ -24,5 +24,9 @@ def update_claim_level_suspense(claim:) make_request(endpoint: bean_name, action: 'updateClaimLevelSuspense', body:) end + + def to_camelcase(claim:) + claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } + end end end diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index 70cec2d24df..10849948ec1 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -8,7 +8,7 @@ require 'claims_api/claim_logger' require 'claims_api/error/soap_error_handler' -require 'claims_api/evss_bgs_mapper' +require_relative 'local_bgs/miscellaneous_operations' module ClaimsApi class LocalBGS @@ -77,141 +77,6 @@ def healthcheck(endpoint) wsdl.status end - def find_poa_by_participant_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ptcpntId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'ClaimantServiceBean/ClaimantWebService', action: 'findPOAByPtcpntId', body:, - key: 'return') - end - - def find_by_ssn(ssn) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ssn: }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'PersonWebServiceBean/PersonWebService', action: 'findPersonBySSN', body:, - key: 'PersonDTO') - end - - def find_poa_history_by_ptcpnt_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ptcpntId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'OrgWebServiceBean/OrgWebService', action: 'findPoaHistoryByPtcpntId', body:, - key: 'PoaHistory') - end - - def find_benefit_claims_status_by_ptcpnt_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ptcpntId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', - action: 'findBenefitClaimsStatusByPtcpntId', body:) - end - - def claims_count(id) - find_benefit_claims_status_by_ptcpnt_id(id).count - rescue ::Common::Exceptions::ResourceNotFound - 0 - end - - def find_benefit_claim_details_by_benefit_claim_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { bnftClaimId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', - action: 'findBenefitClaimDetailsByBnftClaimId', body:) - end - - def insert_intent_to_file(options) - request_body = construct_itf_body(options) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - - EOXML - - request_body.each do |k, z| - node = Nokogiri::XML::Node.new k.to_s, body - node.content = z.to_s - opt = body.at('intentToFileDTO') - node.parent = opt - end - make_request(endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', action: 'insertIntentToFile', - body:, key: 'IntentToFileDTO') - end - - def find_tracked_items(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { claimId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'TrackedItemService/TrackedItemService', action: 'findTrackedItems', body:, - key: 'BenefitClaim') - end - - def find_intent_to_file_by_ptcpnt_id_itf_type_cd(id, type) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - ptcpnt_id = body.at 'ptcpntId' - ptcpnt_id.content = id.to_s - itf_type_cd = body.at 'itfTypeCd' - itf_type_cd.content = type.to_s - - response = - make_request( - endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', - action: 'findIntentToFileByPtcpntIdItfTypeCd', - body: - ) - - Array.wrap(response[:intent_to_file_dto]) - end - - # BEGIN: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. - def update_from_remote(id) - bgs_claim = find_benefit_claim_details_by_benefit_claim_id(id) - transform_bgs_claim_to_evss(bgs_claim) - end - - def all(id) - claims = find_benefit_claims_status_by_ptcpnt_id(id) - return [] if claims.count < 1 || claims[:benefit_claims_dto].blank? - - transform_bgs_claims_to_evss(claims) - end - # END: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. - private def header # rubocop:disable Metrics/MethodLength @@ -320,19 +185,6 @@ def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_ end end - def construct_itf_body(options) - request_body = { - itfTypeCd: options[:intent_to_file_type_code], - ptcpntVetId: options[:participant_vet_id], - rcvdDt: options[:received_date], - signtrInd: options[:signature_indicated], - submtrApplcnTypeCd: options[:submitter_application_icn_type_code] - } - request_body[:ptcpntClmantId] = options[:participant_claimant_id] if options.key?(:participant_claimant_id) - request_body[:clmantSsn] = options[:claimant_ssn] if options.key?(:claimant_ssn) - request_body - end - def log_duration(event: 'default', **extra_params) # Who are we to question sidekiq's use of CLOCK_MONOTONIC to avoid negative durations? # https://github.com/sidekiq/sidekiq/issues/3999 @@ -350,50 +202,5 @@ def log_duration(event: 'default', **extra_params) def soap_error_handler ClaimsApi::SoapErrorHandler.new end - - def transform_bgs_claim_to_evss(claim) - bgs_claim = ClaimsApi::EvssBgsMapper.new(claim[:benefit_claim_details_dto]) - return if bgs_claim.nil? - - bgs_claim.map_and_build_object - end - - def transform_bgs_claims_to_evss(claims) - claims_array = [claims[:benefit_claims_dto][:benefit_claim]].flatten - claims_array&.map do |claim| - bgs_claim = ClaimsApi::EvssBgsMapper.new(claim) - bgs_claim.map_and_build_object - end - end - - def to_camelcase(claim:) - claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } - end - - def convert_nil_values(options) - arg_strg = '' - options.each do |option| - arg = option[0].to_s.camelize(:lower) - arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") - end - arg_strg - end - - def validate_opts!(opts, required_keys) - keys = opts.keys.map(&:to_s) - required_keys = required_keys.map(&:to_s) - missing_keys = required_keys - keys - raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? - end - - def jrn - { - jrn_dt: Time.current.iso8601, - jrn_lctn_id: Settings.bgs.client_station_id, - jrn_status_type_cd: 'U', - jrn_user_id: Settings.bgs.client_username, - jrn_obj_id: Settings.bgs.application - } - end end end diff --git a/modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb b/modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb new file mode 100644 index 00000000000..fce254c04e6 --- /dev/null +++ b/modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb @@ -0,0 +1,198 @@ +# frozen_string_literal: true + +require 'claims_api/evss_bgs_mapper' + +module ClaimsApi + class LocalBGS + def find_poa_by_participant_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'ClaimantServiceBean/ClaimantWebService', action: 'findPOAByPtcpntId', body:, + key: 'return') + end + + def find_by_ssn(ssn) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ssn: }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'PersonWebServiceBean/PersonWebService', action: 'findPersonBySSN', body:, + key: 'PersonDTO') + end + + def find_poa_history_by_ptcpnt_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'OrgWebServiceBean/OrgWebService', action: 'findPoaHistoryByPtcpntId', body:, + key: 'PoaHistory') + end + + def find_benefit_claims_status_by_ptcpnt_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', + action: 'findBenefitClaimsStatusByPtcpntId', body:) + end + + def claims_count(id) + find_benefit_claims_status_by_ptcpnt_id(id).count + rescue ::Common::Exceptions::ResourceNotFound + 0 + end + + def find_benefit_claim_details_by_benefit_claim_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { bnftClaimId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', + action: 'findBenefitClaimDetailsByBnftClaimId', body:) + end + + def insert_intent_to_file(options) + request_body = construct_itf_body(options) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + + EOXML + + request_body.each do |k, z| + node = Nokogiri::XML::Node.new k.to_s, body + node.content = z.to_s + opt = body.at('intentToFileDTO') + node.parent = opt + end + make_request(endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', action: 'insertIntentToFile', + body:, key: 'IntentToFileDTO') + end + + def find_tracked_items(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { claimId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'TrackedItemService/TrackedItemService', action: 'findTrackedItems', body:, + key: 'BenefitClaim') + end + + def find_intent_to_file_by_ptcpnt_id_itf_type_cd(id, type) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + ptcpnt_id = body.at 'ptcpntId' + ptcpnt_id.content = id.to_s + itf_type_cd = body.at 'itfTypeCd' + itf_type_cd.content = type.to_s + + response = + make_request( + endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', + action: 'findIntentToFileByPtcpntIdItfTypeCd', + body: + ) + + Array.wrap(response[:intent_to_file_dto]) + end + + # BEGIN: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. + def update_from_remote(id) + bgs_claim = find_benefit_claim_details_by_benefit_claim_id(id) + transform_bgs_claim_to_evss(bgs_claim) + end + + def all(id) + claims = find_benefit_claims_status_by_ptcpnt_id(id) + return [] if claims.count < 1 || claims[:benefit_claims_dto].blank? + + transform_bgs_claims_to_evss(claims) + end + # END: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. + + private + + def construct_itf_body(options) + request_body = { + itfTypeCd: options[:intent_to_file_type_code], + ptcpntVetId: options[:participant_vet_id], + rcvdDt: options[:received_date], + signtrInd: options[:signature_indicated], + submtrApplcnTypeCd: options[:submitter_application_icn_type_code] + } + request_body[:ptcpntClmantId] = options[:participant_claimant_id] if options.key?(:participant_claimant_id) + request_body[:clmantSsn] = options[:claimant_ssn] if options.key?(:claimant_ssn) + request_body + end + + def transform_bgs_claim_to_evss(claim) + bgs_claim = ClaimsApi::EvssBgsMapper.new(claim[:benefit_claim_details_dto]) + return if bgs_claim.nil? + + bgs_claim.map_and_build_object + end + + def transform_bgs_claims_to_evss(claims) + claims_array = [claims[:benefit_claims_dto][:benefit_claim]].flatten + claims_array&.map do |claim| + bgs_claim = ClaimsApi::EvssBgsMapper.new(claim) + bgs_claim.map_and_build_object + end + end + + def convert_nil_values(options) + arg_strg = '' + options.each do |option| + arg = option[0].to_s.camelize(:lower) + arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") + end + arg_strg + end + + def validate_opts!(opts, required_keys) + keys = opts.keys.map(&:to_s) + required_keys = required_keys.map(&:to_s) + missing_keys = required_keys - keys + raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? + end + + def jrn + { + jrn_dt: Time.current.iso8601, + jrn_lctn_id: Settings.bgs.client_station_id, + jrn_status_type_cd: 'U', + jrn_user_id: Settings.bgs.client_username, + jrn_obj_id: Settings.bgs.application + } + end + end +end From 5c4025b77621d2a19b728415ce6c44a418d8db4d Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 19 Apr 2024 18:57:33 -0400 Subject: [PATCH 03/19] reorganize LocalBGS appease linter in LocalBGS --- .../claims_api/lib/bgs_service/local_bgs.rb | 218 +++++++++--------- .../claims_api/error/soap_error_handler.rb | 6 +- 2 files changed, 117 insertions(+), 107 deletions(-) diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index 10849948ec1..b8cab2283bd 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -12,10 +12,28 @@ module ClaimsApi class LocalBGS + class << self + def breakers_service + url = URI.parse(Settings.bgs.url) + matcher = proc do |request_env| + request_env.url.host == url.host && + request_env.url.port == url.port && + request_env.url.path =~ /^#{url.path}/ + end + + Breakers::Service.new( + name: 'BGS/Claims', + request_matcher: matcher + ) + end + end + attr_accessor :external_uid, :external_key - # rubocop:disable Metrics/MethodLength - def initialize(external_uid:, external_key:) + def initialize( # rubocop:disable Metrics/MethodLength + external_uid: Settings.bgs.external_uid, + external_key: Settings.bgs.external_key + ) @client_ip = if Rails.env.test? # For all intents and purposes, BGS behaves identically no matter what @@ -43,70 +61,79 @@ def initialize(external_uid:, external_key:) @env = Settings.bgs.env @mock_response_location = Settings.bgs.mock_response_location @mock_responses = Settings.bgs.mock_responses - @external_uid = external_uid || Settings.bgs.external_uid - @external_key = external_key || Settings.bgs.external_key + @external_uid = external_uid + @external_key = external_key @forward_proxy_url = Settings.bgs.url @timeout = Settings.bgs.timeout || 120 + @url = Settings.bgs.url end - # rubocop:enable Metrics/MethodLength - - def self.breakers_service - url = Settings.bgs.url - path = URI.parse(url).path - host = URI.parse(url).host - port = URI.parse(url).port - matcher = proc do |request_env| - request_env.url.host == host && - request_env.url.port == port && - request_env.url.path =~ /^#{path}/ - end - Breakers::Service.new( - name: 'BGS/Claims', - request_matcher: matcher - ) + def healthcheck(endpoint) + response = fetch_wsdl(endpoint) + response.status end - def bean_name - raise 'Not Implemented' - end + def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_response: true) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists + begin + wsdl = + log_duration(event: 'connection_wsdl_get', endpoint:) do + fetch_wsdl(endpoint).body + end - def healthcheck(endpoint) - connection = Faraday::Connection.new(ssl: { verify_mode: @ssl_verify_mode }) - wsdl = connection.get("#{Settings.bgs.url}/#{endpoint}?WSDL") - wsdl.status + request_body = + log_duration(event: 'built_request', endpoint:, action:) do + wsdl_body = Hash.from_xml(wsdl) + namespace = wsdl_body.dig('definitions', 'targetNamespace').to_s + + wrap_request_body( + body, + action:, + namespace:, + namespaces: + ) + end + + response = + log_duration(event: 'connection_post', endpoint:, action:) do + connection.post(endpoint) do |req| + req.body = request_body + + req.headers.merge!( + 'Content-Type' => 'text/xml;charset=UTF-8', + 'Host' => "#{@env}.vba.va.gov", + 'Soapaction' => %("#{action}") + ) + end + end + rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e + detail = "local BGS Faraday Timeout: #{e.message}" + ClaimsApi::Logger.log('local_bgs', retry: true, detail:) + + raise ::Common::Exceptions::BadGateway + end + + log_duration(event: 'parsed_response', key:) do + response_body = Hash.from_xml(response.body) + soap_error_handler.handle_errors!(response_body) + + unwrap_response_body( + response_body, + transform: transform_response, + action:, + key: + ) + end end private - def header # rubocop:disable Metrics/MethodLength - # Stock XML structure {{{ - header = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - - - - - - - - - - - - - - EOXML - - { Username: @client_username, CLIENT_MACHINE: @client_ip, - STN_ID: @client_station_id, applicationName: @application, - ExternalUid: @external_uid, ExternalKey: @external_key }.each do |k, v| - header.xpath(".//*[local-name()='#{k}']")[0].content = v + def fetch_wsdl(endpoint) + connection.get(endpoint) do |req| + req.params['WSDL'] = nil end - header.to_s end - def full_body(action:, body:, namespace:, namespaces:) + def wrap_request_body(body, action:, namespace:, namespaces:) # rubocop:disable Metrics/MethodLength namespaces = namespaces.map do |aliaz, path| uri = URI(namespace) @@ -114,26 +141,41 @@ def full_body(action:, body:, namespace:, namespaces:) %(xmlns:#{aliaz}="#{uri}") end - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + <<~EOXML - - #{header} + + + + + #{@client_username} + + + #{@client_ip} + #{@client_station_id} + #{@application} + #{@external_uid} + #{@external_key} + + + #{body} - + EOXML - body.to_s end - def parsed_response(response, action:, key:, transform:) - body = Hash.from_xml(response.body) + def unwrap_response_body(body, action:, key:, transform:) keys = ['Envelope', 'Body', "#{action}Response"] keys << key if key.present? @@ -146,43 +188,17 @@ def parsed_response(response, action:, key:, transform:) end end - def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_response: true) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists - connection = log_duration event: 'establish_ssl_connection' do - Faraday::Connection.new(ssl: { verify_mode: @ssl_verify_mode }) do |f| + def soap_error_handler + ClaimsApi::SoapErrorHandler.new + end + + def connection + @connection ||= + Faraday.new(@url, ssl: { verify_mode: @ssl_verify_mode }) do |f| f.use :breakers f.adapter Faraday.default_adapter + f.options.timeout = @timeout end - end - connection.options.timeout = @timeout - - begin - wsdl = log_duration(event: 'connection_wsdl_get', endpoint:) do - connection.get("#{Settings.bgs.url}/#{endpoint}?WSDL") - end - - url = "#{Settings.bgs.url}/#{endpoint}" - namespace = Hash.from_xml(wsdl.body).dig('definitions', 'targetNamespace').to_s - body = full_body(action:, body:, namespace:, namespaces:) - headers = { - 'Content-Type' => 'text/xml;charset=UTF-8', - 'Host' => "#{@env}.vba.va.gov", - 'Soapaction' => %("#{action}") - } - - response = log_duration(event: 'connection_post', endpoint:, action:) do - connection.post(url, body, headers) - end - rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e - ClaimsApi::Logger.log('local_bgs', - retry: true, - detail: "local BGS Faraday Timeout: #{e.message}") - raise ::Common::Exceptions::BadGateway - end - soap_error_handler.handle_errors(response) if response - - log_duration(event: 'parsed_response', key:) do - parsed_response(response, action:, key:, transform: transform_response) - end end def log_duration(event: 'default', **extra_params) @@ -198,9 +214,5 @@ def log_duration(event: 'default', **extra_params) StatsD.measure("api.claims_api.local_bgs.#{event}.duration", duration, tags: {}) result end - - def soap_error_handler - ClaimsApi::SoapErrorHandler.new - end end end diff --git a/modules/claims_api/lib/claims_api/error/soap_error_handler.rb b/modules/claims_api/lib/claims_api/error/soap_error_handler.rb index 33fef287e84..736981400af 100644 --- a/modules/claims_api/lib/claims_api/error/soap_error_handler.rb +++ b/modules/claims_api/lib/claims_api/error/soap_error_handler.rb @@ -1,13 +1,11 @@ # frozen_string_literal: true -require 'nokogiri' - module ClaimsApi class SoapErrorHandler # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm - def handle_errors(response) - @hash = Hash.from_xml(response.body) + def handle_errors!(body) + @hash = body return if @hash&.dig('Envelope', 'Body', 'Fault').blank? From bc79bbd7d725b721a5d35ab663ad635054db1efb Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 19 Apr 2024 21:34:33 -0400 Subject: [PATCH 04/19] unmix miscellaneous LocalBGS operations ...and turn it into its own service --- .../claims_api/v1/application_controller.rb | 2 +- .../controllers/claims_api/v1/forms/base.rb | 2 +- .../v1/forms/intent_to_file_controller.rb | 2 +- .../v1/forms/power_of_attorney_controller.rb | 4 +-- .../claims_api/v2/application_controller.rb | 4 +-- .../v2/veterans/intent_to_file_controller.rb | 2 +- .../claims_api/lib/bgs_service/bgs_helpers.rb | 31 +++++++++++++++++++ .../claims_api/lib/bgs_service/local_bgs.rb | 4 ++- ...ons.rb => miscellaneous_bgs_operations.rb} | 28 +---------------- .../spec/lib/claims_api/local_bgs_spec.rb | 18 +++++------ .../spec/models/veteran/service/user_spec.rb | 2 +- .../spec/requests/v1/claims_request_spec.rb | 2 +- .../v1/intent_to_file_request_spec.rb | 10 +++--- .../v1/power_of_attorney_request_spec.rb | 4 +-- .../requests/v1/rswag_claims_request_spec.rb | 2 +- .../rswag_power_of_attorney_request_spec.rb | 2 +- .../v2/veterans/claims_request_spec.rb | 4 +-- .../veterans/evidence_waiver_request_spec.rb | 10 +++--- .../veterans/intent_to_files_request_spec.rb | 4 +-- .../power_of_attorney_ind_request_spec.rb | 4 +-- .../power_of_attorney_org_request_spec.rb | 4 +-- .../power_of_attorney_request_spec.rb | 4 +-- .../v2/veterans/rswag_claims_request_spec.rb | 4 +-- .../v2/veterans/rswag_evidence_waiver_spec.rb | 6 ++-- .../rswag_intent_to_file_request_spec.rb | 6 ++-- .../veterans/rswag_power_of_attorney_spec.rb | 4 +-- .../spec/sidekiq/ews_vbms_sidekiq_spec.rb | 1 - modules/veteran/app/models/veteran/user.rb | 4 +-- .../veteran/spec/models/veteran/user_spec.rb | 2 +- 29 files changed, 89 insertions(+), 87 deletions(-) create mode 100644 modules/claims_api/lib/bgs_service/bgs_helpers.rb rename modules/claims_api/lib/bgs_service/{local_bgs/miscellaneous_operations.rb => miscellaneous_bgs_operations.rb} (86%) diff --git a/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb b/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb index d4b28ebb6fd..f58ff7bfe8d 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb @@ -130,7 +130,7 @@ def claims_service end def local_bgs_service - @local_bgs_service ||= ClaimsApi::LocalBGS.new( + @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( external_uid: target_veteran.participant_id, external_key: target_veteran.participant_id ) diff --git a/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb b/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb index 8835f5deebb..2a30fad9db7 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb @@ -95,7 +95,7 @@ def bgs_service def local_bgs_service external_key = target_veteran.participant_id.to_s - @local_bgs_service ||= ClaimsApi::LocalBGS.new( + @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( external_uid: external_key, external_key: ) diff --git a/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb b/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb index b70bddedc9e..cf0883d0c84 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'evss/intent_to_file/service' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' module ClaimsApi module V1 diff --git a/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb b/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb index cda49f9b83b..b66640370f6 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'bgs/power_of_attorney_verifier' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' module ClaimsApi module V1 @@ -227,7 +227,7 @@ def build_representative_info(poa_code) def find_by_ssn(ssn) # rubocop:disable Rails/DynamicFindBy - ClaimsApi::LocalBGS.new( + ClaimsApi::MiscellaneousBGSOperations.new( external_uid: target_veteran.participant_id, external_key: target_veteran.participant_id ).find_by_ssn(ssn) diff --git a/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb b/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb index 9bb20821666..f676302b93f 100644 --- a/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb @@ -5,7 +5,7 @@ require 'token_validation/v2/client' require 'claims_api/error/error_handler' require 'claims_api/claim_logger' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' require 'claims_api/form_schemas' require 'claims_api/v2/benefits_documents/service' require 'bd/bd' @@ -81,7 +81,7 @@ def bgs_service end def local_bgs_service - @local_bgs_service ||= ClaimsApi::LocalBGS.new( + @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( external_uid: target_veteran.participant_id, external_key: target_veteran.participant_id ) diff --git a/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb b/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb index 05a42d4fa04..d5ad0515e92 100644 --- a/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'claims_api/v2/params_validation/intent_to_file' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' module ClaimsApi module V2 diff --git a/modules/claims_api/lib/bgs_service/bgs_helpers.rb b/modules/claims_api/lib/bgs_service/bgs_helpers.rb new file mode 100644 index 00000000000..e4a9f3a2527 --- /dev/null +++ b/modules/claims_api/lib/bgs_service/bgs_helpers.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module ClaimsApi + module BGSHelpers + def convert_nil_values(options) + arg_strg = '' + options.each do |option| + arg = option[0].to_s.camelize(:lower) + arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") + end + arg_strg + end + + def validate_opts!(opts, required_keys) + keys = opts.keys.map(&:to_s) + required_keys = required_keys.map(&:to_s) + missing_keys = required_keys - keys + raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? + end + + def jrn + { + jrn_dt: Time.current.iso8601, + jrn_lctn_id: Settings.bgs.client_station_id, + jrn_status_type_cd: 'U', + jrn_user_id: Settings.bgs.client_username, + jrn_obj_id: Settings.bgs.application + } + end + end +end diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index b8cab2283bd..b1c42fae2e7 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -8,10 +8,12 @@ require 'claims_api/claim_logger' require 'claims_api/error/soap_error_handler' -require_relative 'local_bgs/miscellaneous_operations' +require 'bgs_service/bgs_helpers' module ClaimsApi class LocalBGS + include ClaimsApi::BGSHelpers + class << self def breakers_service url = URI.parse(Settings.bgs.url) diff --git a/modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb b/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb similarity index 86% rename from modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb rename to modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb index fce254c04e6..da5ef08d6e6 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs/miscellaneous_operations.rb +++ b/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb @@ -3,7 +3,7 @@ require 'claims_api/evss_bgs_mapper' module ClaimsApi - class LocalBGS + class MiscellaneousBGSOperations < ClaimsApi::LocalBGS def find_poa_by_participant_id(id) body = Nokogiri::XML::DocumentFragment.parse <<~EOXML @@ -168,31 +168,5 @@ def transform_bgs_claims_to_evss(claims) bgs_claim.map_and_build_object end end - - def convert_nil_values(options) - arg_strg = '' - options.each do |option| - arg = option[0].to_s.camelize(:lower) - arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") - end - arg_strg - end - - def validate_opts!(opts, required_keys) - keys = opts.keys.map(&:to_s) - required_keys = required_keys.map(&:to_s) - missing_keys = required_keys - keys - raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? - end - - def jrn - { - jrn_dt: Time.current.iso8601, - jrn_lctn_id: Settings.bgs.client_station_id, - jrn_status_type_cd: 'U', - jrn_user_id: Settings.bgs.client_username, - jrn_obj_id: Settings.bgs.application - } - end end end diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb index 482c897a6d8..82aa1c9e590 100644 --- a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require 'rails_helper' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' require 'claims_api/error/soap_error_handler' -describe ClaimsApi::LocalBGS do +describe ClaimsApi::MiscellaneousBGSOperations do subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } let(:soap_error_handler) { ClaimsApi::SoapErrorHandler.new } @@ -12,11 +12,9 @@ describe '#find_poa_by_participant_id' do it 'responds as expected, with extra ClaimsApi::Logger logging' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id') do - allow_any_instance_of(BGS::OrgWebService).to receive(:find_poa_history_by_ptcpnt_id).and_return({}) - # Events logged: - # 1: establish_ssl_connection - how long to establish the connection - # 2: connection_wsdl_get - duration of WSDL request cycle + # 1: connection_wsdl_get - duration of WSDL request cycle + # 2: built_request - how long to build the request # 3: connection_post - how long does the post itself take for the request cycle # 4: parsed_response - how long to parse the response expect(ClaimsApi::Logger).to receive(:log).exactly(4).times @@ -44,9 +42,7 @@ it 'triggers StatsD measurements' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id', allow_playback_repeats: true) do - allow_any_instance_of(BGS::OrgWebService).to receive(:find_poa_history_by_ptcpnt_id).and_return({}) - - %w[establish_ssl_connection connection_wsdl_get connection_post parsed_response].each do |event| + %w[connection_wsdl_get built_request connection_post parsed_response].each do |event| expect { subject.find_poa_by_participant_id('does-not-matter') } .to trigger_statsd_measure("api.claims_api.local_bgs.#{event}.duration") end @@ -107,9 +103,9 @@ ), key: 'PersonDTO').and_return(:bgs_unknown_error_message) begin - allow(soap_error_handler).to receive(:handle_errors) + allow(soap_error_handler).to receive(:handle_errors!) .with(:bgs_unknown_error_message).and_raise(Common::Exceptions::UnprocessableEntity) - ret = soap_error_handler.send(:handle_errors, :bgs_unknown_error_message) + ret = soap_error_handler.send(:handle_errors!, :bgs_unknown_error_message) expect(ret.class).to_be Array expect(ret.size).to eq 1 rescue => e diff --git a/modules/claims_api/spec/models/veteran/service/user_spec.rb b/modules/claims_api/spec/models/veteran/service/user_spec.rb index d6965951db2..759675303d6 100644 --- a/modules/claims_api/spec/models/veteran/service/user_spec.rb +++ b/modules/claims_api/spec/models/veteran/service/user_spec.rb @@ -15,7 +15,7 @@ ) end - let(:ows) { ClaimsApi::LocalBGS } + let(:ows) { ClaimsApi::MiscellaneousBGSOperations } it 'initializes from a user' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id') do diff --git a/modules/claims_api/spec/requests/v1/claims_request_spec.rb b/modules/claims_api/spec/requests/v1/claims_request_spec.rb index 433e24fca21..6e291420a59 100644 --- a/modules/claims_api/spec/requests/v1/claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/claims_request_spec.rb @@ -34,7 +34,7 @@ end let(:claims_service) do if Flipper.enabled? :claims_status_v1_bgs_enabled - ClaimsApi::LocalBGS + ClaimsApi::MiscellaneousBGSOperations else ClaimsApi::UnsynchronizedEVSSClaimService end diff --git a/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb b/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb index 3a1c3590cd4..a6f9d73da43 100644 --- a/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require_relative '../../rails_helper' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'Intent to file', type: :request do let(:headers) do @@ -151,7 +151,7 @@ describe 'handling the claimant fields' do context "when 'participant_claimant_id' is provided" do it 'that field and value are sent to BGS' do - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:insert_intent_to_file).with(hash_including(participant_claimant_id: '123')).and_return({}) mock_acg(scopes) do |auth_header| @@ -163,7 +163,7 @@ context "when 'claimant_ssn' is provided" do it 'that field and value are sent to BGS' do - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:insert_intent_to_file).with(hash_including(claimant_ssn: '123')).and_return({}) mock_acg(scopes) do |auth_header| @@ -179,7 +179,7 @@ end it "'participant_claimant_id' is set to the target_veteran.participant_id and sent to BGS " do - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:insert_intent_to_file).with(hash_including(participant_claimant_id: '999')).and_return({}) mock_acg(scopes) do |auth_header| @@ -190,7 +190,7 @@ context "when both 'participant_claimant_id' and 'claimant_ssn' are provided" do it "both 'participant_claimant_id' and 'claimant_ssn' are sent to BGS " do - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:insert_intent_to_file).with( hash_including( participant_claimant_id: '123', claimant_ssn: '456' diff --git a/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb b/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb index 7267e85502d..0a62a977c3b 100644 --- a/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require_relative '../../rails_helper' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'Power of Attorney ', type: :request do let(:headers) do @@ -20,7 +20,7 @@ profile: FactoryBot.build(:mpi_profile, participant_id: nil, participant_ids: %w[123456789 987654321]) ) end - let(:pws) { ClaimsApi::LocalBGS } + let(:pws) { ClaimsApi::MiscellaneousBGSOperations } before do stub_poa_verification diff --git a/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb b/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb index 7d83b317786..d28c9ed95a5 100644 --- a/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb @@ -127,7 +127,7 @@ before do |example| stub_poa_verification - allow_any_instance_of(ClaimsApi::LocalBGS).to receive(:all).and_raise( + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations).to receive(:all).and_raise( Common::Exceptions::ResourceNotFound.new(detail: 'The Resource was not found.') ) mock_acg(scopes) do diff --git a/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb b/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb index 267d47d327f..c423895299b 100644 --- a/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb @@ -7,7 +7,7 @@ require_relative '../../support/swagger_shared_components/v1' describe 'Power of Attorney', openapi_spec: 'modules/claims_api/app/swagger/claims_api/v1/swagger.json' do # rubocop:disable RSpec/DescribeClass - let(:pws) { ClaimsApi::LocalBGS } + let(:pws) { ClaimsApi::MiscellaneousBGSOperations } path '/forms/2122' do get 'Gets schema for POA form.' do diff --git a/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb index e67b76cfee5..f2b437ceba7 100644 --- a/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'Claims', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -23,7 +23,7 @@ participant_ids: []) ) end - let(:bcs) { ClaimsApi::LocalBGS } + let(:bcs) { ClaimsApi::MiscellaneousBGSOperations } let(:profile_for_claimant_on_behalf_of_veteran) do MPI::Responses::FindProfileResponse.new( status: :ok, diff --git a/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb index 1cf9d712b60..52da10c57df 100644 --- a/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb @@ -28,7 +28,7 @@ it 'returns a 200' do mock_ccg(scopes) do |auth_header| VCR.use_cassette('claims_api/bgs/benefit_claim/update_5103_200') do - allow_any_instance_of(ClaimsApi::LocalBGS) + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post sub_path, headers: auth_header @@ -52,7 +52,7 @@ it 'returns a 404' do mock_ccg(scopes) do |auth_header| VCR.use_cassette('claims_api/bgs/benefit_claim/find_bnft_claim_400') do - allow_any_instance_of(ClaimsApi::LocalBGS) + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post error_sub_path, headers: auth_header @@ -67,11 +67,11 @@ it 'passes for a valid type' do bgs_claim_response = build(:bgs_response_with_one_lc_status).to_h bgs_claim_response[:benefit_claim_details_dto][:bnft_claim_type_cd] = '140ISCD' - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_benefit_claim_details_by_benefit_claim_id).and_return(bgs_claim_response) mock_ccg(scopes) do |auth_header| - allow_any_instance_of(ClaimsApi::LocalBGS) + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post sub_path, params: { sponsorIcn: sponsor_id }, headers: auth_header @@ -83,7 +83,7 @@ it 'silently passes for an invalid type' do mock_ccg(scopes) do |auth_header| VCR.use_cassette('claims_api/bgs/benefit_claim/update_5103_200') do - allow_any_instance_of(ClaimsApi::LocalBGS) + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post sub_path, params: { sponsorIcn: sponsor_id }, headers: auth_header diff --git a/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb index 2867bc29e50..029155e258f 100644 --- a/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb @@ -3,12 +3,12 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'IntentToFiles', type: :request do let(:veteran_id) { '1013062086V794840' } let(:iws) do - ClaimsApi::LocalBGS + ClaimsApi::MiscellaneousBGSOperations end describe 'IntentToFiles' do diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb index 92689e629e2..4ca427d922b 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'Power Of Attorney', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -13,7 +13,7 @@ let(:individual_poa_code) { 'A1H' } let(:organization_poa_code) { '083' } let(:bgs_poa) { { person_org_name: "#{individual_poa_code} name-here" } } - let(:local_bgs) { ClaimsApi::LocalBGS } + let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } describe 'PowerOfAttorney' do before do diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb index 1e31d3f757c..10e199bcafb 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'Power Of Attorney', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -13,7 +13,7 @@ let(:individual_poa_code) { 'A1H' } let(:organization_poa_code) { '083' } let(:bgs_poa) { { person_org_name: "#{individual_poa_code} name-here" } } - let(:local_bgs) { ClaimsApi::LocalBGS } + let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } describe 'PowerOfAttorney' do before do diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb index 6aaf02c61a1..639f9a0400d 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' RSpec.describe 'Power Of Attorney', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -14,7 +14,7 @@ let(:individual_poa_code) { 'A1H' } let(:organization_poa_code) { '083' } let(:bgs_poa) { { person_org_name: "#{individual_poa_code} name-here" } } - let(:local_bgs) { ClaimsApi::LocalBGS } + let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } describe 'PowerOfAttorney' do before do diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb index 8dfb2d682cd..4e105cd75c3 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb @@ -3,12 +3,12 @@ require 'swagger_helper' require 'rails_helper' require_relative '../../../rails_helper' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' describe 'Claims', openapi_spec: Rswag::TextHelpers.new.claims_api_docs do let(:bcs) do - ClaimsApi::LocalBGS + ClaimsApi::MiscellaneousBGSOperations end before do diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb index a20b1632e0b..3540d9409f0 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb @@ -57,11 +57,11 @@ before do |example| bgs_claim_response = build(:bgs_response_with_one_lc_status).to_h - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_benefit_claim_details_by_benefit_claim_id).and_return(bgs_claim_response) mock_ccg(scopes) do - allow_any_instance_of(ClaimsApi::LocalBGS) + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) submit_request(example.metadata) end @@ -118,7 +118,7 @@ before do |example| mock_ccg(scopes) do - allow_any_instance_of(ClaimsApi::LocalBGS) + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_benefit_claim_details_by_benefit_claim_id).and_return(nil) submit_request(example.metadata) end diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb index 063e858df64..74038f1331c 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb @@ -60,7 +60,7 @@ Timecop.freeze(Time.zone.parse('2022-01-01T08:00:00Z')) mock_ccg(scopes) do - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_intent_to_file_by_ptcpnt_id_itf_type_cd).and_return(bgs_response) submit_request(example.metadata) @@ -116,7 +116,7 @@ before do |example| mock_ccg(scopes) do - expect_any_instance_of(ClaimsApi::LocalBGS) + expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) .to receive(:find_intent_to_file_by_ptcpnt_id_itf_type_cd).and_return(nil) submit_request(example.metadata) @@ -194,7 +194,7 @@ end before do |example| - allow_any_instance_of(ClaimsApi::LocalBGS).to receive(:insert_intent_to_file).and_return( + allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations).to receive(:insert_intent_to_file).and_return( stub_response ) diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb index 7158c3a3e56..556720a4ade 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb @@ -5,12 +5,12 @@ require 'rails_helper' require_relative '../../../rails_helper' require_relative '../../../support/swagger_shared_components/v2' -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' # doc generation for V2 ITFs temporarily disabled by API-13879 describe 'PowerOfAttorney', openapi_spec: Rswag::TextHelpers.new.claims_api_docs do - let(:local_bgs) { ClaimsApi::LocalBGS } + let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } path '/veterans/{veteranId}/power-of-attorney' do get 'Find current Power of Attorney for a Veteran.' do diff --git a/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb b/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb index 9ff0dd6924e..45f2037cbef 100644 --- a/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb +++ b/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb @@ -11,7 +11,6 @@ context 'when upload is successful' do it 'updates the Evidence Waiver Submission record' do - allow_any_instance_of(ClaimsApi::LocalBGS).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:upload!).and_return( { status: ClaimsApi::EvidenceWaiverSubmission::UPLOADED diff --git a/modules/veteran/app/models/veteran/user.rb b/modules/veteran/app/models/veteran/user.rb index 06906300a0e..cc73d80c075 100644 --- a/modules/veteran/app/models/veteran/user.rb +++ b/modules/veteran/app/models/veteran/user.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'bgs_service/local_bgs' +require 'bgs_service/miscellaneous_bgs_operations' # Veteran model module Veteran @@ -54,7 +54,7 @@ def bgs_service def local_bgs_service external_key = "#{@user.first_name} #{@user.last_name}" - @local_bgs_service ||= ClaimsApi::LocalBGS.new( + @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( external_uid: @user.mpi_icn, external_key: external_key.presence || @user.mpi_icn ) diff --git a/modules/veteran/spec/models/veteran/user_spec.rb b/modules/veteran/spec/models/veteran/user_spec.rb index e973af313e6..59f866b0cd2 100644 --- a/modules/veteran/spec/models/veteran/user_spec.rb +++ b/modules/veteran/spec/models/veteran/user_spec.rb @@ -6,7 +6,7 @@ context 'initialization' do let(:user) { FactoryBot.create(:user, :loa3) } - let(:ows) { ClaimsApi::LocalBGS } + let(:ows) { ClaimsApi::MiscellaneousBGSOperations } it 'initializes from a user' do VCR.use_cassette('bgs/claimant_web_service/find_poa_by_participant_id') do From a23918ad52e18bcb5b760a68468f45a4aa3b6115 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Mon, 22 Apr 2024 02:56:17 -0400 Subject: [PATCH 05/19] parameterize LocalBGS error handler --- .../claims_api/lib/bgs_service/local_bgs.rb | 52 ++++++++++++++----- .../claims_api/error/soap_error_handler.rb | 47 +++++++++-------- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index b1c42fae2e7..ae276beba91 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -14,18 +14,26 @@ module ClaimsApi class LocalBGS include ClaimsApi::BGSHelpers + Fault = + Data.define( + :code, + :string, + :message + ) + class << self def breakers_service url = URI.parse(Settings.bgs.url) - matcher = proc do |request_env| - request_env.url.host == url.host && - request_env.url.port == url.port && - request_env.url.path =~ /^#{url.path}/ - end + request_matcher = + proc do |request_env| + request_env.url.host == url.host && + request_env.url.port == url.port && + request_env.url.path =~ /^#{url.path}/ + end Breakers::Service.new( name: 'BGS/Claims', - request_matcher: matcher + request_matcher: ) end end @@ -71,11 +79,14 @@ def initialize( # rubocop:disable Metrics/MethodLength end def healthcheck(endpoint) - response = fetch_wsdl(endpoint) - response.status + fetch_wsdl(endpoint).status end - def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_response: true) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists + def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists + endpoint:, action:, body:, key: nil, namespaces: {}, + error_handler: ClaimsApi::SoapErrorHandler, + transform_response: true + ) begin wsdl = log_duration(event: 'connection_wsdl_get', endpoint:) do @@ -99,7 +110,6 @@ def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_ log_duration(event: 'connection_post', endpoint:, action:) do connection.post(endpoint) do |req| req.body = request_body - req.headers.merge!( 'Content-Type' => 'text/xml;charset=UTF-8', 'Host' => "#{@env}.vba.va.gov", @@ -116,7 +126,7 @@ def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_ log_duration(event: 'parsed_response', key:) do response_body = Hash.from_xml(response.body) - soap_error_handler.handle_errors!(response_body) + handle_errors!(response_body, error_handler:) unwrap_response_body( response_body, @@ -190,8 +200,24 @@ def unwrap_response_body(body, action:, key:, transform:) end end - def soap_error_handler - ClaimsApi::SoapErrorHandler.new + def handle_errors!(body, error_handler:) + data = body.dig('Envelope', 'Body', 'Fault').to_h + return if data.blank? + + message = + data.dig('detail', 'MessageException') || + data.dig('detail', 'MessageFaultException') + + fault = + Fault.new( + code: data['faultcode'].to_s.split(':').last, + string: data['faultstring'], + message: + ) + + error_handler.handle_errors!( + fault + ) end def connection diff --git a/modules/claims_api/lib/claims_api/error/soap_error_handler.rb b/modules/claims_api/lib/claims_api/error/soap_error_handler.rb index 736981400af..5c4d900ee10 100644 --- a/modules/claims_api/lib/claims_api/error/soap_error_handler.rb +++ b/modules/claims_api/lib/claims_api/error/soap_error_handler.rb @@ -1,30 +1,24 @@ # frozen_string_literal: true module ClaimsApi + # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm + # + # TODO: Some (or all) of these cases should be handled in consumers and not in + # a central location. class SoapErrorHandler - # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm - - def handle_errors!(body) - @hash = body - - return if @hash&.dig('Envelope', 'Body', 'Fault').blank? - - get_fault_info + class << self + def handle_errors!(fault) + new(fault).handle_errors! + end end - def get_fault_info - fault = @hash&.dig('Envelope', 'Body', 'Fault') - @fault_code = fault&.dig('faultcode')&.split(':')&.dig(1) - @fault_string = fault&.dig('faultstring') - @fault_message = fault&.dig('detail', 'MessageException') || fault&.dig('detail', 'MessageFaultException') - return {} if @fault_string.include?('IntentToFileWebService') && @fault_string.include?('not found') - - get_exception + def initialize(fault) + @fault = fault end - private + def handle_errors! + return if not_error? - def get_exception if not_found? raise ::Common::Exceptions::ResourceNotFound.new(detail: 'Resource not found.') elsif bnft_claim_not_found? @@ -39,16 +33,23 @@ def get_exception end end + private + + def not_error? + @fault.string.include?('IntentToFileWebService') && + @fault.string.include?('not found') + end + def not_found? errors = ['bnftClaimId-->bnftClaimId/text()', 'not found', 'No Person found'] - has_errors = errors.any? { |error| @fault_string.include? error } + has_errors = errors.any? { |error| @fault.string.include? error } soap_logging('404') if has_errors has_errors end def bnft_claim_not_found? errors = ['No BnftClaim found'] - has_errors = errors.any? { |error| @fault_string.include? error } + has_errors = errors.any? { |error| @fault.string.include? error } soap_logging('404') if has_errors has_errors end @@ -57,7 +58,7 @@ def unprocessable? errors = ['java.sql', 'MessageException', 'Validation errors', 'Exception Description', 'does not have necessary info', 'Error committing transaction', 'Transaction Rolledback', 'Unexpected error', 'XML reader error', 'could not be converted'] - has_errors = errors.any? { |error| @fault_string.include? error } + has_errors = errors.any? { |error| @fault.string.include? error } soap_logging('422') if has_errors has_errors end @@ -65,8 +66,8 @@ def unprocessable? def soap_logging(status_code) ClaimsApi::Logger.log('soap_error_handler', detail: "Returning #{status_code} via local_bgs & soap_error_handler, " \ - "fault_string: #{@fault_string}, with message: #{@fault_message}, " \ - "and fault_code: #{@fault_code}.") + "fault_string: #{@fault.string}, with message: #{@fault.message}, " \ + "and fault_code: #{@fault.code}.") end end end From 8a482163bb176ef082e08ea28e0e4a37cbd01cbf Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Tue, 23 Apr 2024 02:49:10 -0400 Subject: [PATCH 06/19] BGSClient refactors calling BGS + LocalBGS adapted for compat - add comments about private access while backwards compat (`LocalBGS`) & intentional (`BGSClient`) --- .../app/clients/claims_api/bgs_client.rb | 62 +++++ .../bgs_client/service_action/definition.rb | 27 ++ .../bgs_client/service_action/external_id.rb | 15 ++ .../bgs_client/service_action/request.rb | 207 +++++++++++++++ .../claims_api/lib/bgs_service/local_bgs.rb | 235 +++--------------- .../spec/lib/claims_api/local_bgs_spec.rb | 7 +- 6 files changed, 350 insertions(+), 203 deletions(-) create mode 100644 modules/claims_api/app/clients/claims_api/bgs_client.rb create mode 100644 modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb create mode 100644 modules/claims_api/app/clients/claims_api/bgs_client/service_action/external_id.rb create mode 100644 modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb diff --git a/modules/claims_api/app/clients/claims_api/bgs_client.rb b/modules/claims_api/app/clients/claims_api/bgs_client.rb new file mode 100644 index 00000000000..2c2682c80fd --- /dev/null +++ b/modules/claims_api/app/clients/claims_api/bgs_client.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module ClaimsApi + module BGSClient + class << self + def perform_request(definition:, body:, external_id: ServiceAction::ExternalId::DEFAULT) + ServiceAction.const_get(:Request) + .new(definition:, external_id:) + .perform(body) + end + + def healthcheck(service_path) + connection = build_connection + response = fetch_wsdl(connection, service_path) + response.status + end + + def breakers_service + url = URI.parse(Settings.bgs.url) + request_matcher = + proc do |request_env| + request_env.url.host == url.host && + request_env.url.port == url.port && + request_env.url.path =~ /^#{url.path}/ + end + + Breakers::Service.new( + name: 'BGS/Claims', + request_matcher: + ) + end + + private + + def fetch_wsdl(connection, service_path) + connection.get(service_path) do |req| + req.params['WSDL'] = nil + end + end + + def build_connection + ssl_verify_mode = + if Settings.bgs.ssl_verify_mode == 'none' + OpenSSL::SSL::VERIFY_NONE + else + OpenSSL::SSL::VERIFY_PEER + end + + Faraday.new(Settings.bgs.url) do |conn| + conn.ssl.verify_mode = ssl_verify_mode + conn.options.timeout = Settings.bgs.timeout || 120 + conn.use :breakers + + conn.headers.merge!( + 'Content-Type' => 'text/xml;charset=UTF-8', + 'Host' => "#{Settings.bgs.env}.vba.va.gov" + ) + end + end + end + end +end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb new file mode 100644 index 00000000000..bd8980fe020 --- /dev/null +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ClaimsApi + module BGSClient + module ServiceAction + Definition = + Data.define( + :service_path, + :service_namespaces, + :action_name + ) + + module Definition::ManageRepresentativeService + service_definition = { + service_path: 'VDC/ManageRepresentativeService', + service_namespaces: { 'data' => '/data' } + } + + ReadPoaRequest = + Definition.new( + action_name: 'readPOARequest', + **service_definition + ) + end + end + end +end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/external_id.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/external_id.rb new file mode 100644 index 00000000000..2509fc58386 --- /dev/null +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/external_id.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module ClaimsApi + module BGSClient + module ServiceAction + class ExternalId < Data.define(:external_uid, :external_key) + DEFAULT = + new( + external_uid: Settings.bgs.external_uid, + external_key: Settings.bgs.external_key + ) + end + end + end +end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb new file mode 100644 index 00000000000..e4a702fe8d4 --- /dev/null +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +require 'claims_api/claim_logger' + +module ClaimsApi + module BGSClient + module ServiceAction + # `private_constant` is used to prevent inheritance that could eventually + # tempt someone to add extraneous behavior to this, the parent class. + # Consumers should instead directly interface with + # `BGSClient.perform_request`, which maintains the sole responsibility of + # making a request to BGS. + private_constant :Request + + class Request + def initialize(definition:, external_id:) + @definition = definition + @external_id = external_id + end + + def perform(body) # rubocop:disable Metrics/MethodLength + begin + wsdl = + log_duration('connection_wsdl_get') do + BGSClient.send( + :fetch_wsdl, + connection, + @definition.service_path + ).body + end + + request_body = + log_duration('built_request') do + wsdl_body = Hash.from_xml(wsdl) + namespace = wsdl_body.dig('definitions', 'targetNamespace').to_s + build_request_body(body, namespace:) + end + + response = + log_duration('connection_post') do + connection.post(@definition.service_path) do |req| + req.headers['Soapaction'] = %("#{@definition.action_name}") + req.body = request_body + end + end + rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e + detail = "local BGS Faraday Timeout: #{e.message}" + ClaimsApi::Logger.log('local_bgs', retry: true, detail:) + raise ::Common::Exceptions::BadGateway + end + + log_duration('parsed_response') do + response_body = Hash.from_xml(response.body) + action_body = response_body.dig('Envelope', 'Body').to_h + fault = get_fault(action_body) + + if fault + Result.new( + success: false, + value: fault + ) + else + key = "#{@definition.action_name}Response" + value = action_body[key].to_h + + Result.new( + success: true, + value: + ) + end + end + end + + private + + def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength + namespaces = + @definition.service_namespaces.map do |aliaz, path| + uri = URI(namespace) + uri.path = path + %(xmlns:#{aliaz}="#{uri}") + end + + client_ip = + if Rails.env.test? + # For all intents and purposes, BGS behaves identically no matter + # what IP we provide it. So in a test environment, let's just give + # it a fake IP so that cassette matching isn't defeated on CI and + # everyone's computer. + '127.0.0.1' + else + Socket + .ip_address_list + .detect(&:ipv4_private?) + .ip_address + end + + <<~EOXML + + + + + + #{Settings.bgs.client_username} + + + #{client_ip} + #{Settings.bgs.client_station_id} + #{Settings.bgs.application} + #{@external_id.external_uid} + #{@external_id.external_key} + + + + + #{body} + + + EOXML + end + + def get_fault(body) + fault = body['Fault'].to_h + return if fault.blank? + + message = + fault.dig('detail', 'MessageException') || + fault.dig('detail', 'MessageFaultException') + + Fault.new( + code: fault['faultcode'].to_s.split(':').last, + string: fault['faultstring'], + message: + ) + end + + def connection + @connection ||= BGSClient.send(:build_connection) + end + + def log_duration(event_name) + start = now + result = yield + finish = now + + duration = (finish - start).round(4) + event = { + # event should be first key in log, duration last + event: event_name, + endpoint: @definition.service_path, + action: @definition.action_name, + duration: + } + + ClaimsApi::Logger.log('local_bgs', **event) + metric = "api.claims_api.local_bgs.#{event_name}.duration" + StatsD.measure(metric, duration, tags: {}) + + result + end + + def now + ::Process.clock_gettime( + ::Process::CLOCK_MONOTONIC + ) + end + + Fault = + Data.define( + :code, + :string, + :message + ) + + # Tiny subset of the API for `Dry::Monads[:result]`. Chose this + # particularly because some SOAP `500` really isn't error-like, and it + # is awkward to wrap exception handling for non-exceptional cases. + class Result + attr_reader :value + + def initialize(value:, success:) + @value = value + @success = success + end + + def success? + @success + end + + def failure? + !success? + end + end + end + end + end +end diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index ae276beba91..2d3b4f79523 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -14,233 +14,68 @@ module ClaimsApi class LocalBGS include ClaimsApi::BGSHelpers - Fault = - Data.define( - :code, - :string, - :message - ) - class << self def breakers_service - url = URI.parse(Settings.bgs.url) - request_matcher = - proc do |request_env| - request_env.url.host == url.host && - request_env.url.port == url.port && - request_env.url.path =~ /^#{url.path}/ - end - - Breakers::Service.new( - name: 'BGS/Claims', - request_matcher: - ) + BGSClient.breakers_service end end - attr_accessor :external_uid, :external_key + attr_reader :external_id - def initialize( # rubocop:disable Metrics/MethodLength + def initialize( external_uid: Settings.bgs.external_uid, external_key: Settings.bgs.external_key ) - @client_ip = - if Rails.env.test? - # For all intents and purposes, BGS behaves identically no matter what - # IP we provide it. So in a test environment, let's just give it a - # fake so that cassette matching isn't defeated on CI and everyone's - # computer. - '127.0.0.1' - else - Socket - .ip_address_list - .detect(&:ipv4_private?) - .ip_address - end - - @ssl_verify_mode = - if Settings.bgs.ssl_verify_mode == 'none' - OpenSSL::SSL::VERIFY_NONE - else - OpenSSL::SSL::VERIFY_PEER - end - - @application = Settings.bgs.application - @client_station_id = Settings.bgs.client_station_id - @client_username = Settings.bgs.client_username - @env = Settings.bgs.env - @mock_response_location = Settings.bgs.mock_response_location - @mock_responses = Settings.bgs.mock_responses - @external_uid = external_uid - @external_key = external_key - @forward_proxy_url = Settings.bgs.url - @timeout = Settings.bgs.timeout || 120 - @url = Settings.bgs.url + @external_id = + BGSClient::ServiceAction::ExternalId.new( + external_uid:, + external_key: + ) end def healthcheck(endpoint) - fetch_wsdl(endpoint).status + BGSClient.healthcheck(endpoint) end def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists - endpoint:, action:, body:, key: nil, namespaces: {}, - error_handler: ClaimsApi::SoapErrorHandler, - transform_response: true + endpoint:, action:, body:, key: nil, + namespaces: {}, transform_response: true ) - begin - wsdl = - log_duration(event: 'connection_wsdl_get', endpoint:) do - fetch_wsdl(endpoint).body - end - - request_body = - log_duration(event: 'built_request', endpoint:, action:) do - wsdl_body = Hash.from_xml(wsdl) - namespace = wsdl_body.dig('definitions', 'targetNamespace').to_s - - wrap_request_body( - body, - action:, - namespace:, - namespaces: - ) - end - - response = - log_duration(event: 'connection_post', endpoint:, action:) do - connection.post(endpoint) do |req| - req.body = request_body - req.headers.merge!( - 'Content-Type' => 'text/xml;charset=UTF-8', - 'Host' => "#{@env}.vba.va.gov", - 'Soapaction' => %("#{action}") - ) - end - end - rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e - detail = "local BGS Faraday Timeout: #{e.message}" - ClaimsApi::Logger.log('local_bgs', retry: true, detail:) - - raise ::Common::Exceptions::BadGateway - end - - log_duration(event: 'parsed_response', key:) do - response_body = Hash.from_xml(response.body) - handle_errors!(response_body, error_handler:) - - unwrap_response_body( - response_body, - transform: transform_response, - action:, - key: + definition = + BGSClient::ServiceAction::Definition.new( + service_path: endpoint, + service_namespaces: namespaces, + action_name: action ) - end - end - - private - - def fetch_wsdl(endpoint) - connection.get(endpoint) do |req| - req.params['WSDL'] = nil - end - end - def wrap_request_body(body, action:, namespace:, namespaces:) # rubocop:disable Metrics/MethodLength - namespaces = - namespaces.map do |aliaz, path| - uri = URI(namespace) - uri.path = path - %(xmlns:#{aliaz}="#{uri}") - end + request = + BGSClient::ServiceAction.const_get(:Request).new( + definition:, + external_id: + ) - <<~EOXML - - - - - - #{@client_username} - - - #{@client_ip} - #{@client_station_id} - #{@application} - #{@external_uid} - #{@external_key} - - - - - #{body} - - - EOXML - end + result = request.perform(body) - def unwrap_response_body(body, action:, key:, transform:) - keys = ['Envelope', 'Body', "#{action}Response"] - keys << key if key.present? + if result.success? + value = result.value.to_h + value = value[key].to_h if key.present? - body.dig(*keys).to_h.tap do |value| - if transform - value.deep_transform_keys! do |key| - key.underscore.to_sym + if transform_response + request.send(:log_duration, 'transformed_response') do + value.deep_transform_keys! do |key| + key.underscore.to_sym + end end end - end - end - - def handle_errors!(body, error_handler:) - data = body.dig('Envelope', 'Body', 'Fault').to_h - return if data.blank? - - message = - data.dig('detail', 'MessageException') || - data.dig('detail', 'MessageFaultException') - fault = - Fault.new( - code: data['faultcode'].to_s.split(':').last, - string: data['faultstring'], - message: - ) + return value + end - error_handler.handle_errors!( - fault + SoapErrorHandler.handle_errors!( + result.value ) - end - - def connection - @connection ||= - Faraday.new(@url, ssl: { verify_mode: @ssl_verify_mode }) do |f| - f.use :breakers - f.adapter Faraday.default_adapter - f.options.timeout = @timeout - end - end - - def log_duration(event: 'default', **extra_params) - # Who are we to question sidekiq's use of CLOCK_MONOTONIC to avoid negative durations? - # https://github.com/sidekiq/sidekiq/issues/3999 - start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - result = yield - duration = (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start_time).round(4) - # event should be first key in log, duration last - event_for_log = { event: }.merge(extra_params).merge({ duration: }) - ClaimsApi::Logger.log 'local_bgs', **event_for_log - StatsD.measure("api.claims_api.local_bgs.#{event}.duration", duration, tags: {}) - result + {} end end end diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb index 82aa1c9e590..0ea9da18b59 100644 --- a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb @@ -7,7 +7,7 @@ describe ClaimsApi::MiscellaneousBGSOperations do subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } - let(:soap_error_handler) { ClaimsApi::SoapErrorHandler.new } + let(:soap_error_handler) { ClaimsApi::SoapErrorHandler } describe '#find_poa_by_participant_id' do it 'responds as expected, with extra ClaimsApi::Logger logging' do @@ -17,7 +17,8 @@ # 2: built_request - how long to build the request # 3: connection_post - how long does the post itself take for the request cycle # 4: parsed_response - how long to parse the response - expect(ClaimsApi::Logger).to receive(:log).exactly(4).times + # 5: transformed_response - how long to transform the response + expect(ClaimsApi::Logger).to receive(:log).exactly(5).times result = subject.find_poa_by_participant_id('does-not-matter') expect(result).to be_a Hash expect(result[:end_date]).to eq '08/26/2020' @@ -42,7 +43,7 @@ it 'triggers StatsD measurements' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id', allow_playback_repeats: true) do - %w[connection_wsdl_get built_request connection_post parsed_response].each do |event| + %w[connection_wsdl_get built_request connection_post parsed_response transformed_response].each do |event| expect { subject.find_poa_by_participant_id('does-not-matter') } .to trigger_statsd_measure("api.claims_api.local_bgs.#{event}.duration") end From 637823fc33828c2c5699eb4f4c39d36e1db54560 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Tue, 23 Apr 2024 04:07:08 -0400 Subject: [PATCH 07/19] remix miscellaneous LocalBGS operations --- .../claims_api/v1/application_controller.rb | 2 +- .../app/controllers/claims_api/v1/forms/base.rb | 2 +- .../claims_api/v1/forms/intent_to_file_controller.rb | 2 +- .../v1/forms/power_of_attorney_controller.rb | 4 ++-- .../claims_api/v2/application_controller.rb | 4 ++-- .../v2/veterans/intent_to_file_controller.rb | 2 +- modules/claims_api/lib/bgs_service/local_bgs.rb | 2 ++ .../lib/bgs_service/miscellaneous_bgs_operations.rb | 2 +- .../claims_api/spec/lib/claims_api/local_bgs_spec.rb | 4 ++-- .../spec/models/veteran/service/user_spec.rb | 2 +- .../claims_api/spec/requests/v1/claims_request_spec.rb | 2 +- .../spec/requests/v1/intent_to_file_request_spec.rb | 10 +++++----- .../spec/requests/v1/power_of_attorney_request_spec.rb | 4 ++-- .../spec/requests/v1/rswag_claims_request_spec.rb | 2 +- .../v1/rswag_power_of_attorney_request_spec.rb | 2 +- .../spec/requests/v2/veterans/claims_request_spec.rb | 4 ++-- .../v2/veterans/evidence_waiver_request_spec.rb | 10 +++++----- .../v2/veterans/intent_to_files_request_spec.rb | 4 ++-- .../v2/veterans/power_of_attorney_ind_request_spec.rb | 4 ++-- .../v2/veterans/power_of_attorney_org_request_spec.rb | 4 ++-- .../v2/veterans/power_of_attorney_request_spec.rb | 4 ++-- .../requests/v2/veterans/rswag_claims_request_spec.rb | 4 ++-- .../requests/v2/veterans/rswag_evidence_waiver_spec.rb | 6 +++--- .../v2/veterans/rswag_intent_to_file_request_spec.rb | 6 +++--- .../v2/veterans/rswag_power_of_attorney_spec.rb | 4 ++-- .../claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb | 1 + modules/veteran/app/models/veteran/user.rb | 4 ++-- modules/veteran/spec/models/veteran/user_spec.rb | 2 +- 28 files changed, 53 insertions(+), 50 deletions(-) diff --git a/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb b/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb index f58ff7bfe8d..d4b28ebb6fd 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/application_controller.rb @@ -130,7 +130,7 @@ def claims_service end def local_bgs_service - @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( + @local_bgs_service ||= ClaimsApi::LocalBGS.new( external_uid: target_veteran.participant_id, external_key: target_veteran.participant_id ) diff --git a/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb b/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb index 2a30fad9db7..8835f5deebb 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/forms/base.rb @@ -95,7 +95,7 @@ def bgs_service def local_bgs_service external_key = target_veteran.participant_id.to_s - @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( + @local_bgs_service ||= ClaimsApi::LocalBGS.new( external_uid: external_key, external_key: ) diff --git a/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb b/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb index cf0883d0c84..b70bddedc9e 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/forms/intent_to_file_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'evss/intent_to_file/service' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' module ClaimsApi module V1 diff --git a/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb b/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb index b66640370f6..cda49f9b83b 100644 --- a/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v1/forms/power_of_attorney_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'bgs/power_of_attorney_verifier' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' module ClaimsApi module V1 @@ -227,7 +227,7 @@ def build_representative_info(poa_code) def find_by_ssn(ssn) # rubocop:disable Rails/DynamicFindBy - ClaimsApi::MiscellaneousBGSOperations.new( + ClaimsApi::LocalBGS.new( external_uid: target_veteran.participant_id, external_key: target_veteran.participant_id ).find_by_ssn(ssn) diff --git a/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb b/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb index f676302b93f..9bb20821666 100644 --- a/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v2/application_controller.rb @@ -5,7 +5,7 @@ require 'token_validation/v2/client' require 'claims_api/error/error_handler' require 'claims_api/claim_logger' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' require 'claims_api/form_schemas' require 'claims_api/v2/benefits_documents/service' require 'bd/bd' @@ -81,7 +81,7 @@ def bgs_service end def local_bgs_service - @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( + @local_bgs_service ||= ClaimsApi::LocalBGS.new( external_uid: target_veteran.participant_id, external_key: target_veteran.participant_id ) diff --git a/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb b/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb index d5ad0515e92..05a42d4fa04 100644 --- a/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v2/veterans/intent_to_file_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'claims_api/v2/params_validation/intent_to_file' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' module ClaimsApi module V2 diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index 2d3b4f79523..ede89387e4d 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -9,9 +9,11 @@ require 'claims_api/claim_logger' require 'claims_api/error/soap_error_handler' require 'bgs_service/bgs_helpers' +require 'bgs_service/miscellaneous_bgs_operations' module ClaimsApi class LocalBGS + include ClaimsApi::MiscellaneousBGSOperations include ClaimsApi::BGSHelpers class << self diff --git a/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb b/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb index da5ef08d6e6..1c0e6bd41c7 100644 --- a/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb +++ b/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb @@ -3,7 +3,7 @@ require 'claims_api/evss_bgs_mapper' module ClaimsApi - class MiscellaneousBGSOperations < ClaimsApi::LocalBGS + module MiscellaneousBGSOperations # rubocop:disable Metrics/ModuleLength def find_poa_by_participant_id(id) body = Nokogiri::XML::DocumentFragment.parse <<~EOXML diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb index 0ea9da18b59..b8e94b87dfe 100644 --- a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require 'rails_helper' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' require 'claims_api/error/soap_error_handler' -describe ClaimsApi::MiscellaneousBGSOperations do +describe ClaimsApi::LocalBGS do subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } let(:soap_error_handler) { ClaimsApi::SoapErrorHandler } diff --git a/modules/claims_api/spec/models/veteran/service/user_spec.rb b/modules/claims_api/spec/models/veteran/service/user_spec.rb index 759675303d6..d6965951db2 100644 --- a/modules/claims_api/spec/models/veteran/service/user_spec.rb +++ b/modules/claims_api/spec/models/veteran/service/user_spec.rb @@ -15,7 +15,7 @@ ) end - let(:ows) { ClaimsApi::MiscellaneousBGSOperations } + let(:ows) { ClaimsApi::LocalBGS } it 'initializes from a user' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id') do diff --git a/modules/claims_api/spec/requests/v1/claims_request_spec.rb b/modules/claims_api/spec/requests/v1/claims_request_spec.rb index 6e291420a59..433e24fca21 100644 --- a/modules/claims_api/spec/requests/v1/claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/claims_request_spec.rb @@ -34,7 +34,7 @@ end let(:claims_service) do if Flipper.enabled? :claims_status_v1_bgs_enabled - ClaimsApi::MiscellaneousBGSOperations + ClaimsApi::LocalBGS else ClaimsApi::UnsynchronizedEVSSClaimService end diff --git a/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb b/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb index a6f9d73da43..3a1c3590cd4 100644 --- a/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/intent_to_file_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require_relative '../../rails_helper' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'Intent to file', type: :request do let(:headers) do @@ -151,7 +151,7 @@ describe 'handling the claimant fields' do context "when 'participant_claimant_id' is provided" do it 'that field and value are sent to BGS' do - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:insert_intent_to_file).with(hash_including(participant_claimant_id: '123')).and_return({}) mock_acg(scopes) do |auth_header| @@ -163,7 +163,7 @@ context "when 'claimant_ssn' is provided" do it 'that field and value are sent to BGS' do - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:insert_intent_to_file).with(hash_including(claimant_ssn: '123')).and_return({}) mock_acg(scopes) do |auth_header| @@ -179,7 +179,7 @@ end it "'participant_claimant_id' is set to the target_veteran.participant_id and sent to BGS " do - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:insert_intent_to_file).with(hash_including(participant_claimant_id: '999')).and_return({}) mock_acg(scopes) do |auth_header| @@ -190,7 +190,7 @@ context "when both 'participant_claimant_id' and 'claimant_ssn' are provided" do it "both 'participant_claimant_id' and 'claimant_ssn' are sent to BGS " do - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:insert_intent_to_file).with( hash_including( participant_claimant_id: '123', claimant_ssn: '456' diff --git a/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb b/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb index 0a62a977c3b..7267e85502d 100644 --- a/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/power_of_attorney_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require_relative '../../rails_helper' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'Power of Attorney ', type: :request do let(:headers) do @@ -20,7 +20,7 @@ profile: FactoryBot.build(:mpi_profile, participant_id: nil, participant_ids: %w[123456789 987654321]) ) end - let(:pws) { ClaimsApi::MiscellaneousBGSOperations } + let(:pws) { ClaimsApi::LocalBGS } before do stub_poa_verification diff --git a/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb b/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb index d28c9ed95a5..7d83b317786 100644 --- a/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/rswag_claims_request_spec.rb @@ -127,7 +127,7 @@ before do |example| stub_poa_verification - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations).to receive(:all).and_raise( + allow_any_instance_of(ClaimsApi::LocalBGS).to receive(:all).and_raise( Common::Exceptions::ResourceNotFound.new(detail: 'The Resource was not found.') ) mock_acg(scopes) do diff --git a/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb b/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb index c423895299b..267d47d327f 100644 --- a/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb +++ b/modules/claims_api/spec/requests/v1/rswag_power_of_attorney_request_spec.rb @@ -7,7 +7,7 @@ require_relative '../../support/swagger_shared_components/v1' describe 'Power of Attorney', openapi_spec: 'modules/claims_api/app/swagger/claims_api/v1/swagger.json' do # rubocop:disable RSpec/DescribeClass - let(:pws) { ClaimsApi::MiscellaneousBGSOperations } + let(:pws) { ClaimsApi::LocalBGS } path '/forms/2122' do get 'Gets schema for POA form.' do diff --git a/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb index f2b437ceba7..e67b76cfee5 100644 --- a/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/claims_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'Claims', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -23,7 +23,7 @@ participant_ids: []) ) end - let(:bcs) { ClaimsApi::MiscellaneousBGSOperations } + let(:bcs) { ClaimsApi::LocalBGS } let(:profile_for_claimant_on_behalf_of_veteran) do MPI::Responses::FindProfileResponse.new( status: :ok, diff --git a/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb index 52da10c57df..1cf9d712b60 100644 --- a/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/evidence_waiver_request_spec.rb @@ -28,7 +28,7 @@ it 'returns a 200' do mock_ccg(scopes) do |auth_header| VCR.use_cassette('claims_api/bgs/benefit_claim/update_5103_200') do - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + allow_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post sub_path, headers: auth_header @@ -52,7 +52,7 @@ it 'returns a 404' do mock_ccg(scopes) do |auth_header| VCR.use_cassette('claims_api/bgs/benefit_claim/find_bnft_claim_400') do - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + allow_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post error_sub_path, headers: auth_header @@ -67,11 +67,11 @@ it 'passes for a valid type' do bgs_claim_response = build(:bgs_response_with_one_lc_status).to_h bgs_claim_response[:benefit_claim_details_dto][:bnft_claim_type_cd] = '140ISCD' - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_benefit_claim_details_by_benefit_claim_id).and_return(bgs_claim_response) mock_ccg(scopes) do |auth_header| - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + allow_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post sub_path, params: { sponsorIcn: sponsor_id }, headers: auth_header @@ -83,7 +83,7 @@ it 'silently passes for an invalid type' do mock_ccg(scopes) do |auth_header| VCR.use_cassette('claims_api/bgs/benefit_claim/update_5103_200') do - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + allow_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) post sub_path, params: { sponsorIcn: sponsor_id }, headers: auth_header diff --git a/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb index 029155e258f..2867bc29e50 100644 --- a/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/intent_to_files_request_spec.rb @@ -3,12 +3,12 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'IntentToFiles', type: :request do let(:veteran_id) { '1013062086V794840' } let(:iws) do - ClaimsApi::MiscellaneousBGSOperations + ClaimsApi::LocalBGS end describe 'IntentToFiles' do diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb index 4ca427d922b..92689e629e2 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'Power Of Attorney', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -13,7 +13,7 @@ let(:individual_poa_code) { 'A1H' } let(:organization_poa_code) { '083' } let(:bgs_poa) { { person_org_name: "#{individual_poa_code} name-here" } } - let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } + let(:local_bgs) { ClaimsApi::LocalBGS } describe 'PowerOfAttorney' do before do diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb index 10e199bcafb..1e31d3f757c 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'Power Of Attorney', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -13,7 +13,7 @@ let(:individual_poa_code) { 'A1H' } let(:organization_poa_code) { '083' } let(:bgs_poa) { { person_org_name: "#{individual_poa_code} name-here" } } - let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } + let(:local_bgs) { ClaimsApi::LocalBGS } describe 'PowerOfAttorney' do before do diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb index 639f9a0400d..6aaf02c61a1 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_request_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' require_relative '../../../rails_helper' require 'token_validation/v2/client' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' RSpec.describe 'Power Of Attorney', type: :request do let(:veteran_id) { '1013062086V794840' } @@ -14,7 +14,7 @@ let(:individual_poa_code) { 'A1H' } let(:organization_poa_code) { '083' } let(:bgs_poa) { { person_org_name: "#{individual_poa_code} name-here" } } - let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } + let(:local_bgs) { ClaimsApi::LocalBGS } describe 'PowerOfAttorney' do before do diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb index 4e105cd75c3..8dfb2d682cd 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_claims_request_spec.rb @@ -3,12 +3,12 @@ require 'swagger_helper' require 'rails_helper' require_relative '../../../rails_helper' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' describe 'Claims', openapi_spec: Rswag::TextHelpers.new.claims_api_docs do let(:bcs) do - ClaimsApi::MiscellaneousBGSOperations + ClaimsApi::LocalBGS end before do diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb index 3540d9409f0..a20b1632e0b 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_evidence_waiver_spec.rb @@ -57,11 +57,11 @@ before do |example| bgs_claim_response = build(:bgs_response_with_one_lc_status).to_h - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_benefit_claim_details_by_benefit_claim_id).and_return(bgs_claim_response) mock_ccg(scopes) do - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + allow_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_by_ssn).and_return({ file_nbr: '123456780' }) submit_request(example.metadata) end @@ -118,7 +118,7 @@ before do |example| mock_ccg(scopes) do - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + allow_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_benefit_claim_details_by_benefit_claim_id).and_return(nil) submit_request(example.metadata) end diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb index 74038f1331c..063e858df64 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_intent_to_file_request_spec.rb @@ -60,7 +60,7 @@ Timecop.freeze(Time.zone.parse('2022-01-01T08:00:00Z')) mock_ccg(scopes) do - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_intent_to_file_by_ptcpnt_id_itf_type_cd).and_return(bgs_response) submit_request(example.metadata) @@ -116,7 +116,7 @@ before do |example| mock_ccg(scopes) do - expect_any_instance_of(ClaimsApi::MiscellaneousBGSOperations) + expect_any_instance_of(ClaimsApi::LocalBGS) .to receive(:find_intent_to_file_by_ptcpnt_id_itf_type_cd).and_return(nil) submit_request(example.metadata) @@ -194,7 +194,7 @@ end before do |example| - allow_any_instance_of(ClaimsApi::MiscellaneousBGSOperations).to receive(:insert_intent_to_file).and_return( + allow_any_instance_of(ClaimsApi::LocalBGS).to receive(:insert_intent_to_file).and_return( stub_response ) diff --git a/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb b/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb index 556720a4ade..7158c3a3e56 100644 --- a/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/rswag_power_of_attorney_spec.rb @@ -5,12 +5,12 @@ require 'rails_helper' require_relative '../../../rails_helper' require_relative '../../../support/swagger_shared_components/v2' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' # doc generation for V2 ITFs temporarily disabled by API-13879 describe 'PowerOfAttorney', openapi_spec: Rswag::TextHelpers.new.claims_api_docs do - let(:local_bgs) { ClaimsApi::MiscellaneousBGSOperations } + let(:local_bgs) { ClaimsApi::LocalBGS } path '/veterans/{veteranId}/power-of-attorney' do get 'Find current Power of Attorney for a Veteran.' do diff --git a/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb b/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb index 45f2037cbef..9ff0dd6924e 100644 --- a/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb +++ b/modules/claims_api/spec/sidekiq/ews_vbms_sidekiq_spec.rb @@ -11,6 +11,7 @@ context 'when upload is successful' do it 'updates the Evidence Waiver Submission record' do + allow_any_instance_of(ClaimsApi::LocalBGS).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:upload!).and_return( { status: ClaimsApi::EvidenceWaiverSubmission::UPLOADED diff --git a/modules/veteran/app/models/veteran/user.rb b/modules/veteran/app/models/veteran/user.rb index cc73d80c075..06906300a0e 100644 --- a/modules/veteran/app/models/veteran/user.rb +++ b/modules/veteran/app/models/veteran/user.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs' # Veteran model module Veteran @@ -54,7 +54,7 @@ def bgs_service def local_bgs_service external_key = "#{@user.first_name} #{@user.last_name}" - @local_bgs_service ||= ClaimsApi::MiscellaneousBGSOperations.new( + @local_bgs_service ||= ClaimsApi::LocalBGS.new( external_uid: @user.mpi_icn, external_key: external_key.presence || @user.mpi_icn ) diff --git a/modules/veteran/spec/models/veteran/user_spec.rb b/modules/veteran/spec/models/veteran/user_spec.rb index 59f866b0cd2..e973af313e6 100644 --- a/modules/veteran/spec/models/veteran/user_spec.rb +++ b/modules/veteran/spec/models/veteran/user_spec.rb @@ -6,7 +6,7 @@ context 'initialization' do let(:user) { FactoryBot.create(:user, :loa3) } - let(:ows) { ClaimsApi::MiscellaneousBGSOperations } + let(:ows) { ClaimsApi::LocalBGS } it 'initializes from a user' do VCR.use_cassette('bgs/claimant_web_service/find_poa_by_participant_id') do From a06aa2ab0531de2c5e1d9e04bddb9810bd657f1f Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Tue, 23 Apr 2024 17:36:06 -0400 Subject: [PATCH 08/19] extract bgs request envelope generation --- .../app/clients/claims_api/bgs_client.rb | 3 +- .../bgs_client/service_action/definition.rb | 2 + .../bgs_client/service_action/request.rb | 64 +++++++--------- .../service_action/request/envelope.rb | 75 +++++++++++++++++++ 4 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 modules/claims_api/app/clients/claims_api/bgs_client/service_action/request/envelope.rb diff --git a/modules/claims_api/app/clients/claims_api/bgs_client.rb b/modules/claims_api/app/clients/claims_api/bgs_client.rb index 2c2682c80fd..515ed6fb8a2 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client.rb @@ -4,7 +4,8 @@ module ClaimsApi module BGSClient class << self def perform_request(definition:, body:, external_id: ServiceAction::ExternalId::DEFAULT) - ServiceAction.const_get(:Request) + ServiceAction + .const_get(:Request) .new(definition:, external_id:) .perform(body) end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb index bd8980fe020..7376cb20d43 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb @@ -3,6 +3,8 @@ module ClaimsApi module BGSClient module ServiceAction + # TODO: consider service and action definitions separately? E.g. to power + # healthcheck in a non-duplicative fashion? Definition = Data.define( :service_path, diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb index e4a702fe8d4..d2a5ad7506f 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb @@ -13,6 +13,8 @@ module ServiceAction private_constant :Request class Request + attr_reader :external_id + def initialize(definition:, external_id:) @definition = definition @external_id = external_id @@ -75,10 +77,15 @@ def perform(body) # rubocop:disable Metrics/MethodLength def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength namespaces = - @definition.service_namespaces.map do |aliaz, path| - uri = URI(namespace) - uri.path = path - %(xmlns:#{aliaz}="#{uri}") + {}.tap do |value| + namespace = URI(namespace) + value['tns'] = namespace + + @definition.service_namespaces.to_h.each do |aliaz, path| + uri = namespace.clone + uri.path = path + value[aliaz] = uri + end end client_ip = @@ -95,38 +102,23 @@ def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength .ip_address end - <<~EOXML - - - - - - #{Settings.bgs.client_username} - - - #{client_ip} - #{Settings.bgs.client_station_id} - #{Settings.bgs.application} - #{@external_id.external_uid} - #{@external_id.external_key} - - - - - #{body} - - - EOXML + headers = + Envelope::Headers.new( + ip: client_ip, + username: Settings.bgs.client_username, + station_id: Settings.bgs.client_station_id, + application_name: Settings.bgs.application, + external_id: + ) + + action = @definition.action_name + + Envelope.generate( + namespaces:, + headers:, + action:, + body: + ) end def get_fault(body) diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request/envelope.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request/envelope.rb new file mode 100644 index 00000000000..08bf04177b1 --- /dev/null +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request/envelope.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module ClaimsApi + module BGSClient + module ServiceAction + class Request + module Envelope + Headers = + Data.define( + :ip, + :username, + :station_id, + :application_name, + :external_id + ) + + # rubocop:disable Style/FormatStringToken + TEMPLATE = <<~EOXML + + + + + + %{username} + + + %{ip} + %{station_id} + %{application_name} + %{external_uid} + %{external_key} + + + + + %{body} + + + EOXML + # rubocop:enable Style/FormatStringToken + + class << self + def generate(namespaces:, headers:, action:, body:) + namespaces = + namespaces.map do |aliaz, uri| + %(xmlns:#{aliaz}="#{uri}") + end + + headers = headers.to_h + external_id = headers.delete(:external_id).to_h + + format( + TEMPLATE, + namespaces: namespaces.join("\n"), + **headers, + **external_id, + action:, + body: + ) + end + end + end + end + end + end +end From c98a11e4eb8c5cd34060d2499524019a50c425e4 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Tue, 23 Apr 2024 21:36:32 -0400 Subject: [PATCH 09/19] add bgs service definitions --- .../app/clients/claims_api/bgs_client.rb | 4 +- .../bgs_client/service_action/definition.rb | 41 +++++++++++-------- .../bgs_client/service_action/request.rb | 16 ++++---- .../claims_api/lib/bgs_service/local_bgs.rb | 30 ++++++++++++-- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/modules/claims_api/app/clients/claims_api/bgs_client.rb b/modules/claims_api/app/clients/claims_api/bgs_client.rb index 515ed6fb8a2..0682cf9f7f9 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client.rb @@ -10,9 +10,9 @@ def perform_request(definition:, body:, external_id: ServiceAction::ExternalId:: .perform(body) end - def healthcheck(service_path) + def healthcheck(definition) connection = build_connection - response = fetch_wsdl(connection, service_path) + response = fetch_wsdl(connection, definition.path) response.status end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb index 7376cb20d43..092af328a7e 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb @@ -3,26 +3,31 @@ module ClaimsApi module BGSClient module ServiceAction - # TODO: consider service and action definitions separately? E.g. to power - # healthcheck in a non-duplicative fashion? - Definition = - Data.define( - :service_path, - :service_namespaces, - :action_name - ) + class Definition < Data.define(:service, :action) + Service = Data.define(:path, :namespaces) + Action = Data.define(:name) - module Definition::ManageRepresentativeService - service_definition = { - service_path: 'VDC/ManageRepresentativeService', - service_namespaces: { 'data' => '/data' } - } + class ManageRepresentativeService < Service + include Singleton - ReadPoaRequest = - Definition.new( - action_name: 'readPOARequest', - **service_definition - ) + def initialize + super( + path: 'VDC/ManageRepresentativeService', + namespaces: { 'data' => '/data' } + ) + end + + class ReadPoaRequest < Definition + include Singleton + + def initialize + super( + service: ManageRepresentativeService.instance, + action: Action.new(name: 'readPOARequest'), + ) + end + end + end end end end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb index d2a5ad7506f..a5851b086f0 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb @@ -27,7 +27,7 @@ def perform(body) # rubocop:disable Metrics/MethodLength BGSClient.send( :fetch_wsdl, connection, - @definition.service_path + @definition.service.path ).body end @@ -40,8 +40,8 @@ def perform(body) # rubocop:disable Metrics/MethodLength response = log_duration('connection_post') do - connection.post(@definition.service_path) do |req| - req.headers['Soapaction'] = %("#{@definition.action_name}") + connection.post(@definition.service.path) do |req| + req.headers['Soapaction'] = %("#{@definition.action.name}") req.body = request_body end end @@ -62,7 +62,7 @@ def perform(body) # rubocop:disable Metrics/MethodLength value: fault ) else - key = "#{@definition.action_name}Response" + key = "#{@definition.action.name}Response" value = action_body[key].to_h Result.new( @@ -81,7 +81,7 @@ def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength namespace = URI(namespace) value['tns'] = namespace - @definition.service_namespaces.to_h.each do |aliaz, path| + @definition.service.namespaces.to_h.each do |aliaz, path| uri = namespace.clone uri.path = path value[aliaz] = uri @@ -111,7 +111,7 @@ def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength external_id: ) - action = @definition.action_name + action = @definition.action.name Envelope.generate( namespaces:, @@ -149,8 +149,8 @@ def log_duration(event_name) event = { # event should be first key in log, duration last event: event_name, - endpoint: @definition.service_path, - action: @definition.action_name, + endpoint: @definition.service.path, + action: @definition.action.name, duration: } diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index ede89387e4d..bd732d906e3 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -36,18 +36,40 @@ def initialize( end def healthcheck(endpoint) - BGSClient.healthcheck(endpoint) + service = + BGSClient::ServiceAction::Definition::Service.new( + path: endpoint, + # Namespaces are only actually relevant when performing a service + # action request. We could easily slightly tweak the API to reflect + # that, but it's probably not horrible leaving it slightly funky here + # for vague aesthetic reasons. + namespaces: nil + ) + + BGSClient.healthcheck( + service + ) end def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists endpoint:, action:, body:, key: nil, namespaces: {}, transform_response: true ) + service = + BGSClient::ServiceAction::Definition::Service.new( + path: endpoint, + namespaces: + ) + + action = + BGSClient::ServiceAction::Definition::Action.new( + name: action + ) + definition = BGSClient::ServiceAction::Definition.new( - service_path: endpoint, - service_namespaces: namespaces, - action_name: action + service:, + action: ) request = From dfe362f8cb8b5233ba5925ec541791c2d27defc4 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Wed, 24 Apr 2024 03:20:19 -0400 Subject: [PATCH 10/19] Document BGSClient public interface --- .../app/clients/claims_api/bgs_client.rb | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/modules/claims_api/app/clients/claims_api/bgs_client.rb b/modules/claims_api/app/clients/claims_api/bgs_client.rb index 0682cf9f7f9..89c0ff13dfd 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client.rb @@ -3,13 +3,71 @@ module ClaimsApi module BGSClient class << self - def perform_request(definition:, body:, external_id: ServiceAction::ExternalId::DEFAULT) + ## + # Invokes the given BGS SOAP service action with the given payload and + # returns a result containing a success payload or a fault. + # + # @example Perform a request to BGS at: + # /VDC/ManageRepresentativeService(readPOARequest) + # + # body = <<~EOXML + # + # New + # + # + # 012 + # + # EOXML + # + # definition = + # BGSClient::ServiceAction::Definition:: + # ManageRepresentativeService:: + # ReadPoaRequest.instance + # + # BGSClient.perform_request( + # definition:, + # body: + # ) + # + # @param definition [BGSClient::ServiceAction::Definition] a value object that + # identifies a particular BGS SOAP service action by way of: + # `{.service.path, .service.namespaces, .action.name}` + # + # @param body [String, #to_xml, #to_s] the action payload + # + # @param external_id [BGSClient::ServiceAction::ExternalId] a value object that + # arbitrarily self-identifies ourselves to BGS as its caller by: + # `{.external_uid, .external_key}` + # + # @return [BGSClient::ServiceAction::Request::Result] + # the response payload of a successful request, or the fault object of a + # failed request + def perform_request( + definition:, body:, + external_id: ServiceAction::ExternalId::DEFAULT + ) ServiceAction .const_get(:Request) .new(definition:, external_id:) .perform(body) end + ## + # Reveals the momemtary health of a BGS service by attempting to request + # its WSDL and returning the HTTP status code of the response. + # + # @example + # definition = + # BGSClient::ServiceAction::Definition:: + # ManageRepresentativeService.instance + # + # BGSClient.healthcheck(definition) + # + # @param definition [ServiceAction::Definition::Service] a value object + # that identifies a particular BGS SOAP service by way of: + # `{.path, .namespaces}` + # + # @return [Integer] HTTP status code def healthcheck(definition) connection = build_connection response = fetch_wsdl(connection, definition.path) From 860ea996450e6b76880d778f38ddf759b4775bb9 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Wed, 24 Apr 2024 03:25:16 -0400 Subject: [PATCH 11/19] preserve conn config variation of LocalBGS in BGSClient --- .../app/clients/claims_api/bgs_client.rb | 8 +------- .../bgs_client/service_action/request.rb | 17 +++++++++++++++-- .../spec/support/bgs_client_helpers.rb | 3 +-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/modules/claims_api/app/clients/claims_api/bgs_client.rb b/modules/claims_api/app/clients/claims_api/bgs_client.rb index 89c0ff13dfd..6e9f9319f88 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client.rb @@ -107,13 +107,7 @@ def build_connection Faraday.new(Settings.bgs.url) do |conn| conn.ssl.verify_mode = ssl_verify_mode - conn.options.timeout = Settings.bgs.timeout || 120 - conn.use :breakers - - conn.headers.merge!( - 'Content-Type' => 'text/xml;charset=UTF-8', - 'Host' => "#{Settings.bgs.env}.vba.va.gov" - ) + yield(conn) if block_given? end end end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb index a5851b086f0..bcba9f6bc23 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb @@ -41,8 +41,12 @@ def perform(body) # rubocop:disable Metrics/MethodLength response = log_duration('connection_post') do connection.post(@definition.service.path) do |req| - req.headers['Soapaction'] = %("#{@definition.action.name}") req.body = request_body + req.headers.merge!( + 'Soapaction' => %("#{@definition.action.name}"), + 'Content-Type' => 'text/xml;charset=UTF-8', + 'Host' => "#{Settings.bgs.env}.vba.va.gov" + ) end end rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e @@ -137,7 +141,16 @@ def get_fault(body) end def connection - @connection ||= BGSClient.send(:build_connection) + @connection ||= + BGSClient.send(:build_connection) do |conn| + # Should all of this connection configuration really not be + # involved in the BGS service healthcheck performed by + # `BGSClient.healthcheck`? Under the hood, that just fetches WSDL + # which we also do here but with "smarter" connection config. + conn.options.timeout = Settings.bgs.timeout || 120 + conn.adapter Faraday.default_adapter + conn.use :breakers + end end def log_duration(event_name) diff --git a/modules/claims_api/spec/support/bgs_client_helpers.rb b/modules/claims_api/spec/support/bgs_client_helpers.rb index 4d6394c50fd..de8b5147708 100644 --- a/modules/claims_api/spec/support/bgs_client_helpers.rb +++ b/modules/claims_api/spec/support/bgs_client_helpers.rb @@ -19,9 +19,8 @@ module BGSClientHelpers end VCR_OPTIONS = { - # Consider matching on `:headers` too? match_requests_on: [ - :method, :uri, + :method, :uri, :headers, body_as_xml_matcher.freeze ].freeze }.freeze From 6d743d572733e2d6378d9a8b05e871a619da0c4d Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Wed, 24 Apr 2024 12:36:21 -0400 Subject: [PATCH 12/19] revert to simpler BGSClient::ServiceAction::Definition --- .../app/clients/claims_api/bgs_client.rb | 28 ++++++------- .../bgs_client/service_action/definition.rb | 36 +++++++---------- .../bgs_client/service_action/request.rb | 16 ++++---- .../claims_api/lib/bgs_service/local_bgs.rb | 39 ++++++++----------- 4 files changed, 55 insertions(+), 64 deletions(-) diff --git a/modules/claims_api/app/clients/claims_api/bgs_client.rb b/modules/claims_api/app/clients/claims_api/bgs_client.rb index 6e9f9319f88..fd7ab3b57cd 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client.rb @@ -22,21 +22,21 @@ class << self # definition = # BGSClient::ServiceAction::Definition:: # ManageRepresentativeService:: - # ReadPoaRequest.instance + # ReadPoaRequest # # BGSClient.perform_request( # definition:, # body: # ) # - # @param definition [BGSClient::ServiceAction::Definition] a value object that - # identifies a particular BGS SOAP service action by way of: - # `{.service.path, .service.namespaces, .action.name}` + # @param definition [BGSClient::ServiceAction::Definition] a value object + # that identifies a particular BGS SOAP service action by way of: + # `{.service_path, .service_namespaces, .action_name}` # # @param body [String, #to_xml, #to_s] the action payload # - # @param external_id [BGSClient::ServiceAction::ExternalId] a value object that - # arbitrarily self-identifies ourselves to BGS as its caller by: + # @param external_id [BGSClient::ServiceAction::ExternalId] a value object + # that arbitrarily self-identifies ourselves to BGS as its caller by: # `{.external_uid, .external_key}` # # @return [BGSClient::ServiceAction::Request::Result] @@ -59,18 +59,20 @@ def perform_request( # @example # definition = # BGSClient::ServiceAction::Definition:: - # ManageRepresentativeService.instance + # ManageRepresentativeService:: + # ReadPoaRequest # # BGSClient.healthcheck(definition) # - # @param definition [ServiceAction::Definition::Service] a value object - # that identifies a particular BGS SOAP service by way of: - # `{.path, .namespaces}` + # @param definition [BGSClient::ServiceAction::Definition] a value object + # that identifies a particular BGS SOAP service action by way of: + # `{.service_path, .service_namespaces, .action_name}` # # @return [Integer] HTTP status code def healthcheck(definition) connection = build_connection - response = fetch_wsdl(connection, definition.path) + path = definition.service_path + response = fetch_wsdl(connection, path) response.status end @@ -91,8 +93,8 @@ def breakers_service private - def fetch_wsdl(connection, service_path) - connection.get(service_path) do |req| + def fetch_wsdl(connection, path) + connection.get(path) do |req| req.params['WSDL'] = nil end end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb index 092af328a7e..4bbfa03cf35 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/definition.rb @@ -3,30 +3,24 @@ module ClaimsApi module BGSClient module ServiceAction - class Definition < Data.define(:service, :action) - Service = Data.define(:path, :namespaces) - Action = Data.define(:name) + class Definition < + Data.define( + :service_path, + :service_namespaces, + :action_name + ) - class ManageRepresentativeService < Service - include Singleton + module ManageRepresentativeService + service = { + service_path: 'VDC/ManageRepresentativeService', + service_namespaces: { 'data' => '/data' } + } - def initialize - super( - path: 'VDC/ManageRepresentativeService', - namespaces: { 'data' => '/data' } + ReadPoaRequest = + Definition.new( + action_name: 'readPOARequest', + **service ) - end - - class ReadPoaRequest < Definition - include Singleton - - def initialize - super( - service: ManageRepresentativeService.instance, - action: Action.new(name: 'readPOARequest'), - ) - end - end end end end diff --git a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb index bcba9f6bc23..33777195ed6 100644 --- a/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb +++ b/modules/claims_api/app/clients/claims_api/bgs_client/service_action/request.rb @@ -27,7 +27,7 @@ def perform(body) # rubocop:disable Metrics/MethodLength BGSClient.send( :fetch_wsdl, connection, - @definition.service.path + @definition.service_path ).body end @@ -40,10 +40,10 @@ def perform(body) # rubocop:disable Metrics/MethodLength response = log_duration('connection_post') do - connection.post(@definition.service.path) do |req| + connection.post(@definition.service_path) do |req| req.body = request_body req.headers.merge!( - 'Soapaction' => %("#{@definition.action.name}"), + 'Soapaction' => %("#{@definition.action_name}"), 'Content-Type' => 'text/xml;charset=UTF-8', 'Host' => "#{Settings.bgs.env}.vba.va.gov" ) @@ -66,7 +66,7 @@ def perform(body) # rubocop:disable Metrics/MethodLength value: fault ) else - key = "#{@definition.action.name}Response" + key = "#{@definition.action_name}Response" value = action_body[key].to_h Result.new( @@ -85,7 +85,7 @@ def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength namespace = URI(namespace) value['tns'] = namespace - @definition.service.namespaces.to_h.each do |aliaz, path| + @definition.service_namespaces.to_h.each do |aliaz, path| uri = namespace.clone uri.path = path value[aliaz] = uri @@ -115,7 +115,7 @@ def build_request_body(body, namespace:) # rubocop:disable Metrics/MethodLength external_id: ) - action = @definition.action.name + action = @definition.action_name Envelope.generate( namespaces:, @@ -162,8 +162,8 @@ def log_duration(event_name) event = { # event should be first key in log, duration last event: event_name, - endpoint: @definition.service.path, - action: @definition.action.name, + endpoint: @definition.service_path, + action: @definition.action_name, duration: } diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index bd732d906e3..9715f3fddf6 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -36,18 +36,23 @@ def initialize( end def healthcheck(endpoint) - service = - BGSClient::ServiceAction::Definition::Service.new( - path: endpoint, - # Namespaces are only actually relevant when performing a service - # action request. We could easily slightly tweak the API to reflect - # that, but it's probably not horrible leaving it slightly funky here - # for vague aesthetic reasons. - namespaces: nil + definition = + BGSClient::ServiceAction::Definition.new( + service_path: endpoint, + # We could introduce an idea of just the service definition, but for + # now the idea is to be able to pass a predefined value if we already + # have it, e.g.: + # ``` + # BGSClient::ServiceAction::Definition:: + # ManageRepresentativeService:: + # ReadPoaRequest + # ``` + service_namespaces: nil, + action_name: nil ) BGSClient.healthcheck( - service + definition ) end @@ -55,21 +60,11 @@ def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists endpoint:, action:, body:, key: nil, namespaces: {}, transform_response: true ) - service = - BGSClient::ServiceAction::Definition::Service.new( - path: endpoint, - namespaces: - ) - - action = - BGSClient::ServiceAction::Definition::Action.new( - name: action - ) - definition = BGSClient::ServiceAction::Definition.new( - service:, - action: + service_path: endpoint, + service_namespaces: namespaces, + action_name: action ) request = From 49fd8ece571193bd1ca7a4b05b19b2e6b586d4ea Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Wed, 24 Apr 2024 13:05:29 -0400 Subject: [PATCH 13/19] add deprecation comment to LocalBGS --- modules/claims_api/lib/bgs_service/local_bgs.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index 9715f3fddf6..ab1c0ecc1b5 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -12,6 +12,15 @@ require 'bgs_service/miscellaneous_bgs_operations' module ClaimsApi + # @deprecated Use {BGSClient.perform_request} instead. There ought to be a + # clear separation between the single method that performs the transport to + # BGS and any business logic that invokes said transport. By housing that + # single method as an instance method of this class, we encouraged + # business logic modules to inherit this class and then inevitably start to + # conflate business logic back into the transport layer here. There was a + # particularly easy temptation to put business object state validation as + # well as the dumping and loading of business object state into this layer, + # but that should live in the business logic layer and not here. class LocalBGS include ClaimsApi::MiscellaneousBGSOperations include ClaimsApi::BGSHelpers From 80471dfa2276e95f889e5cb557bfb54f7ae0f891 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Thu, 25 Apr 2024 18:56:48 -0400 Subject: [PATCH 14/19] restore original `LocalBGS` w/ feature toggle to delegate to `LocalBGSRefactored` --- .../lib/bgs_service/bgs_error_handler.rb | 73 +++ .../claims_api/lib/bgs_service/local_bgs.rb | 474 +++++++++++++++--- .../lib/bgs_service/local_bgs_refactored.rb | 109 ++++ .../claims_api/error/soap_error_handler.rb | 49 +- .../claims_api/local_bgs_refactored_spec.rb | 118 +++++ .../spec/lib/claims_api/local_bgs_spec.rb | 19 +- 6 files changed, 738 insertions(+), 104 deletions(-) create mode 100644 modules/claims_api/lib/bgs_service/bgs_error_handler.rb create mode 100644 modules/claims_api/lib/bgs_service/local_bgs_refactored.rb create mode 100644 modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb diff --git a/modules/claims_api/lib/bgs_service/bgs_error_handler.rb b/modules/claims_api/lib/bgs_service/bgs_error_handler.rb new file mode 100644 index 00000000000..bc953f372ea --- /dev/null +++ b/modules/claims_api/lib/bgs_service/bgs_error_handler.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module ClaimsApi + # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm + # + # TODO: Some (or all) of these cases should be handled in consumers and not in + # a central location. + class BGSErrorHandler + class << self + def handle_errors!(fault) + new(fault).handle_errors! + end + end + + def initialize(fault) + @fault = fault + end + + def handle_errors! + return if not_error? + + if not_found? + raise ::Common::Exceptions::ResourceNotFound.new(detail: 'Resource not found.') + elsif bnft_claim_not_found? + {} + elsif unprocessable? + raise ::Common::Exceptions::UnprocessableEntity.new( + detail: 'Please try again after checking your input values.' + ) + else + soap_logging('500') + raise ::Common::Exceptions::ServiceError.new(detail: 'An external server is experiencing difficulty.') + end + end + + private + + def not_error? + @fault.string.include?('IntentToFileWebService') && + @fault.string.include?('not found') + end + + def not_found? + errors = ['bnftClaimId-->bnftClaimId/text()', 'not found', 'No Person found'] + has_errors = errors.any? { |error| @fault.string.include? error } + soap_logging('404') if has_errors + has_errors + end + + def bnft_claim_not_found? + errors = ['No BnftClaim found'] + has_errors = errors.any? { |error| @fault.string.include? error } + soap_logging('404') if has_errors + has_errors + end + + def unprocessable? + errors = ['java.sql', 'MessageException', 'Validation errors', 'Exception Description', + 'does not have necessary info', 'Error committing transaction', 'Transaction Rolledback', + 'Unexpected error', 'XML reader error', 'could not be converted'] + has_errors = errors.any? { |error| @fault.string.include? error } + soap_logging('422') if has_errors + has_errors + end + + def soap_logging(status_code) + ClaimsApi::Logger.log('soap_error_handler', + detail: "Returning #{status_code} via local_bgs & soap_error_handler, " \ + "fault_string: #{@fault.string}, with message: #{@fault.message}, " \ + "and fault_code: #{@fault.code}.") + end + end +end diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index ab1c0ecc1b5..af4480bdf3c 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -8,102 +8,432 @@ require 'claims_api/claim_logger' require 'claims_api/error/soap_error_handler' -require 'bgs_service/bgs_helpers' -require 'bgs_service/miscellaneous_bgs_operations' +require 'claims_api/evss_bgs_mapper' +require 'bgs_service/local_bgs_refactored' module ClaimsApi - # @deprecated Use {BGSClient.perform_request} instead. There ought to be a - # clear separation between the single method that performs the transport to - # BGS and any business logic that invokes said transport. By housing that - # single method as an instance method of this class, we encouraged - # business logic modules to inherit this class and then inevitably start to - # conflate business logic back into the transport layer here. There was a - # particularly easy temptation to put business object state validation as - # well as the dumping and loading of business object state into this layer, - # but that should live in the business logic layer and not here. class LocalBGS - include ClaimsApi::MiscellaneousBGSOperations - include ClaimsApi::BGSHelpers + attr_accessor :external_uid, :external_key - class << self - def breakers_service - BGSClient.breakers_service - end + # rubocop:disable Metrics/MethodLength + def initialize(external_uid:, external_key:) + @client_ip = + if Rails.env.test? + # For all intents and purposes, BGS behaves identically no matter what + # IP we provide it. So in a test environment, let's just give it a + # fake so that cassette matching isn't defeated on CI and everyone's + # computer. + '127.0.0.1' + else + Socket + .ip_address_list + .detect(&:ipv4_private?) + .ip_address + end + + @ssl_verify_mode = + if Settings.bgs.ssl_verify_mode == 'none' + OpenSSL::SSL::VERIFY_NONE + else + OpenSSL::SSL::VERIFY_PEER + end + + @application = Settings.bgs.application + @client_station_id = Settings.bgs.client_station_id + @client_username = Settings.bgs.client_username + @env = Settings.bgs.env + @mock_response_location = Settings.bgs.mock_response_location + @mock_responses = Settings.bgs.mock_responses + @external_uid = external_uid || Settings.bgs.external_uid + @external_key = external_key || Settings.bgs.external_key + @forward_proxy_url = Settings.bgs.url + @timeout = Settings.bgs.timeout || 120 end + # rubocop:enable Metrics/MethodLength - attr_reader :external_id + def self.breakers_service + if Flipper.enabled?(:claims_api_local_bgs_refactor) # rubocop:disable Style/IfUnlessModifier + return LocalBGSRefactored.breakers_service + end - def initialize( - external_uid: Settings.bgs.external_uid, - external_key: Settings.bgs.external_key - ) - @external_id = - BGSClient::ServiceAction::ExternalId.new( - external_uid:, - external_key: - ) + url = Settings.bgs.url + path = URI.parse(url).path + host = URI.parse(url).host + port = URI.parse(url).port + matcher = proc do |request_env| + request_env.url.host == host && + request_env.url.port == port && + request_env.url.path =~ /^#{path}/ + end + + Breakers::Service.new( + name: 'BGS/Claims', + request_matcher: matcher + ) + end + + def bean_name + raise 'Not Implemented' end def healthcheck(endpoint) - definition = - BGSClient::ServiceAction::Definition.new( - service_path: endpoint, - # We could introduce an idea of just the service definition, but for - # now the idea is to be able to pass a predefined value if we already - # have it, e.g.: - # ``` - # BGSClient::ServiceAction::Definition:: - # ManageRepresentativeService:: - # ReadPoaRequest - # ``` - service_namespaces: nil, - action_name: nil - ) + connection = Faraday::Connection.new(ssl: { verify_mode: @ssl_verify_mode }) + wsdl = connection.get("#{Settings.bgs.url}/#{endpoint}?WSDL") + wsdl.status + end - BGSClient.healthcheck( - definition - ) + def find_poa_by_participant_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'ClaimantServiceBean/ClaimantWebService', action: 'findPOAByPtcpntId', body:, + key: 'return') end - def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists - endpoint:, action:, body:, key: nil, - namespaces: {}, transform_response: true - ) - definition = - BGSClient::ServiceAction::Definition.new( - service_path: endpoint, - service_namespaces: namespaces, - action_name: action - ) + def find_by_ssn(ssn) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ssn: }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'PersonWebServiceBean/PersonWebService', action: 'findPersonBySSN', body:, + key: 'PersonDTO') + end + + def find_poa_history_by_ptcpnt_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end - request = - BGSClient::ServiceAction.const_get(:Request).new( - definition:, - external_id: + make_request(endpoint: 'OrgWebServiceBean/OrgWebService', action: 'findPoaHistoryByPtcpntId', body:, + key: 'PoaHistory') + end + + def find_benefit_claims_status_by_ptcpnt_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', + action: 'findBenefitClaimsStatusByPtcpntId', body:) + end + + def claims_count(id) + find_benefit_claims_status_by_ptcpnt_id(id).count + rescue ::Common::Exceptions::ResourceNotFound + 0 + end + + def find_benefit_claim_details_by_benefit_claim_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { bnftClaimId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', + action: 'findBenefitClaimDetailsByBnftClaimId', body:) + end + + def insert_intent_to_file(options) + request_body = construct_itf_body(options) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + + EOXML + + request_body.each do |k, z| + node = Nokogiri::XML::Node.new k.to_s, body + node.content = z.to_s + opt = body.at('intentToFileDTO') + node.parent = opt + end + make_request(endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', action: 'insertIntentToFile', + body:, key: 'IntentToFileDTO') + end + + def find_tracked_items(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { claimId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'TrackedItemService/TrackedItemService', action: 'findTrackedItems', body:, + key: 'BenefitClaim') + end + + def find_intent_to_file_by_ptcpnt_id_itf_type_cd(id, type) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + ptcpnt_id = body.at 'ptcpntId' + ptcpnt_id.content = id.to_s + itf_type_cd = body.at 'itfTypeCd' + itf_type_cd.content = type.to_s + + response = + make_request( + endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', + action: 'findIntentToFileByPtcpntIdItfTypeCd', + body: ) - result = request.perform(body) + Array.wrap(response[:intent_to_file_dto]) + end - if result.success? - value = result.value.to_h - value = value[key].to_h if key.present? + # BEGIN: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. + def update_from_remote(id) + bgs_claim = find_benefit_claim_details_by_benefit_claim_id(id) + transform_bgs_claim_to_evss(bgs_claim) + end - if transform_response - request.send(:log_duration, 'transformed_response') do - value.deep_transform_keys! do |key| - key.underscore.to_sym - end + def all(id) + claims = find_benefit_claims_status_by_ptcpnt_id(id) + return [] if claims.count < 1 || claims[:benefit_claims_dto].blank? + + transform_bgs_claims_to_evss(claims) + end + # END: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. + + private + + def header # rubocop:disable Metrics/MethodLength + # Stock XML structure {{{ + header = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + + + + + + + + + + + + + + EOXML + + { Username: @client_username, CLIENT_MACHINE: @client_ip, + STN_ID: @client_station_id, applicationName: @application, + ExternalUid: @external_uid, ExternalKey: @external_key }.each do |k, v| + header.xpath(".//*[local-name()='#{k}']")[0].content = v + end + header.to_s + end + + def full_body(action:, body:, namespace:, namespaces:) + namespaces = + namespaces.map do |aliaz, path| + uri = URI(namespace) + uri.path = path + %(xmlns:#{aliaz}="#{uri}") + end + + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + + #{header} + + #{body} + + + EOXML + body.to_s + end + + def parsed_response(response, action:, key:, transform:) + body = Hash.from_xml(response.body) + keys = ['Envelope', 'Body', "#{action}Response"] + keys << key if key.present? + + body.dig(*keys).to_h.tap do |value| + if transform + value.deep_transform_keys! do |key| + key.underscore.to_sym end end + end + end - return value + def make_request(endpoint:, action:, body:, key: nil, namespaces: {}, transform_response: true) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists + connection = log_duration event: 'establish_ssl_connection' do + Faraday::Connection.new(ssl: { verify_mode: @ssl_verify_mode }) do |f| + f.use :breakers + f.adapter Faraday.default_adapter + end end + connection.options.timeout = @timeout - SoapErrorHandler.handle_errors!( - result.value - ) + begin + wsdl = log_duration(event: 'connection_wsdl_get', endpoint:) do + connection.get("#{Settings.bgs.url}/#{endpoint}?WSDL") + end + + url = "#{Settings.bgs.url}/#{endpoint}" + namespace = Hash.from_xml(wsdl.body).dig('definitions', 'targetNamespace').to_s + body = full_body(action:, body:, namespace:, namespaces:) + headers = { + 'Content-Type' => 'text/xml;charset=UTF-8', + 'Host' => "#{@env}.vba.va.gov", + 'Soapaction' => %("#{action}") + } + + response = log_duration(event: 'connection_post', endpoint:, action:) do + connection.post(url, body, headers) + end + rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e + ClaimsApi::Logger.log('local_bgs', + retry: true, + detail: "local BGS Faraday Timeout: #{e.message}") + raise ::Common::Exceptions::BadGateway + end + soap_error_handler.handle_errors(response) if response + + log_duration(event: 'parsed_response', key:) do + parsed_response(response, action:, key:, transform: transform_response) + end + end + + def construct_itf_body(options) + request_body = { + itfTypeCd: options[:intent_to_file_type_code], + ptcpntVetId: options[:participant_vet_id], + rcvdDt: options[:received_date], + signtrInd: options[:signature_indicated], + submtrApplcnTypeCd: options[:submitter_application_icn_type_code] + } + request_body[:ptcpntClmantId] = options[:participant_claimant_id] if options.key?(:participant_claimant_id) + request_body[:clmantSsn] = options[:claimant_ssn] if options.key?(:claimant_ssn) + request_body + end + + def log_duration(event: 'default', **extra_params) + # Who are we to question sidekiq's use of CLOCK_MONOTONIC to avoid negative durations? + # https://github.com/sidekiq/sidekiq/issues/3999 + start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + result = yield + duration = (::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start_time).round(4) + + # event should be first key in log, duration last + event_for_log = { event: }.merge(extra_params).merge({ duration: }) + ClaimsApi::Logger.log 'local_bgs', **event_for_log + StatsD.measure("api.claims_api.local_bgs.#{event}.duration", duration, tags: {}) + result + end + + def soap_error_handler + ClaimsApi::SoapErrorHandler.new + end + + def transform_bgs_claim_to_evss(claim) + bgs_claim = ClaimsApi::EvssBgsMapper.new(claim[:benefit_claim_details_dto]) + return if bgs_claim.nil? - {} + bgs_claim.map_and_build_object end + + def transform_bgs_claims_to_evss(claims) + claims_array = [claims[:benefit_claims_dto][:benefit_claim]].flatten + claims_array&.map do |claim| + bgs_claim = ClaimsApi::EvssBgsMapper.new(claim) + bgs_claim.map_and_build_object + end + end + + def to_camelcase(claim:) + claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } + end + + def convert_nil_values(options) + arg_strg = '' + options.each do |option| + arg = option[0].to_s.camelize(:lower) + arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") + end + arg_strg + end + + def validate_opts!(opts, required_keys) + keys = opts.keys.map(&:to_s) + required_keys = required_keys.map(&:to_s) + missing_keys = required_keys - keys + raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? + end + + def jrn + { + jrn_dt: Time.current.iso8601, + jrn_lctn_id: Settings.bgs.client_station_id, + jrn_status_type_cd: 'U', + jrn_user_id: Settings.bgs.client_username, + jrn_obj_id: Settings.bgs.application + } + end + + # This has to come after all `LocalBGS` method definitions. + prepend( + Module.new do + meths = + LocalBGS.private_instance_methods(false) + + LocalBGS.instance_methods(false) - + [:initialize] + + meths.each do |meth| + define_method(meth) do |*args, **kwargs, &block| + if Flipper.enabled?(:claims_api_local_bgs_refactor) + @refactored ||= + LocalBGSRefactored.new( + external_uid:, + external_key: + ) + + @refactored.send( + meth, + *args, + **kwargs, + &block + ) + else + super( + *args, + **kwargs, + &block + ) + end + end + end + end + ) end end diff --git a/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb b/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb new file mode 100644 index 00000000000..9778e923ea0 --- /dev/null +++ b/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +# As a work of the United States Government, this project is in the +# public domain within the United States. +# +# Additionally, we waive copyright and related rights in the work +# worldwide through the CC0 1.0 Universal public domain dedication. + +require 'claims_api/claim_logger' +require 'bgs_service/bgs_helpers' +require 'bgs_service/bgs_error_handler' +require 'bgs_service/miscellaneous_bgs_operations' + +module ClaimsApi + # @deprecated Use {BGSClient.perform_request} instead. There ought to be a + # clear separation between the single method that performs the transport to + # BGS and any business logic that invokes said transport. By housing that + # single method as an instance method of this class, we encouraged + # business logic modules to inherit this class and then inevitably start to + # conflate business logic back into the transport layer here. There was a + # particularly easy temptation to put business object state validation as + # well as the dumping and loading of business object state into this layer, + # but that should live in the business logic layer and not here. + class LocalBGSRefactored + include ClaimsApi::MiscellaneousBGSOperations + include ClaimsApi::BGSHelpers + + class << self + def breakers_service + BGSClient.breakers_service + end + end + + attr_reader :external_id + + def initialize( + external_uid: Settings.bgs.external_uid, + external_key: Settings.bgs.external_key + ) + @external_id = + BGSClient::ServiceAction::ExternalId.new( + external_uid:, + external_key: + ) + end + + def healthcheck(endpoint) + definition = + BGSClient::ServiceAction::Definition.new( + service_path: endpoint, + # We could introduce an idea of just the service definition, but for + # now the idea is to be able to pass a predefined value if we already + # have it, e.g.: + # ``` + # BGSClient::ServiceAction::Definition:: + # ManageRepresentativeService:: + # ReadPoaRequest + # ``` + service_namespaces: nil, + action_name: nil + ) + + BGSClient.healthcheck( + definition + ) + end + + def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists + endpoint:, action:, body:, key: nil, + namespaces: {}, transform_response: true + ) + definition = + BGSClient::ServiceAction::Definition.new( + service_path: endpoint, + service_namespaces: namespaces, + action_name: action + ) + + request = + BGSClient::ServiceAction.const_get(:Request).new( + definition:, + external_id: + ) + + result = request.perform(body) + + if result.success? + value = result.value.to_h + value = value[key].to_h if key.present? + + if transform_response + request.send(:log_duration, 'transformed_response') do + value.deep_transform_keys! do |key| + key.underscore.to_sym + end + end + end + + return value + end + + BGSErrorHandler.handle_errors!( + result.value + ) + + {} + end + end +end diff --git a/modules/claims_api/lib/claims_api/error/soap_error_handler.rb b/modules/claims_api/lib/claims_api/error/soap_error_handler.rb index 5c4d900ee10..33fef287e84 100644 --- a/modules/claims_api/lib/claims_api/error/soap_error_handler.rb +++ b/modules/claims_api/lib/claims_api/error/soap_error_handler.rb @@ -1,24 +1,32 @@ # frozen_string_literal: true +require 'nokogiri' + module ClaimsApi - # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm - # - # TODO: Some (or all) of these cases should be handled in consumers and not in - # a central location. class SoapErrorHandler - class << self - def handle_errors!(fault) - new(fault).handle_errors! - end + # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm + + def handle_errors(response) + @hash = Hash.from_xml(response.body) + + return if @hash&.dig('Envelope', 'Body', 'Fault').blank? + + get_fault_info end - def initialize(fault) - @fault = fault + def get_fault_info + fault = @hash&.dig('Envelope', 'Body', 'Fault') + @fault_code = fault&.dig('faultcode')&.split(':')&.dig(1) + @fault_string = fault&.dig('faultstring') + @fault_message = fault&.dig('detail', 'MessageException') || fault&.dig('detail', 'MessageFaultException') + return {} if @fault_string.include?('IntentToFileWebService') && @fault_string.include?('not found') + + get_exception end - def handle_errors! - return if not_error? + private + def get_exception if not_found? raise ::Common::Exceptions::ResourceNotFound.new(detail: 'Resource not found.') elsif bnft_claim_not_found? @@ -33,23 +41,16 @@ def handle_errors! end end - private - - def not_error? - @fault.string.include?('IntentToFileWebService') && - @fault.string.include?('not found') - end - def not_found? errors = ['bnftClaimId-->bnftClaimId/text()', 'not found', 'No Person found'] - has_errors = errors.any? { |error| @fault.string.include? error } + has_errors = errors.any? { |error| @fault_string.include? error } soap_logging('404') if has_errors has_errors end def bnft_claim_not_found? errors = ['No BnftClaim found'] - has_errors = errors.any? { |error| @fault.string.include? error } + has_errors = errors.any? { |error| @fault_string.include? error } soap_logging('404') if has_errors has_errors end @@ -58,7 +59,7 @@ def unprocessable? errors = ['java.sql', 'MessageException', 'Validation errors', 'Exception Description', 'does not have necessary info', 'Error committing transaction', 'Transaction Rolledback', 'Unexpected error', 'XML reader error', 'could not be converted'] - has_errors = errors.any? { |error| @fault.string.include? error } + has_errors = errors.any? { |error| @fault_string.include? error } soap_logging('422') if has_errors has_errors end @@ -66,8 +67,8 @@ def unprocessable? def soap_logging(status_code) ClaimsApi::Logger.log('soap_error_handler', detail: "Returning #{status_code} via local_bgs & soap_error_handler, " \ - "fault_string: #{@fault.string}, with message: #{@fault.message}, " \ - "and fault_code: #{@fault.code}.") + "fault_string: #{@fault_string}, with message: #{@fault_message}, " \ + "and fault_code: #{@fault_code}.") end end end diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb new file mode 100644 index 00000000000..b8e94b87dfe --- /dev/null +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'bgs_service/local_bgs' +require 'claims_api/error/soap_error_handler' + +describe ClaimsApi::LocalBGS do + subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } + + let(:soap_error_handler) { ClaimsApi::SoapErrorHandler } + + describe '#find_poa_by_participant_id' do + it 'responds as expected, with extra ClaimsApi::Logger logging' do + VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id') do + # Events logged: + # 1: connection_wsdl_get - duration of WSDL request cycle + # 2: built_request - how long to build the request + # 3: connection_post - how long does the post itself take for the request cycle + # 4: parsed_response - how long to parse the response + # 5: transformed_response - how long to transform the response + expect(ClaimsApi::Logger).to receive(:log).exactly(5).times + result = subject.find_poa_by_participant_id('does-not-matter') + expect(result).to be_a Hash + expect(result[:end_date]).to eq '08/26/2020' + end + end + + describe 'breakers' do + it 'returns a Bad Gateway' do + stub_request(:any, "#{Settings.bgs.url}/ClaimantServiceBean/ClaimantWebService?WSDL").to_timeout + expect do + subject.find_poa_by_participant_id('also-does-not-matter') + end.to raise_error(Common::Exceptions::BadGateway) + end + + it 'hits breakers' do + ClaimsApi::LocalBGS.breakers_service.begin_forced_outage! + expect { subject.find_poa_by_participant_id('also-does-not-matter') }.to raise_error(Breakers::OutageException) + ClaimsApi::LocalBGS.breakers_service.end_forced_outage! + end + end + + it 'triggers StatsD measurements' do + VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id', + allow_playback_repeats: true) do + %w[connection_wsdl_get built_request connection_post parsed_response transformed_response].each do |event| + expect { subject.find_poa_by_participant_id('does-not-matter') } + .to trigger_statsd_measure("api.claims_api.local_bgs.#{event}.duration") + end + end + end + end + + # Testing potential ways the current check could be tricked + describe '#all' do + let(:subject_instance) { subject } + let(:id) { 12_343 } + let(:error_message) { { error: 'Did not work', code: 'XXX' } } + let(:bgs_unknown_error_message) { { error: 'Unexpected error' } } + let(:empty_array) { [] } + + context 'when an error message gets returned it still does not pass the count check' do + it 'returns an empty array' do + expect(error_message.count).to eq(2) # trick the claims count check + # error message should trigger return + allow(subject_instance).to receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(error_message) + expect(subject.all(id)).to eq([]) # verify correct return + end + end + + context 'when claims come back as a hash instead of an array' do + it 'casts the hash as an array' do + VCR.use_cassette('claims_api/bgs/claims/claims_trimmed_down') do + claims = subject_instance.find_benefit_claims_status_by_ptcpnt_id('600061742') + claims[:benefit_claims_dto][:benefit_claim] = claims[:benefit_claims_dto][:benefit_claim][0] + allow(subject_instance).to receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(claims) + + begin + ret = subject_instance.send(:transform_bgs_claims_to_evss, claims) + expect(ret.class).to_be Array + expect(ret.size).to eq 1 + rescue => e + expect(e.message).not_to include 'no implicit conversion of Array into Hash' + end + end + end + end + + # Already being checked but based on an error seen just want to lock this in to ensure nothing gets missed + context 'when an empty array gets returned it still does not pass the count check' do + it 'returns an empty array' do + # error message should trigger return + allow(subject_instance).to receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(empty_array) + expect(subject.all(id)).to eq([]) # verify correct return + end + end + + context 'when an error message gets returns unknown' do + it 'the soap error handler returns unprocessable' do + allow(subject_instance).to receive(:make_request).with(endpoint: 'PersonWebServiceBean/PersonWebService', + action: 'findPersonBySSN', + body: Nokogiri::XML::DocumentFragment.new( + Nokogiri::XML::Document.new + ), + key: 'PersonDTO').and_return(:bgs_unknown_error_message) + begin + allow(soap_error_handler).to receive(:handle_errors!) + .with(:bgs_unknown_error_message).and_raise(Common::Exceptions::UnprocessableEntity) + ret = soap_error_handler.send(:handle_errors!, :bgs_unknown_error_message) + expect(ret.class).to_be Array + expect(ret.size).to eq 1 + rescue => e + expect(e.message).to include 'Unprocessable Entity' + end + end + end + end +end diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb index b8e94b87dfe..482c897a6d8 100644 --- a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb @@ -7,18 +7,19 @@ describe ClaimsApi::LocalBGS do subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } - let(:soap_error_handler) { ClaimsApi::SoapErrorHandler } + let(:soap_error_handler) { ClaimsApi::SoapErrorHandler.new } describe '#find_poa_by_participant_id' do it 'responds as expected, with extra ClaimsApi::Logger logging' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id') do + allow_any_instance_of(BGS::OrgWebService).to receive(:find_poa_history_by_ptcpnt_id).and_return({}) + # Events logged: - # 1: connection_wsdl_get - duration of WSDL request cycle - # 2: built_request - how long to build the request + # 1: establish_ssl_connection - how long to establish the connection + # 2: connection_wsdl_get - duration of WSDL request cycle # 3: connection_post - how long does the post itself take for the request cycle # 4: parsed_response - how long to parse the response - # 5: transformed_response - how long to transform the response - expect(ClaimsApi::Logger).to receive(:log).exactly(5).times + expect(ClaimsApi::Logger).to receive(:log).exactly(4).times result = subject.find_poa_by_participant_id('does-not-matter') expect(result).to be_a Hash expect(result[:end_date]).to eq '08/26/2020' @@ -43,7 +44,9 @@ it 'triggers StatsD measurements' do VCR.use_cassette('claims_api/bgs/claimant_web_service/find_poa_by_participant_id', allow_playback_repeats: true) do - %w[connection_wsdl_get built_request connection_post parsed_response transformed_response].each do |event| + allow_any_instance_of(BGS::OrgWebService).to receive(:find_poa_history_by_ptcpnt_id).and_return({}) + + %w[establish_ssl_connection connection_wsdl_get connection_post parsed_response].each do |event| expect { subject.find_poa_by_participant_id('does-not-matter') } .to trigger_statsd_measure("api.claims_api.local_bgs.#{event}.duration") end @@ -104,9 +107,9 @@ ), key: 'PersonDTO').and_return(:bgs_unknown_error_message) begin - allow(soap_error_handler).to receive(:handle_errors!) + allow(soap_error_handler).to receive(:handle_errors) .with(:bgs_unknown_error_message).and_raise(Common::Exceptions::UnprocessableEntity) - ret = soap_error_handler.send(:handle_errors!, :bgs_unknown_error_message) + ret = soap_error_handler.send(:handle_errors, :bgs_unknown_error_message) expect(ret.class).to_be Array expect(ret.size).to eq 1 rescue => e From c89952168937e0755d120c9c8480936a7c105c46 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 26 Apr 2024 00:28:56 -0400 Subject: [PATCH 15/19] move one small function back to BGSHelpers --- modules/claims_api/lib/bgs_service/bgs_helpers.rb | 4 ++++ .../claims_api/lib/bgs_service/claim_management_service.rb | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/claims_api/lib/bgs_service/bgs_helpers.rb b/modules/claims_api/lib/bgs_service/bgs_helpers.rb index e4a9f3a2527..03f3c614c5f 100644 --- a/modules/claims_api/lib/bgs_service/bgs_helpers.rb +++ b/modules/claims_api/lib/bgs_service/bgs_helpers.rb @@ -27,5 +27,9 @@ def jrn jrn_obj_id: Settings.bgs.application } end + + def to_camelcase(claim:) + claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } + end end end diff --git a/modules/claims_api/lib/bgs_service/claim_management_service.rb b/modules/claims_api/lib/bgs_service/claim_management_service.rb index 39fce5ccff0..2dd7981c084 100644 --- a/modules/claims_api/lib/bgs_service/claim_management_service.rb +++ b/modules/claims_api/lib/bgs_service/claim_management_service.rb @@ -24,9 +24,5 @@ def update_claim_level_suspense(claim:) make_request(endpoint: bean_name, action: 'updateClaimLevelSuspense', body:) end - - def to_camelcase(claim:) - claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } - end end end From c4886548c1f58fd9627893fa203d1f31165c1f4a Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 26 Apr 2024 00:55:14 -0400 Subject: [PATCH 16/19] tweak `local_bgs_refactored` file organization --- .../lib/bgs_service/bgs_error_handler.rb | 73 ------- .../claims_api/lib/bgs_service/bgs_helpers.rb | 35 --- .../lib/bgs_service/local_bgs_refactored.rb | 10 +- .../local_bgs_refactored/error_handler.rb | 75 +++++++ .../local_bgs_refactored/miscellaneous.rb | 204 ++++++++++++++++++ .../miscellaneous_bgs_operations.rb | 172 --------------- 6 files changed, 283 insertions(+), 286 deletions(-) delete mode 100644 modules/claims_api/lib/bgs_service/bgs_error_handler.rb delete mode 100644 modules/claims_api/lib/bgs_service/bgs_helpers.rb create mode 100644 modules/claims_api/lib/bgs_service/local_bgs_refactored/error_handler.rb create mode 100644 modules/claims_api/lib/bgs_service/local_bgs_refactored/miscellaneous.rb delete mode 100644 modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb diff --git a/modules/claims_api/lib/bgs_service/bgs_error_handler.rb b/modules/claims_api/lib/bgs_service/bgs_error_handler.rb deleted file mode 100644 index bc953f372ea..00000000000 --- a/modules/claims_api/lib/bgs_service/bgs_error_handler.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -module ClaimsApi - # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm - # - # TODO: Some (or all) of these cases should be handled in consumers and not in - # a central location. - class BGSErrorHandler - class << self - def handle_errors!(fault) - new(fault).handle_errors! - end - end - - def initialize(fault) - @fault = fault - end - - def handle_errors! - return if not_error? - - if not_found? - raise ::Common::Exceptions::ResourceNotFound.new(detail: 'Resource not found.') - elsif bnft_claim_not_found? - {} - elsif unprocessable? - raise ::Common::Exceptions::UnprocessableEntity.new( - detail: 'Please try again after checking your input values.' - ) - else - soap_logging('500') - raise ::Common::Exceptions::ServiceError.new(detail: 'An external server is experiencing difficulty.') - end - end - - private - - def not_error? - @fault.string.include?('IntentToFileWebService') && - @fault.string.include?('not found') - end - - def not_found? - errors = ['bnftClaimId-->bnftClaimId/text()', 'not found', 'No Person found'] - has_errors = errors.any? { |error| @fault.string.include? error } - soap_logging('404') if has_errors - has_errors - end - - def bnft_claim_not_found? - errors = ['No BnftClaim found'] - has_errors = errors.any? { |error| @fault.string.include? error } - soap_logging('404') if has_errors - has_errors - end - - def unprocessable? - errors = ['java.sql', 'MessageException', 'Validation errors', 'Exception Description', - 'does not have necessary info', 'Error committing transaction', 'Transaction Rolledback', - 'Unexpected error', 'XML reader error', 'could not be converted'] - has_errors = errors.any? { |error| @fault.string.include? error } - soap_logging('422') if has_errors - has_errors - end - - def soap_logging(status_code) - ClaimsApi::Logger.log('soap_error_handler', - detail: "Returning #{status_code} via local_bgs & soap_error_handler, " \ - "fault_string: #{@fault.string}, with message: #{@fault.message}, " \ - "and fault_code: #{@fault.code}.") - end - end -end diff --git a/modules/claims_api/lib/bgs_service/bgs_helpers.rb b/modules/claims_api/lib/bgs_service/bgs_helpers.rb deleted file mode 100644 index 03f3c614c5f..00000000000 --- a/modules/claims_api/lib/bgs_service/bgs_helpers.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module ClaimsApi - module BGSHelpers - def convert_nil_values(options) - arg_strg = '' - options.each do |option| - arg = option[0].to_s.camelize(:lower) - arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") - end - arg_strg - end - - def validate_opts!(opts, required_keys) - keys = opts.keys.map(&:to_s) - required_keys = required_keys.map(&:to_s) - missing_keys = required_keys - keys - raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? - end - - def jrn - { - jrn_dt: Time.current.iso8601, - jrn_lctn_id: Settings.bgs.client_station_id, - jrn_status_type_cd: 'U', - jrn_user_id: Settings.bgs.client_username, - jrn_obj_id: Settings.bgs.application - } - end - - def to_camelcase(claim:) - claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } - end - end -end diff --git a/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb b/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb index 9778e923ea0..52a944f2e82 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs_refactored.rb @@ -7,9 +7,8 @@ # worldwide through the CC0 1.0 Universal public domain dedication. require 'claims_api/claim_logger' -require 'bgs_service/bgs_helpers' -require 'bgs_service/bgs_error_handler' -require 'bgs_service/miscellaneous_bgs_operations' +require 'bgs_service/local_bgs_refactored/error_handler' +require 'bgs_service/local_bgs_refactored/miscellaneous' module ClaimsApi # @deprecated Use {BGSClient.perform_request} instead. There ought to be a @@ -22,8 +21,7 @@ module ClaimsApi # well as the dumping and loading of business object state into this layer, # but that should live in the business logic layer and not here. class LocalBGSRefactored - include ClaimsApi::MiscellaneousBGSOperations - include ClaimsApi::BGSHelpers + include Miscellaneous class << self def breakers_service @@ -99,7 +97,7 @@ def make_request( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists return value end - BGSErrorHandler.handle_errors!( + ErrorHandler.handle_errors!( result.value ) diff --git a/modules/claims_api/lib/bgs_service/local_bgs_refactored/error_handler.rb b/modules/claims_api/lib/bgs_service/local_bgs_refactored/error_handler.rb new file mode 100644 index 00000000000..4ea8b042680 --- /dev/null +++ b/modules/claims_api/lib/bgs_service/local_bgs_refactored/error_handler.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module ClaimsApi + class LocalBGSRefactored + # list of fault codes: https://hub.verj.io/ebase/doc/SOAP_Faults.htm + # + # TODO: Some (or all) of these cases should be handled in consumers and not in + # a central location. + class ErrorHandler + class << self + def handle_errors!(fault) + new(fault).handle_errors! + end + end + + def initialize(fault) + @fault = fault + end + + def handle_errors! + return if not_error? + + if not_found? + raise ::Common::Exceptions::ResourceNotFound.new(detail: 'Resource not found.') + elsif bnft_claim_not_found? + {} + elsif unprocessable? + raise ::Common::Exceptions::UnprocessableEntity.new( + detail: 'Please try again after checking your input values.' + ) + else + soap_logging('500') + raise ::Common::Exceptions::ServiceError.new(detail: 'An external server is experiencing difficulty.') + end + end + + private + + def not_error? + @fault.string.include?('IntentToFileWebService') && + @fault.string.include?('not found') + end + + def not_found? + errors = ['bnftClaimId-->bnftClaimId/text()', 'not found', 'No Person found'] + has_errors = errors.any? { |error| @fault.string.include? error } + soap_logging('404') if has_errors + has_errors + end + + def bnft_claim_not_found? + errors = ['No BnftClaim found'] + has_errors = errors.any? { |error| @fault.string.include? error } + soap_logging('404') if has_errors + has_errors + end + + def unprocessable? + errors = ['java.sql', 'MessageException', 'Validation errors', 'Exception Description', + 'does not have necessary info', 'Error committing transaction', 'Transaction Rolledback', + 'Unexpected error', 'XML reader error', 'could not be converted'] + has_errors = errors.any? { |error| @fault.string.include? error } + soap_logging('422') if has_errors + has_errors + end + + def soap_logging(status_code) + ClaimsApi::Logger.log('soap_error_handler', + detail: "Returning #{status_code} via local_bgs & soap_error_handler, " \ + "fault_string: #{@fault.string}, with message: #{@fault.message}, " \ + "and fault_code: #{@fault.code}.") + end + end + end +end diff --git a/modules/claims_api/lib/bgs_service/local_bgs_refactored/miscellaneous.rb b/modules/claims_api/lib/bgs_service/local_bgs_refactored/miscellaneous.rb new file mode 100644 index 00000000000..c715d8c46e1 --- /dev/null +++ b/modules/claims_api/lib/bgs_service/local_bgs_refactored/miscellaneous.rb @@ -0,0 +1,204 @@ +# frozen_string_literal: true + +require 'claims_api/evss_bgs_mapper' + +module ClaimsApi + class LocalBGSRefactored + module Miscellaneous # rubocop:disable Metrics/ModuleLength + def find_poa_by_participant_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'ClaimantServiceBean/ClaimantWebService', action: 'findPOAByPtcpntId', body:, + key: 'return') + end + + def find_by_ssn(ssn) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ssn: }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'PersonWebServiceBean/PersonWebService', action: 'findPersonBySSN', body:, + key: 'PersonDTO') + end + + def find_poa_history_by_ptcpnt_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'OrgWebServiceBean/OrgWebService', action: 'findPoaHistoryByPtcpntId', body:, + key: 'PoaHistory') + end + + def find_benefit_claims_status_by_ptcpnt_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { ptcpntId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', + action: 'findBenefitClaimsStatusByPtcpntId', body:) + end + + def claims_count(id) + find_benefit_claims_status_by_ptcpnt_id(id).count + rescue ::Common::Exceptions::ResourceNotFound + 0 + end + + def find_benefit_claim_details_by_benefit_claim_id(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { bnftClaimId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', + action: 'findBenefitClaimDetailsByBnftClaimId', body:) + end + + def insert_intent_to_file(options) + request_body = construct_itf_body(options) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + + EOXML + + request_body.each do |k, z| + node = Nokogiri::XML::Node.new k.to_s, body + node.content = z.to_s + opt = body.at('intentToFileDTO') + node.parent = opt + end + make_request(endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', action: 'insertIntentToFile', + body:, key: 'IntentToFileDTO') + end + + def find_tracked_items(id) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + { claimId: id }.each do |k, v| + body.xpath("./*[local-name()='#{k}']")[0].content = v + end + + make_request(endpoint: 'TrackedItemService/TrackedItemService', action: 'findTrackedItems', body:, + key: 'BenefitClaim') + end + + def find_intent_to_file_by_ptcpnt_id_itf_type_cd(id, type) + body = Nokogiri::XML::DocumentFragment.parse <<~EOXML + + EOXML + + ptcpnt_id = body.at 'ptcpntId' + ptcpnt_id.content = id.to_s + itf_type_cd = body.at 'itfTypeCd' + itf_type_cd.content = type.to_s + + response = + make_request( + endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', + action: 'findIntentToFileByPtcpntIdItfTypeCd', + body: + ) + + Array.wrap(response[:intent_to_file_dto]) + end + + # BEGIN: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. + def update_from_remote(id) + bgs_claim = find_benefit_claim_details_by_benefit_claim_id(id) + transform_bgs_claim_to_evss(bgs_claim) + end + + def all(id) + claims = find_benefit_claims_status_by_ptcpnt_id(id) + return [] if claims.count < 1 || claims[:benefit_claims_dto].blank? + + transform_bgs_claims_to_evss(claims) + end + # END: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. + + private + + def construct_itf_body(options) + request_body = { + itfTypeCd: options[:intent_to_file_type_code], + ptcpntVetId: options[:participant_vet_id], + rcvdDt: options[:received_date], + signtrInd: options[:signature_indicated], + submtrApplcnTypeCd: options[:submitter_application_icn_type_code] + } + request_body[:ptcpntClmantId] = options[:participant_claimant_id] if options.key?(:participant_claimant_id) + request_body[:clmantSsn] = options[:claimant_ssn] if options.key?(:claimant_ssn) + request_body + end + + def transform_bgs_claim_to_evss(claim) + bgs_claim = ClaimsApi::EvssBgsMapper.new(claim[:benefit_claim_details_dto]) + return if bgs_claim.nil? + + bgs_claim.map_and_build_object + end + + def transform_bgs_claims_to_evss(claims) + claims_array = [claims[:benefit_claims_dto][:benefit_claim]].flatten + claims_array&.map do |claim| + bgs_claim = ClaimsApi::EvssBgsMapper.new(claim) + bgs_claim.map_and_build_object + end + end + + def convert_nil_values(options) + arg_strg = '' + options.each do |option| + arg = option[0].to_s.camelize(:lower) + arg_strg += (option[1].nil? ? "<#{arg} xsi:nil='true'/>" : "<#{arg}>#{option[1]}") + end + arg_strg + end + + def validate_opts!(opts, required_keys) + keys = opts.keys.map(&:to_s) + required_keys = required_keys.map(&:to_s) + missing_keys = required_keys - keys + raise ArgumentError, "Missing required keys: #{missing_keys.join(', ')}" if missing_keys.present? + end + + def jrn + { + jrn_dt: Time.current.iso8601, + jrn_lctn_id: Settings.bgs.client_station_id, + jrn_status_type_cd: 'U', + jrn_user_id: Settings.bgs.client_username, + jrn_obj_id: Settings.bgs.application + } + end + + def to_camelcase(claim:) + claim.deep_transform_keys { |k| k.to_s.camelize(:lower) } + end + end + end +end diff --git a/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb b/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb deleted file mode 100644 index 1c0e6bd41c7..00000000000 --- a/modules/claims_api/lib/bgs_service/miscellaneous_bgs_operations.rb +++ /dev/null @@ -1,172 +0,0 @@ -# frozen_string_literal: true - -require 'claims_api/evss_bgs_mapper' - -module ClaimsApi - module MiscellaneousBGSOperations # rubocop:disable Metrics/ModuleLength - def find_poa_by_participant_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ptcpntId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'ClaimantServiceBean/ClaimantWebService', action: 'findPOAByPtcpntId', body:, - key: 'return') - end - - def find_by_ssn(ssn) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ssn: }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'PersonWebServiceBean/PersonWebService', action: 'findPersonBySSN', body:, - key: 'PersonDTO') - end - - def find_poa_history_by_ptcpnt_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ptcpntId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'OrgWebServiceBean/OrgWebService', action: 'findPoaHistoryByPtcpntId', body:, - key: 'PoaHistory') - end - - def find_benefit_claims_status_by_ptcpnt_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { ptcpntId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', - action: 'findBenefitClaimsStatusByPtcpntId', body:) - end - - def claims_count(id) - find_benefit_claims_status_by_ptcpnt_id(id).count - rescue ::Common::Exceptions::ResourceNotFound - 0 - end - - def find_benefit_claim_details_by_benefit_claim_id(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { bnftClaimId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'EBenefitsBnftClaimStatusWebServiceBean/EBenefitsBnftClaimStatusWebService', - action: 'findBenefitClaimDetailsByBnftClaimId', body:) - end - - def insert_intent_to_file(options) - request_body = construct_itf_body(options) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - - EOXML - - request_body.each do |k, z| - node = Nokogiri::XML::Node.new k.to_s, body - node.content = z.to_s - opt = body.at('intentToFileDTO') - node.parent = opt - end - make_request(endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', action: 'insertIntentToFile', - body:, key: 'IntentToFileDTO') - end - - def find_tracked_items(id) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - { claimId: id }.each do |k, v| - body.xpath("./*[local-name()='#{k}']")[0].content = v - end - - make_request(endpoint: 'TrackedItemService/TrackedItemService', action: 'findTrackedItems', body:, - key: 'BenefitClaim') - end - - def find_intent_to_file_by_ptcpnt_id_itf_type_cd(id, type) - body = Nokogiri::XML::DocumentFragment.parse <<~EOXML - - EOXML - - ptcpnt_id = body.at 'ptcpntId' - ptcpnt_id.content = id.to_s - itf_type_cd = body.at 'itfTypeCd' - itf_type_cd.content = type.to_s - - response = - make_request( - endpoint: 'IntentToFileWebServiceBean/IntentToFileWebService', - action: 'findIntentToFileByPtcpntIdItfTypeCd', - body: - ) - - Array.wrap(response[:intent_to_file_dto]) - end - - # BEGIN: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. - def update_from_remote(id) - bgs_claim = find_benefit_claim_details_by_benefit_claim_id(id) - transform_bgs_claim_to_evss(bgs_claim) - end - - def all(id) - claims = find_benefit_claims_status_by_ptcpnt_id(id) - return [] if claims.count < 1 || claims[:benefit_claims_dto].blank? - - transform_bgs_claims_to_evss(claims) - end - # END: switching v1 from evss to bgs. Delete after EVSS is no longer available. Fix controller first. - - private - - def construct_itf_body(options) - request_body = { - itfTypeCd: options[:intent_to_file_type_code], - ptcpntVetId: options[:participant_vet_id], - rcvdDt: options[:received_date], - signtrInd: options[:signature_indicated], - submtrApplcnTypeCd: options[:submitter_application_icn_type_code] - } - request_body[:ptcpntClmantId] = options[:participant_claimant_id] if options.key?(:participant_claimant_id) - request_body[:clmantSsn] = options[:claimant_ssn] if options.key?(:claimant_ssn) - request_body - end - - def transform_bgs_claim_to_evss(claim) - bgs_claim = ClaimsApi::EvssBgsMapper.new(claim[:benefit_claim_details_dto]) - return if bgs_claim.nil? - - bgs_claim.map_and_build_object - end - - def transform_bgs_claims_to_evss(claims) - claims_array = [claims[:benefit_claims_dto][:benefit_claim]].flatten - claims_array&.map do |claim| - bgs_claim = ClaimsApi::EvssBgsMapper.new(claim) - bgs_claim.map_and_build_object - end - end - end -end From 7dc9ae15eae899e06e26f19ed96ac70731b7653a Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 26 Apr 2024 01:08:00 -0400 Subject: [PATCH 17/19] mv to BGSClientSpecHelpers --- modules/claims_api/README.md | 2 +- .../create_veteran_representative_request_spec.rb | 2 +- .../read_all_veteran_representatives_spec.rb | 2 +- .../veteran_representative_service_spec.rb | 1 - .../manage_representative_service/read_poa_request_spec.rb | 2 +- .../manage_representative_service/update_poa_request_spec.rb | 2 +- .../claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb | 2 +- .../claims_api/spec/lib/claims_api/vnp_person_service_spec.rb | 2 +- .../spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb | 2 +- .../v2/power_of_attorney_requests/index/rswag_spec.rb | 2 +- .../{bgs_client_helpers.rb => bgs_client_spec_helpers.rb} | 4 ++-- 11 files changed, 11 insertions(+), 12 deletions(-) rename modules/claims_api/spec/support/{bgs_client_helpers.rb => bgs_client_spec_helpers.rb} (97%) diff --git a/modules/claims_api/README.md b/modules/claims_api/README.md index 53cd34fdb30..13dab9b1d3d 100644 --- a/modules/claims_api/README.md +++ b/modules/claims_api/README.md @@ -12,7 +12,7 @@ ssh -L 4431:localhost:4431 {{aws-url}} ## Testing ### Unit testing BGS service operation wrappers If using cassettes, make sure to only make or use ones under [spec/support/vcr_cassettes/claims_api](spec/support/vcr_cassettes/claims_api) -Check out documentation in comments for the spec helper `BGSClientHelpers#use_bgs_cassette` +Check out documentation in comments for the spec helper `BGSClientSpecHelpers#use_bgs_cassette` ## OpenApi/Swagger Doc Generation This api uses [rswag](https://github.com/rswag/rswag) to build the OpenApi/Swagger docs that are displayed in the [VA|Lighthouse APIs Documentation](https://developer.va.gov/explore/benefits/docs/claims?version=current). To generate/update the docs for this api, navigate to the root directory of `vets-api` and run the following command :: diff --git a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb index ebcf7905201..47f189bdb93 100644 --- a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/veteran_representative_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb index cdbabb597e4..285ba000961 100644 --- a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/veteran_representative_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/veteran_representative_service_spec.rb b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/veteran_representative_service_spec.rb index c2db3c02fd1..5a35a2cacad 100644 --- a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/veteran_representative_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/veteran_representative_service_spec.rb @@ -2,7 +2,6 @@ require 'rails_helper' require 'bgs_service/veteran_representative_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') describe ClaimsApi::VeteranRepresentativeService do let(:header_params) do diff --git a/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb b/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb index 24ba183322a..673c5a61130 100644 --- a/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/manage_representative_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb b/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb index 525f504f95b..9fe26fc7315 100644 --- a/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/manage_representative_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb b/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb index 6260c7993a6..23c929c4410 100644 --- a/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/vnp_atchms_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb b/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb index 47acbb2681c..e02f9a65b71 100644 --- a/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/vnp_person_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb b/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb index e9dfa8550cb..2b06ac1defa 100644 --- a/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' require 'bgs_service/vnp_ptcpnt_addrs_service' -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { bgs: { diff --git a/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb b/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb index b4125980b5e..693db5b46ea 100644 --- a/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb +++ b/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb @@ -4,7 +4,7 @@ require Rails.root.join('spec', 'rswag_override.rb').to_s require 'rails_helper' require Rails.root.join('modules', 'claims_api', 'spec', 'rails_helper.rb') -require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_helpers.rb') +require Rails.root.join('modules', 'claims_api', 'spec', 'support', 'bgs_client_spec_helpers.rb') metadata = { openapi_spec: Rswag::TextHelpers.new.claims_api_docs, diff --git a/modules/claims_api/spec/support/bgs_client_helpers.rb b/modules/claims_api/spec/support/bgs_client_spec_helpers.rb similarity index 97% rename from modules/claims_api/spec/support/bgs_client_helpers.rb rename to modules/claims_api/spec/support/bgs_client_spec_helpers.rb index de8b5147708..9c8f4ede8b9 100644 --- a/modules/claims_api/spec/support/bgs_client_helpers.rb +++ b/modules/claims_api/spec/support/bgs_client_spec_helpers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module BGSClientHelpers +module BGSClientSpecHelpers # If one finds this request matcher useful elsewhere in the future, # Rather than using a callable custom request matcher: # https://benoittgt.github.io/vcr/#/request_matching/custom_matcher?id=use-a-callable-as-a-custom-request-matcher @@ -57,5 +57,5 @@ def use_bgs_cassette(name, &) end RSpec.configure do |config| - config.include BGSClientHelpers, :bgs + config.include BGSClientSpecHelpers, :bgs end From 0c82c6701a2edac26707b9e1a01a07909e363cb5 Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 26 Apr 2024 01:28:19 -0400 Subject: [PATCH 18/19] operation -> action in `BGSClientSpecHelpers` for naming consistency --- modules/claims_api/README.md | 2 +- .../create_veteran_representative_request_spec.rb | 2 +- .../read_all_veteran_representatives_spec.rb | 2 +- .../read_poa_request_spec.rb | 2 +- .../update_poa_request_spec.rb | 2 +- .../spec/lib/claims_api/vnp_atchms_service_spec.rb | 2 +- .../spec/lib/claims_api/vnp_person_service_spec.rb | 2 +- .../claims_api/vnp_ptcpnt_addrs_service_spec.rb | 2 +- .../power_of_attorney_requests/index/rswag_spec.rb | 2 +- .../spec/support/bgs_client_spec_helpers.rb | 14 +++++++------- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/claims_api/README.md b/modules/claims_api/README.md index 13dab9b1d3d..14bc40e63d2 100644 --- a/modules/claims_api/README.md +++ b/modules/claims_api/README.md @@ -10,7 +10,7 @@ ssh -L 4447:localhost:4447 {{aws-url}} ssh -L 4431:localhost:4431 {{aws-url}} ## Testing -### Unit testing BGS service operation wrappers +### Unit testing BGS service action wrappers If using cassettes, make sure to only make or use ones under [spec/support/vcr_cassettes/claims_api](spec/support/vcr_cassettes/claims_api) Check out documentation in comments for the spec helper `BGSClientSpecHelpers#use_bgs_cassette` diff --git a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb index 47f189bdb93..6b4a039e1cf 100644 --- a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/create_veteran_representative_request_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'veteran_representative_service', - operation: 'create_veteran_representative' + action: 'create_veteran_representative' } } diff --git a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb index 285ba000961..4bdcde0fdbf 100644 --- a/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/bgs/veteran_representative_service/read_all_veteran_representatives_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'veteran_representative_service', - operation: 'read_all_veteran_representatives' + action: 'read_all_veteran_representatives' }, run_at: '2024-04-17T23:10:31+00:00' } diff --git a/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb b/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb index 673c5a61130..af695f4a26c 100644 --- a/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/manage_representative_service/read_poa_request_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'manage_representative_service', - operation: 'read_poa_request' + action: 'read_poa_request' } } diff --git a/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb b/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb index 9fe26fc7315..680e7acf9b9 100644 --- a/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/manage_representative_service/update_poa_request_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'manage_representative_service', - operation: 'update_poa_request' + action: 'update_poa_request' } } diff --git a/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb b/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb index 23c929c4410..96e078dbde3 100644 --- a/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/vnp_atchms_service_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'vnp_atchms_service', - operation: 'vnp_atchms_create' + action: 'vnp_atchms_create' } } diff --git a/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb b/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb index e02f9a65b71..9cf16770b8c 100644 --- a/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/vnp_person_service_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'vnp_person_service', - operation: 'vnp_person_create' + action: 'vnp_person_create' } } diff --git a/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb b/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb index 2b06ac1defa..7a626a44c3f 100644 --- a/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/vnp_ptcpnt_addrs_service_spec.rb @@ -7,7 +7,7 @@ metadata = { bgs: { service: 'vnp_ptcpnt_addrs_service', - operation: 'vnp_ptcpnt_addrs_create' + action: 'vnp_ptcpnt_addrs_create' } } diff --git a/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb b/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb index 693db5b46ea..fa1816989a4 100644 --- a/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb +++ b/modules/claims_api/spec/requests/v2/power_of_attorney_requests/index/rswag_spec.rb @@ -11,7 +11,7 @@ production: false, bgs: { service: 'manage_representative_service', - operation: 'read_poa_request' + action: 'read_poa_request' } } diff --git a/modules/claims_api/spec/support/bgs_client_spec_helpers.rb b/modules/claims_api/spec/support/bgs_client_spec_helpers.rb index 9c8f4ede8b9..bbe5b931670 100644 --- a/modules/claims_api/spec/support/bgs_client_spec_helpers.rb +++ b/modules/claims_api/spec/support/bgs_client_spec_helpers.rb @@ -26,32 +26,32 @@ module BGSClientSpecHelpers }.freeze # This convenience method affords a handful of quality of life improvements - # for developing BGS service operation wrappers. It makes development a less + # for developing BGS service action wrappers. It makes development a less # manual process. It also turns VCR cassettes into a human readable resource # that documents the behavior of BGS. # # In order to take advantage of this method, you will need to have supplied, # to your example or example group, metadata of this form: - # `{ bgs: { service: "service", operation: "operation" } }`. + # `{ bgs: { service: "service", action: "action" } }`. # # Then, HTTP interactions that occur within the block supplied to this method # will be captured by VCR cassettes that have the following convenient # properties: - # - They will be nicely organized at `claims_api/bgs/:service/:operation/:name` + # - They will be nicely organized at `claims_api/bgs/:service/:action/:name` # - Cassette matching will be done on canonicalized XML bodies, so # reformatting cassettes for human readability won't defeat matching def use_bgs_cassette(name, &) metadata = RSpec.current_example.metadata[:bgs].to_h - service, operation = metadata.values_at(:service, :operation) + service, action = metadata.values_at(:service, :action) - if service.blank? || operation.blank? + if service.blank? || action.blank? raise ArgumentError, <<~HEREDOC Must provide spec metadata of the form: - `{ bgs: { service: "service", operation: "operation" } }' + `{ bgs: { service: "service", action: "action" } }' HEREDOC end - name = File.join('claims_api/bgs', service, operation, name) + name = File.join('claims_api/bgs', service, action, name) VCR.use_cassette(name, VCR_OPTIONS, &) end end From 847d3d5ac38b46156986c1d917fc7e51a9f10afb Mon Sep 17 00:00:00 2001 From: Oren Mittman Date: Fri, 26 Apr 2024 04:19:53 -0400 Subject: [PATCH 19/19] work on LocalBGS specs --- .../claims_api/lib/bgs_service/local_bgs.rb | 31 ++++++++++++++----- .../claims_api/local_bgs_refactored_spec.rb | 15 ++++++--- .../spec/lib/claims_api/local_bgs_spec.rb | 8 +++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index af4480bdf3c..fd891e7bc74 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -404,19 +404,36 @@ def jrn # This has to come after all `LocalBGS` method definitions. prepend( Module.new do + class << self + def prepended(klass) + # This is just exposed so that we are able to stub the proxied + # method in `RSpec` when we desire. + klass.attr_reader :refactored + end + end + + def initialize(...) + # There is no internal state that changes for instances of this class. + # It is properly stateless. This also means that there is no + # circumstance where state would ever need to be synchronized over + # time between the refactored and original instances of this module. + @refactored = LocalBGSRefactored.new(...) + super + end + meths = LocalBGS.private_instance_methods(false) + - LocalBGS.instance_methods(false) - - [:initialize] + LocalBGS.instance_methods(false) - [ + :initialize, + # These `attr_accessor` methods are not called anywhere as far as I + # can tell. + :external_uid, + :external_key + ] meths.each do |meth| define_method(meth) do |*args, **kwargs, &block| if Flipper.enabled?(:claims_api_local_bgs_refactor) - @refactored ||= - LocalBGSRefactored.new( - external_uid:, - external_key: - ) @refactored.send( meth, diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb index b8e94b87dfe..9636d8d9994 100644 --- a/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_refactored_spec.rb @@ -2,12 +2,11 @@ require 'rails_helper' require 'bgs_service/local_bgs' -require 'claims_api/error/soap_error_handler' describe ClaimsApi::LocalBGS do subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } - let(:soap_error_handler) { ClaimsApi::SoapErrorHandler } + let(:soap_error_handler) { ClaimsApi::LocalBGSRefactored::ErrorHandler } describe '#find_poa_by_participant_id' do it 'responds as expected, with extra ClaimsApi::Logger logging' do @@ -63,7 +62,9 @@ it 'returns an empty array' do expect(error_message.count).to eq(2) # trick the claims count check # error message should trigger return - allow(subject_instance).to receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(error_message) + allow(subject_instance.refactored).to( + receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(error_message) + ) expect(subject.all(id)).to eq([]) # verify correct return end end @@ -73,7 +74,9 @@ VCR.use_cassette('claims_api/bgs/claims/claims_trimmed_down') do claims = subject_instance.find_benefit_claims_status_by_ptcpnt_id('600061742') claims[:benefit_claims_dto][:benefit_claim] = claims[:benefit_claims_dto][:benefit_claim][0] - allow(subject_instance).to receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(claims) + allow(subject_instance.refactored).to( + receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(claims) + ) begin ret = subject_instance.send(:transform_bgs_claims_to_evss, claims) @@ -90,7 +93,9 @@ context 'when an empty array gets returned it still does not pass the count check' do it 'returns an empty array' do # error message should trigger return - allow(subject_instance).to receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(empty_array) + allow(subject_instance.refactored).to( + receive(:find_benefit_claims_status_by_ptcpnt_id).with(id).and_return(empty_array) + ) expect(subject.all(id)).to eq([]) # verify correct return end end diff --git a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb index 482c897a6d8..7f7af23c280 100644 --- a/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb +++ b/modules/claims_api/spec/lib/claims_api/local_bgs_spec.rb @@ -7,6 +7,14 @@ describe ClaimsApi::LocalBGS do subject { described_class.new external_uid: 'xUid', external_key: 'xKey' } + before do + allow(Flipper).to( + receive(:enabled?) + .with(:claims_api_local_bgs_refactor) + .and_return(false) + ) + end + let(:soap_error_handler) { ClaimsApi::SoapErrorHandler.new } describe '#find_poa_by_participant_id' do