Class: Common::Client::Base

Inherits:
Object
  • Object
show all
Includes:
SentryLogging
Defined in:
lib/common/client/base.rb

Overview

Base class for creating HTTP services. Wraps the Faraday gem and is configured via by passing in a Configuration::REST or Configuration::SOAP depending on the type of service you’re connecting to. Once configured requests are made via the ‘perform` method.

Examples:

Create a service and make a GET request

class MyService < Common::Client::Base
  configuration MyConfiguration

  def get_resource
    perform(:get, '/api/v1/resource')
  end
end

service = MyService.new
response = service.get_resource

a POST request with a body, headers, and Faraday options

def post_resource(json)
  headers = { 'Content-Type' => 'application/json' }
  options = { timeout: 60 }
  response = perform(:post, '/submit', json, headers, options)
end

Direct Known Subclasses

Apps::Client, Auth::ClientCredentials::Service, BB::Client, BBInternal::Client, BID::Service, BenefitsClaims::Service, BenefitsDocuments::Form526::DocumentsStatusPollingService, BenefitsDocuments::Service, BenefitsDocuments::WorkerService, BenefitsEducation::Service, BenefitsIntake::Service, BenefitsIntakeService::Service, BenefitsReferenceData::Service, BipClaims::Service, CARMA::Client::MuleSoftAuthTokenClient, CARMA::Client::MuleSoftClient, Caseflow::Service, CentralMail::Service, Chip::Service, DebtManagementCenter::BaseService, DecisionReview::PdfValidation::Service, DecisionReview::Service, DecisionReviewV1::Service, DirectDeposit::Client, EVSS::Service, Form1010Ezr::Service, Forms::Client, GI::Client, HCA::EnrollmentEligibility::Service, HCA::Service, IAMSSOeOAuth::Service, IHub::Service, LGY::Service, Lighthouse::Facilities::Client, Lighthouse::Facilities::V1::Client, Lighthouse::LettersGenerator::Service, Lighthouse::VeteransHealth::Client, MAP::SecurityToken::Service, MAP::SignUp::Service, MDOT::Client, MHV::AccountCreation::Service, MHVAC::Client, MHVLogging::Client, MPI::Service, MailAutomation::Client, MedicalRecords::Client, MedicalRecords::LighthouseClient, Okta::DirectoryService, PHRMgr::Client, PagerDuty::Service, Post911SOB::DGIB::Client, Preneeds::Service, RES::Service, Rx::Client, SM::Client, Salesforce::Service, Search::Service, SearchClickTracking::Service, SearchGsa::Service, SearchTypeahead::Service, SignIn::Idme::Service, SignIn::Logingov::Service, TokenValidation::V2::Client, UserEligibility::Client, V0::VirtualAgent::JwtToken, VAProfile::Profile::V3::Service, VAProfile::Service, VBS::Client, VEText::Service, VRE::Service, VeteranVerification::Service, VirtualRegionalOffice::Client

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SentryLogging

#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata

Class Method Details

.configuration(configuration = nil) ⇒ Object

Sets the configuration singleton to use



53
54
55
# File 'lib/common/client/base.rb', line 53

def self.configuration(configuration = nil)
  @configuration ||= configuration.instance
end

Instance Method Details

#configObject (private)



68
69
70
# File 'lib/common/client/base.rb', line 68

def config
  self.class.configuration
end

#connectionObject (private)



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/common/client/base.rb', line 76

def connection
  @connection ||= lambda do
    connection = config.connection
    handlers = connection.builder.handlers
    adapter = connection.builder.adapter

    if adapter == Faraday::Adapter::HTTPClient &&
       handlers.exclude?(Common::Client::Middleware::Request::RemoveCookies)
      raise SecurityError, 'http client needs cookies stripped'
    end

    if handlers.include?(Breakers::UptimeMiddleware)
      return connection if handlers.first == Breakers::UptimeMiddleware

      raise BreakersImplementationError, 'Breakers should be the first middleware implemented.'
    else
      warn("Breakers is not implemented for service: #{service_name}")
    end

    connection
  end.call
end

#delete(path, params, headers, options) ⇒ Object (private)



158
159
160
# File 'lib/common/client/base.rb', line 158

def delete(path, params, headers, options)
  request(:delete, path, params, headers, options)
end

#get(path, params, headers, options) ⇒ Object (private)



146
147
148
# File 'lib/common/client/base.rb', line 146

def get(path, params, headers, options)
  request(:get, path, params, headers, options)
end

#perform(method, path, params, headers = nil, options = nil) ⇒ Object (private)

Raises:

  • (NoMethodError)


99
100
101
102
103
# File 'lib/common/client/base.rb', line 99

def perform(method, path, params, headers = nil, options = nil)
  raise NoMethodError, "#{method} not implemented" unless config.request_types.include?(method)

  send(method, path, params || {}, headers || {}, options || {})
end

#post(path, params, headers, options) ⇒ Object (private)



150
151
152
# File 'lib/common/client/base.rb', line 150

def post(path, params, headers, options)
  request(:post, path, params, headers, options)
end

#put(path, params, headers, options) ⇒ Object (private)



154
155
156
# File 'lib/common/client/base.rb', line 154

def put(path, params, headers, options)
  request(:put, path, params, headers, options)
end

#raise_backend_exception(key, source, error = nil) ⇒ Object



57
58
59
60
61
62
63
64
# File 'lib/common/client/base.rb', line 57

def raise_backend_exception(key, source, error = nil)
  raise Common::Exceptions::BackendServiceException.new(
    key,
    { source: source.to_s },
    error&.status,
    error&.body
  )
end

#raise_not_authenticatedObject (private)



162
163
164
# File 'lib/common/client/base.rb', line 162

def raise_not_authenticated
  raise Common::Client::Errors::NotAuthenticated, 'Not Authenticated'
end

#request(method, path, params = {}, headers = {}, options = {}) ⇒ Object (private)

rubocop:disable Metrics/MethodLength



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/common/client/base.rb', line 105

def request(method, path, params = {}, headers = {}, options = {}) # rubocop:disable Metrics/MethodLength
  Datadog::Tracing.active_span&.set_tag('common_client_service', service_name)
  sanitize_headers!(method, path, params, headers)
  raise_not_authenticated if headers.keys.include?('Token') && headers['Token'].nil?
  connection.send(method.to_sym, path, params) do |request|
    request.headers.update(headers)
    options.each { |option, value| request.options.send("#{option}=", value) }
  end.env
rescue Common::Exceptions::BackendServiceException => e
  # convert BackendServiceException into a more meaningful exception title for Sentry
  raise config.service_exception.new(
    e.key, e.response_values, e.original_status, e.original_body
  )
rescue Timeout::Error, Faraday::TimeoutError => e
  Sentry.set_extras(service_name:, url: path)
  raise Common::Exceptions::GatewayTimeout, e.class.name
rescue Faraday::ClientError, Faraday::ServerError, Faraday::Error => e
  error_class = case e
                when Faraday::ParsingError
                  Common::Client::Errors::ParsingError
                else
                  Common::Client::Errors::ClientError
                end

  response_hash = e.response&.to_hash
  client_error = error_class.new(e.message, response_hash&.dig(:status), response_hash&.dig(:body))
  raise client_error
end

#sanitize_headers!(_method, _path, _params, headers) ⇒ Object (private)



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/common/client/base.rb', line 134

def sanitize_headers!(_method, _path, _params, headers)
  headers.transform_keys!(&:to_s)

  headers.transform_values! do |value|
    if value.nil?
      ''
    else
      value
    end
  end
end

#service_nameObject (private)



72
73
74
# File 'lib/common/client/base.rb', line 72

def service_name
  config.service_name
end