Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Model base API improvements #5

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ movements = account.get_movements
# Or get all the movements since a specific date
movements = account.get_movements(since: '2020-08-15')

```
## Quickstart with the new refactored API
```ruby
require 'fintoc'

Fintoc.api_key = 'sk_test_9c8d8CeyBTx1VcJzuDgpm4H-bywJCeSx'
link = Fintoc::Link.find('6n12zLmai3lLE9Dq_token_gvEJi8FrBge4fb3cz7Wp856W')
account = link.accounts.find(type: 'checking_account')

# Get the las 30 movements
movements = account.movements

# Or get all the movements since a specific date
movements = account.movements.find(since: '2020-08-15')
```
And that’s it!

Expand All @@ -86,6 +100,22 @@ link.show_accounts

```

### Get accounts with the new refactored API

```ruby
require 'fintoc'

client = Fintoc.api_key = 'api_key'
link = Fintoc::Link.find('link_token')
puts link.accounts

# Or... you can pretty print all the accounts in a Link

link = Fintoc::Link.find('link_token')
link.show_accounts

```

If you want to find a specific account in a link, you can use **find**. You can search by any account field:

```ruby
Expand Down
15 changes: 15 additions & 0 deletions lib/fintoc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,20 @@
require 'fintoc/errors'
require 'fintoc/client'

#Name API resources
require 'fintoc/resources'

#Support classes
require 'fintoc/resource'


#API Operations
require 'fintoc/api_operations/request'
require 'fintoc/api_operations/delete'


module Fintoc
class << self
attr_accessor :api_key
end
end
9 changes: 9 additions & 0 deletions lib/fintoc/api_operations/delete.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Fintoc
module APIOperations
module Delete
def delete
request(:delete, resource_url)
end
end
end
end
45 changes: 45 additions & 0 deletions lib/fintoc/api_operations/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Fintoc
module APIOperations
module Request
def self.included(base)
base.extend(ClassMethods)
end

module ClassMethods
def request(method, url, params={})
check_api_key!
raise "Not supported method" unless %i[get delete].include(method)
api_client.send(method).call(url, **params)
end

def fetch_next(method, url, params={})
first = request(method, url, params)
return first if params.empty?

first + Utils.flatten(api_client.fetch_next)
end

def api_client
@api_client = Client.new(Fintoc.api_key)
end

protected def check_api_key!
return if Fintoc.api_key

raise AuthenticationError, <<~MSG
No API key provided.
Set your API key using 'Fitoc.api_key = <API-KEY>'
MSG
end
end

protected def request(method, url, params = {})
self.class.request(method, url, params)
end

protected def fetch_next(method, url, params = {})
self.class.fetch_next(method, url, params)
end
end
end
end
3 changes: 1 addition & 2 deletions lib/fintoc/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def initialize(api_key)
@user_agent = "fintoc-ruby/#{Fintoc::VERSION}"
@headers = { "Authorization": @api_key, "User-Agent": @user_agent }
@link_headers = nil
@link_header_pattern = '<(?<url>.*)>;\s*rel="(?<rel>.*)"'
@default_params = {}
end

Expand Down Expand Up @@ -82,7 +81,7 @@ def client
end

def parse_headers(dict, link)
matches = link.strip.match(@link_header_pattern)
matches = link.strip.match(Fintoc::Constants::PAGINATION_REGEX)
dict[matches[:rel]] = matches[:url]
dict
end
Expand Down
1 change: 1 addition & 0 deletions lib/fintoc/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ module Constants
GENERAL_DOC_URL = "https://fintoc.com/docs"
SCHEME = "https://"
BASE_URL = "api.fintoc.com/v1/"
PAGINATION_REGEX = '<(?<url>.*)>;\s*rel="(?<rel>.*)"'
end
end
105 changes: 105 additions & 0 deletions lib/fintoc/resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
module Fintoc
class Resource
include Fintoc::APIOperations::Request
include Fintoc::APIOperations::Delete

class << self
attr_reader :resource_url

def class_name
name.split("::")[-1]
end

def resource_url
if self == Resource
raise NotImplementedError, <<~MSG
Fintoc::Resource is an abstract class. You should perform actions
on its subsclasses (Link, Account, Movement, etc)
MSG
end

resource_url || "#{class_name.downcase}"
end

def find(id)
response = request(:get, "#{resource_url}/#{id}")
new(response)
end

def all(params = {})
response = request(:get, resource_url, params)
response.map {|data| new(data)}
end

def belongs_to(resource_name, args={})
class_name = args[:class_name]
unless resource_name && class_name
raise <<~MSG
You must specify the resource name and its class name
for example has_many :accounts, class_name: 'Account'
MSG
end
class_name = "::Fintoc::#{class_name}" unless class_name.include?("Fintoc")
if resource == :method
define_method(resource_name) do |params = {}|
data = request(:get, resource_url, params)
Object.const_get(class_name).new(**data)
end
end

def has_many(resource_name, args={})
class_name = args[:class_name]
unless resource_name && class_name
raise <<~MSG
You must specify the resource name and its class name
for example has_many :accounts, class_name: 'Account'
MSG
end

# Add namespace to class_name
class_name = "::Fintoc::#{class_name}" unless class_name.include?("Fintoc")
define_method(resource_name) do |params = {}|
#link/accounts
fetch_next(
:get,
"#{resource_url}/#{resource_name}", params
).lazy.map do |data|
Object.const_get(class_name).new(**data)
end
end
end
end

def initialize(data={})
@data = @unsaved_data = {}
initialize_from_data(data)
end

def initilize_from_data(data)
klass = self.class

@data = data

data.each_key do |k|
klass.define_method(k) { @data[k]}

klass.define_method(:"#{k}=") do |value|
@data[k] = @unsaved_data[k] = value
end

next unless[FalseClass, TrueClass].include?(
@data[k]
)

klass.define_method(:"#{k}?") do
@data[k]
end
end
self
end

def resource_url
"#{self.resource_url}/#{id}"
end
end
end
7 changes: 7 additions & 0 deletions lib/fintoc/resources.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

require 'fintoc/resources/account'
require 'fintoc/resources/link'
require 'fintoc/resources/institution'
require 'fintoc/resources/balance'
require 'fintoc/resources/movement'
require 'fintoc/resources/transfer_account'
67 changes: 7 additions & 60 deletions lib/fintoc/resources/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,18 @@
require 'fintoc/resources/balance'

module Fintoc
class Account
class Account < Resource
include Utils
attr_reader :id, :name, :holder_name, :currency, :type, :refreshed_at,
:official_name, :number, :holder_id, :balance, :movements
def initialize(
id:,
name:,
official_name:,
number:,
holder_id:,
holder_name:,
type:,
currency:,
refreshed_at: nil,
balance: nil,
movements: nil,
client: nil,
**
)
@id = id
@name = name
@official_name = official_name
@number = number
@holder_id = holder_id
@holder_name = holder_name
@type = type
@currency = currency
@refreshed_at = DateTime.iso8601(refreshed_at) if refreshed_at
@balance = Fintoc::Balance.new(**balance)
@movements = movements || []
@client = client
end

def update_balance
@balance = Fintoc::Balance.new(**get_account[:balance])
end

def get_movements(**params)
_get_movements(**params).lazy.map { |movement| Fintoc::Movement.new(**movement) }
end

def update_movements(**params)
@movements += get_movements(**params).to_a
@movements = @movements.uniq.sort_by(&:post_date)
end
belongs_to :balance, class_name: Fintoc::Balance.to_s
has_many :movements, class_name: Fintoc::Movement.to_s

def show_movements(rows = 5)
puts("This account has #{Utils.pluralize(@movements.size, 'movement')}.")
puts("This account has #{Utils.pluralize(movements.size, 'movement')}.")

return unless @movements.any?
return unless movements.any?

movements = @movements.to_a.slice(0, rows)
movements = movements.to_a.slice(0, rows)
.map.with_index do |mov, index|
[index + 1, mov.amount, mov.currency, mov.description, mov.locale_date]
end
Expand All @@ -65,20 +25,7 @@ def show_movements(rows = 5)
end

def to_s
"💰 #{@holder_name}’s #{@name} #{@balance}"
end

private

def get_account
@client.get.call("accounts/#{@id}")
end

def _get_movements(**params)
first = @client.get.call("accounts/#{@id}/movements", **params)
return first if params.empty?

first + Utils.flatten(@client.fetch_next)
"💰 #{holder_name}’s #{@name} #{@balance}"
end
end
end
Loading