Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,5 @@ config/private.json
config/keys.json
stripe-ruby/
*.md
/exemples
/exemples
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ group :development do
gem 'webmock', '~> 3.26'
gem 'yard', '~> 0.9.37'
end

gem 'dotenv', '~> 3.1'
7 changes: 0 additions & 7 deletions exe/bridge_api

This file was deleted.

7 changes: 7 additions & 0 deletions lib/bridge_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,10 @@ def default_base_url
require_relative 'bridge_api/resources/total_balance'
require_relative 'bridge_api/models/wallets_collection'
require_relative 'bridge_api/client'
require_relative 'bridge_api/services/base_service'
require_relative 'bridge_api/services/kyc_link_service'
require_relative 'bridge_api/services/customer_service'
require_relative 'bridge_api/services/wallet_service'
require_relative 'bridge_api/services/external_account_service'
require_relative 'bridge_api/services/virtual_account_service'
require_relative 'bridge_api/services/webhook_service'
6 changes: 4 additions & 2 deletions lib/bridge_api/api_operations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ def list(client, params = {})
response = client.request(:get, resource_path, params)
return response unless response.success?

# Create a list response - for now return as is but can be enhanced later
response
BridgeApi::Util.convert_to_bridged_object(
response,
resource_hint: self::OBJECT_NAME,
)
end
end
end
Expand Down
16 changes: 16 additions & 0 deletions lib/bridge_api/base_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,27 @@ def set_attribute(key, value)
@unsaved_values.add(key)
end

# Dynamic method handling for all attributes in @values
# Only allows attribute access without arguments, raises error if arguments are provided
def method_missing(method_name, *args)
if args.empty? && @values.key?(method_name)
@values[method_name]
else
super
end
end

def respond_to_missing?(method_name, include_private = false)
@values.key?(method_name) || super
end

# Convert values to appropriate types when possible
def convert_value(value)
case value
when Array
value.map { |v| convert_value(v) }
when Hash
value.each_with_object({}) { |(k, v), hash| hash[k.to_sym] = convert_value(v) }
else
value
end
Expand Down
93 changes: 91 additions & 2 deletions lib/bridge_api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ def build_request_options(method, payload, idempotency_key: nil)
if %i[get delete].include?(method)
{ query: payload }
else
headers = { 'Idempotency-Key' => idempotency_key || SecureRandom.uuid }
# Only add Idempotency-Key if explicitly provided or for POST/PATCH
# PUT requests (like webhook updates) don't support idempotency keys
headers = {}
if idempotency_key || %i[post patch].include?(method)
headers['Idempotency-Key'] = idempotency_key || SecureRandom.uuid
end
{ body: payload.to_json, headers: headers }
end
end
Expand All @@ -92,11 +97,85 @@ def retry_request(method, endpoint, payload, retries, response)
request(method, endpoint, payload, retries + 1)
end

# --- Resource Accessor Support ---
class ResourceAccessor
def initialize(client, resource_name)
@client = client
@resource_name = resource_name
@singular_resource_name = resource_name.to_s.sub(/s$/, '')
end

def list(params = {})
@client.send("list_#{@resource_name}", params)
end

def get(id)
@client.send("get_#{@singular_resource_name}", id)
end

def retrieve(id)
@client.send("get_#{@singular_resource_name}", id)
end

def create(params = {}, idempotency_key: nil)
method_name = if idempotency_key
"create_#{@singular_resource_name}_with_idempotency"
else
"create_#{@singular_resource_name}"
end
if @client.respond_to?(method_name)
@client.send(method_name, params, idempotency_key: idempotency_key)
else
@client.send(:request, :post, @resource_name, params)
end
end

def update(id, params = {}, idempotency_key: nil)
method_name = if idempotency_key
"update_#{@singular_resource_name}_with_idempotency"
else
"update_#{@singular_resource_name}"
end
if @client.respond_to?(method_name)
@client.send(method_name, id, params, idempotency_key: idempotency_key)
else
# Use PUT for webhooks, PATCH for others
http_method = @resource_name == :webhooks ? :put : :patch
@client.send(:request, http_method, "#{@resource_name}/#{id}", params)
end
end

def delete(id)
method_name = "delete_#{@singular_resource_name}"
if @client.respond_to?(method_name)
@client.send(method_name, id)
else
@client.send(:request, :delete, "#{@resource_name}/#{id}", {})
end
end

private

def method_missing(method_name, *, &)
# Delegate other methods to the client that start with the resource name
if @client.respond_to?(method_name)
@client.send(method_name, *)
else
super
end
end

def respond_to_missing?(method_name, include_private = false)
@client.respond_to?(method_name) || super
end
end

# --- Class Methods ---
class << self
def define_dynamic_resource_methods
(RESOURCES + READ_ONLY_RESOURCES).each do |resource|
define_resource_methods(resource)
define_resource_accessor(resource)
end
end

Expand All @@ -111,6 +190,16 @@ def define_resource_methods(resource)
define_special_methods(resource) unless READ_ONLY_RESOURCES.include?(resource)
end

