Skip to content

Commit

Permalink
Added support for custom http client
Browse files Browse the repository at this point in the history
  • Loading branch information
a-ibarra committed Nov 4, 2022
1 parent d89b15b commit 32520a3
Show file tree
Hide file tree
Showing 13 changed files with 133 additions and 67 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Metrics/ParameterLists:
Metrics/ClassLength:
Max: 110

Metrics/CyclomaticComplexity:
Max: 8

Style/Documentation:
Enabled: false

Expand Down
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,63 @@ All the API responses that do not fall in the 2** status codes will cause a `Che
exception
encapsulates the `http_metadata` and a map of `error_details`, if available.

## Custom Http Client

Ruby SDK supports your own configuration for `http client` using `Faraday::Connection` object, you can pass
through the
SDK instantiation as follows:

```ruby
http_client = Faraday.new do |conn|
conn.options.timeout = 10
conn.proxy "http://localhost:8080"
end

api = CheckoutSdk.builder
.static_keys
.with_secret_key('secret_key')
.with_public_key('public_key') # optional, only required for operations related with tokens
.with_environment(CheckoutSdk::Environment.sandbox)
.with_http_client(http_client)
.build
```

You don't need to specify the URL for Faraday constructor the SDK grabs the belong URI from `CheckoutSdk::Environment`
however if you want to
use specific URI's without a proxy you can create the `Environment` object as follows.

```ruby
environment = CheckoutSdk::Environment.new('https://the.base.uri/', # the uri for all CKO operations
'https://the.oauth.uri/connect/token', # the uri used for OAUTH authorization, only required for OAuth operations
'https://the.files.uri/', # the uri used for Files operations, only required for Accounts module
'https://the.transfers.uri/', # the uri used for Transfer operations, only required for Transfers module
'https://the.balances.uri/', # the uri used for Balances operations, only required for Balances module
false)
```

If you want to provide your own `http client` and wants to use the `upload_file` functionality from `Disputes` or `Accounts` modules, you
also need to specify the custom `http client` for `multipart requests`:

```ruby
http_client = Faraday.new do |conn|
conn.options.timeout = 10
conn.proxy "http://localhost:8080"
end

multipart_client = Faraday.new do |f|
f.request :multipart
end

