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?