def define_resource_accessor(resource)
define_method(resource) do
instance_variable_name = "@#{resource}_accessor"
unless instance_variable_defined?(instance_variable_name)
instance_variable_set(instance_variable_name, ResourceAccessor.new(self, resource))
end
instance_variable_get(instance_variable_name)
end
end

def define_special_methods(resource)
send("define_#{resource}_methods") if respond_to?("define_#{resource}_methods", true)
end
Expand Down Expand Up @@ -200,7 +289,7 @@ def define_webhooks_methods
end
end

private :define_resource_methods, :define_special_methods,
private :define_resource_methods, :define_special_methods, :define_resource_accessor,
:define_wallets_methods, :define_customers_methods,
:define_customers_wallet_methods, :define_customers_virtual_account_methods,
:define_webhooks_methods
Expand Down
25 changes: 2 additions & 23 deletions lib/bridge_api/resources/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,56 +19,34 @@ def self.resource_path
include BridgeApi::APIOperations::Delete

def self.get_customer_wallets(client, customer_id, params = {})
# Use the client's public API to make the request
client.get_customer_wallets(customer_id, params)
end

def self.create_wallet_for_customer(client, customer_id, chain, idempotency_key: nil)
# Use the client's public API to make the request
client.create_customer_wallet(customer_id, chain, idempotency_key: idempotency_key)
end

def self.get_customer_wallet(client, customer_id, wallet_id)
# Use the client's public API to make the request
client.get_customer_wallet(customer_id, wallet_id)
end

def self.get_customer_virtual_accounts(client, customer_id, params = {})
# Use the client's public API to make the request
client.list_customer_virtual_accounts(customer_id, params)
end

def self.get_customer_virtual_account(client, customer_id, virtual_account_id)
# Use the client's public API to make the request
client.get_customer_virtual_account(customer_id, virtual_account_id)
end

def self.create_customer_virtual_account(client, customer_id, params, idempotency_key: nil)
# Use the client's public API to make the request
client.create_customer_virtual_account(customer_id, params, idempotency_key: idempotency_key)
end

def initialize(attributes = {})
super
end

# Specific accessor methods for convenience
def id
@values[:id]
end

def email
@values[:email]
end

def first_name
@values[:first_name]
end

def last_name
@values[:last_name]
end

# Override datetime accessors to return parsed Time objects
def created_at
parse_datetime(@values[:created_at])
end
Expand Down Expand Up @@ -101,6 +79,7 @@ def create_virtual_account(client, params, idempotency_key: nil)
self.class.create_customer_virtual_account(client, id, params, idempotency_key: idempotency_key)
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/kyc_link.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def created_at
parse_datetime(@values[:created_at])
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/reward_rate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def expires_at
parse_datetime(@values[:expires_at])
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/transaction_history.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def destination
@values[:destination]
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/virtual_account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def balances
@values[:balances] || []
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/wallet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def self.get_for_customer(client, customer_id, wallet_id)
client.get_customer_wallet(customer_id, wallet_id)
end


private

# Parse a datetime string to a Time object
Expand Down
3 changes: 2 additions & 1 deletion lib/bridge_api/resources/webhook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def self.retrieve(client, id, _params = {})
end

def self.create(client, params = {})
client.create_webhook(params)
super
end

# Specific accessor methods for convenience
Expand Down Expand Up @@ -49,6 +49,7 @@ def created_at
parse_datetime(@values[:created_at])
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/webhook_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def event_created_at
parse_datetime(@values[:event_created_at])
end


private

# Parse a datetime string to a Time object
Expand Down
1 change: 1 addition & 0 deletions lib/bridge_api/resources/webhook_event_delivery_log.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def created_at
parse_datetime(@values[:created_at])
end


private

# Parse a datetime string to a Time object
Expand Down
20 changes: 20 additions & 0 deletions lib/bridge_api/services/base_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true
module BridgeApi
module Services
class BaseService
def initialize(client)
@client = client
end

protected

def request(method, endpoint, params = {}, idempotency_key: nil)
if idempotency_key
@client.send(:request_with_idempotency, method, endpoint, params, idempotency_key)
else
@client.send(:request, method, endpoint, params)
end
end
end
end
end
26 changes: 26 additions & 0 deletions lib/bridge_api/services/customer_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true
module BridgeApi
module Services
class CustomerService < BaseService
def create(params = {}, idempotency_key: nil)
request(:post, 'customers', params, idempotency_key: idempotency_key)
end

def retrieve(id, params = {})
response = request(:get, "customers/#{id}", params)
return response unless response.success?

# Return a Customer object directly if the request was successful
BridgeApi::Resources::Customer.construct_from(response.data)
end

def list(params = {})
request(:get, 'customers', params)
end

def update(id, params = {}, idempotency_key: nil)
request(:patch, "customers/#{id}", params, idempotency_key: idempotency_key)
end
end
end
end
Loading