api = CheckoutSdk.builder
.static_keys
.with_secret_key('secret_key')
.with_public_key('public_key') # optional, only required for operations related with tokens
.with_environment(CheckoutSdk::Environment.sandbox)
.with_http_client(http_client)
.with_multipart_http_client(multipart_client)
.build
```

## Building from source

Once you check out the code from GitHub, the project can be built using gem:
Expand Down
29 changes: 25 additions & 4 deletions lib/checkout_sdk/abstract_checkout_sdk_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,45 @@ class AbstractCheckoutSdkBuilder
# @!attribute environment
# @return [Environment]
# @!attribute http_client
# @return [Object]
attr_accessor :environment, :http_client
# @return [Faraday::Connection]
# @!attribute multipart_http_client
# @return [Faraday::Connection]
attr_accessor :environment, :http_client, :multipart_http_client

# @param [Environment] environment
def with_environment(environment)
@environment = environment
self
end

# @param [Faraday::Connection] http_client
def with_http_client(http_client)
@http_client = http_client
self
end

# @param [Faraday::Connection] multipart_http_client
def with_multipart_http_client(multipart_http_client)
@multipart_http_client = multipart_http_client
self
end

def build
with_environment(Environment.sandbox) if environment.nil?
raise CheckoutArgumentException, 'HttpClient must be an instance of Faraday::Connection' \
if !http_client.nil? && !@http_client.instance_of?(Faraday::Connection)
if http_client.nil?
@http_client = CheckoutUtils.build_default_client
else
unless @http_client.instance_of?(Faraday::Connection)
raise CheckoutArgumentException, 'http_client must be an instance of Faraday::Connection'
end
end
if multipart_http_client.nil?
@multipart_http_client = CheckoutUtils.build_multipart_client
else
unless @multipart_http_client.instance_of?(Faraday::Connection)
raise CheckoutArgumentException, 'multipart_http_client must be an instance of Faraday::Connection'
end
end
end
end
end
29 changes: 10 additions & 19 deletions lib/checkout_sdk/api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

module CheckoutSdk
class ApiClient
# @param [CheckoutConfiguration] _configuration
attr_accessor :client, :multipart_client

# @param [CheckoutConfiguration] configuration
# @param [String] uri
def initialize(_configuration, uri)
# TODO: expect given http client
@client = build_default_client(uri)
@multipart_client = build_multipart_client(uri)
def initialize(configuration, uri)
@client = configuration.http_client.clone
@client.url_prefix = uri
@multipart_client = configuration.multipart_http_client.clone
@multipart_client.url_prefix = uri
end

# @param [String] path
Expand Down Expand Up @@ -51,19 +54,6 @@ def submit_file(path,

private

def build_multipart_client(uri)
Faraday.new(uri) do |f|
f.request :multipart
f.response :raise_error
end
end

def build_default_client(uri)
Faraday.new(uri) do |f|
f.response :raise_error
end
end

def invoke(method, path, authorization, body = nil, idempotency_key = nil, params: nil)
path = append_params(path, params) unless params.nil?

Expand Down Expand Up @@ -91,7 +81,6 @@ def get_default_headers(authorization)
end

def append_params(path, input_params)
# TODO: refactor this into a utils method
hash = CheckoutSdk::JsonSerializer.to_custom_hash(input_params)
params = URI.encode_www_form(hash)
"#{path}?#{params}"
Expand Down Expand Up @@ -128,6 +117,8 @@ def upload_file(path, authorization, file_request)
end

def parse_response(response)
raise CheckoutApiException, response if response.status < 200 || response.status >= 300

metadata = CheckoutUtils.map_to_http_metadata(response)
body = (JSON.parse(response.body, object_class: OpenStruct) if !response.body.nil? && response.body != '')
body = OpenStruct.new if body.nil?
Expand Down
4 changes: 2 additions & 2 deletions lib/checkout_sdk/checkout_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ def initialize(configuration)
# @param [CheckoutConfiguration] configuration
# @return [ApiClient]
def base_api_client(configuration)
CheckoutSdk::ApiClient.new configuration, configuration.environment.base_uri
ApiClient.new(configuration, configuration.environment.base_uri)
end

# @param [CheckoutConfiguration] configuration
# @return [ApiClient]
def files_client(configuration)
CheckoutSdk::ApiClient.new configuration, configuration.environment.files_uri
ApiClient.new(configuration, configuration.environment.files_uri)
end
end
end
10 changes: 6 additions & 4 deletions lib/checkout_sdk/checkout_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ module CheckoutSdk
# @!attribute environment
# @return [Environment]
# @!attribute http_client
# @return [Object]
# @return [Faraday::Connection]
class CheckoutConfiguration
attr_accessor :credentials, :environment, :http_client
attr_accessor :credentials, :environment, :http_client, :multipart_http_client

# @param [SdkCredentials] credentials
# @param [Environment] environment
# @param [Object] http_client
def initialize(credentials, environment, http_client)
# @param [Faraday::Connection] http_client
# @param [Faraday::Connection] multipart_http_client
def initialize(credentials, environment, http_client, multipart_http_client)
@credentials = credentials
@environment = environment
@http_client = http_client
@multipart_http_client = multipart_http_client
end
end
end
4 changes: 3 additions & 1 deletion lib/checkout_sdk/checkout_oauth_sdk_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ def with_scopes(scopes)

# @return [CheckoutSdk::CheckoutApi]
def build
super
CheckoutSdk::CheckoutApi.new(
CheckoutConfiguration.new(
OAuthSdkCredentials.new(client_id, client_secret, scopes, http_client, environment, authorization_uri),
environment,
http_client
http_client,
multipart_http_client
)
)
end
Expand Down
3 changes: 2 additions & 1 deletion lib/checkout_sdk/checkout_static_keys_sdk_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def build
CheckoutConfiguration.new(
StaticKeysSdkCredentials.new(secret_key, public_key),
environment,
http_client
http_client,
multipart_http_client
)
)
end
Expand Down
15 changes: 15 additions & 0 deletions lib/checkout_sdk/checkout_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,20 @@ def self.map_to_http_metadata(response)
end
metadata
end

# @return [Faraday::Connection]
def self.build_multipart_client
Faraday.new do |f|
f.request :multipart
f.response :raise_error
end
end

# @return [Faraday::Connection]
def self.build_default_client
Faraday.new do |f|
f.response :raise_error
end
end
end
end
18 changes: 5 additions & 13 deletions lib/checkout_sdk/oauth_sdk_credentials.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class OAuthSdkCredentials < SdkCredentials
# @param [String] client_id
# @param [String] client_secret
# @param [Array(CheckoutSdk::OAuthScopes)] scopes
# @param [Faraday] http_client
# @param [Faraday::Connection] http_client
# @param [CheckoutSdk::Environment] environment
# @param [String, nil] auth_uri
def initialize(client_id, client_secret, scopes, http_client, environment, auth_uri = nil)
Expand All @@ -36,7 +36,8 @@ def initialize(client_id, client_secret, scopes, http_client, environment, auth_
@client_secret = client_secret
@scopes = scopes
@authorization_uri = auth_uri.nil? ? environment.authorization_uri : auth_uri
@http_client = http_client.nil? ? build_default_client(authorization_uri) : http_client
@oauth_http_client = http_client.clone
@oauth_http_client.url_prefix = @authorization_uri
build_access_token
end

Expand Down Expand Up @@ -66,7 +67,7 @@ def build_access_token
body = URI.encode_www_form data

begin
response = @http_client.run_request(:post, @authorization_uri, body, headers)
response = @oauth_http_client.run_request(:post, @authorization_uri, body, headers)
rescue Faraday::ClientError => e
raise CheckoutAuthorizationException, e.response
rescue Faraday::ConnectionFailed => e
Expand All @@ -85,19 +86,10 @@ def build_access_token

private

# TODO: refactor this into a utils method
# @param [String] authorization_uri
# @return [Faraday::Connection]
def build_default_client(authorization_uri)
Faraday.new(authorization_uri) do |f|
f.response(:raise_error)
end
end

# @param [String] client_id
# @param [String] client_secret
# @param [Environment] environment
# @param [String] authorization_uri
# @param [String, nil] authorization_uri
def validate_arguments(client_id, client_secret, environment, authorization_uri)
if client_id.nil? || client_secret.nil?
raise CheckoutArgumentException, 'Invalid OAuth "client_id" or "client_secret"'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def build
CheckoutConfiguration.new(
PreviousStaticKeysSdkCredentials.new(secret_key, public_key),
environment,
http_client
http_client,
multipart_http_client
)
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@
end

context 'when requesting Sofort source payment' do
it 'should request payment correctly' do
it 'raises an error (apm_service_unavailable)' do
source = CheckoutSdk::Payments::SofortSource.new

request = CheckoutSdk::Payments::PaymentRequest.new
Expand All @@ -277,14 +277,8 @@
request.success_url = 'https://testing.checkout.com/sucess'
request.failure_url = 'https://testing.checkout.com/failure'

response = default_sdk.payments.request_payment(request)
expect(response).not_to be nil
expect(response.id).not_to be nil

payment_details = default_sdk.payments.get_payment_details(response.id)
expect(payment_details).not_to be nil
expect(payment_details.id).to eq response.id
expect(payment_details.source.type).to eq CheckoutSdk::Common::PaymentSourceType::SOFORT
expect { default_sdk.payments.request_payment(request) }
.to raise_error(CheckoutSdk::CheckoutApiException) { |e| expect(e.error_details[:error_codes].first).to eq 'apm_service_unavailable' }
end
end

Expand Down
13 changes: 0 additions & 13 deletions spec/checkout_sdk_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,6 @@
'Invalid OAuth "client_id" or "client_secret"')
end

it 'raises an error when environment and authorization_uri are not provided' do
expect do
CheckoutSdk.builder
.oauth
.with_client_credentials(ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_ID'],
ENV['CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET'])
.with_scopes([CheckoutSdk::OAuthScopes::VAULT,
CheckoutSdk::OAuthScopes::GATEWAY])
.build
end.to raise_error(CheckoutSdk::CheckoutArgumentException,
'Invalid configuration. Please specify an "environment" or a specific OAuth "authorization_uri"')
end

it 'raises an error when the authorization_uri provided is invalid' do
expect do
CheckoutSdk.builder
Expand Down

0 comments on commit 32520a3

Please sign in to comment.