blnk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05e768b805f7c2e8f061d334fd923afcf82fcc3945bf0c5a3ac844815403fd3f
4
- data.tar.gz: 2edddcd8e0227f1183f05d488a7aec58b22cdef9d157e4479fb7e8ca9026aad7
3
+ metadata.gz: 5c5fd560be10539e1da9ac8d3d47685a4070b2723432ef1b2580ae5e57760a82
4
+ data.tar.gz: d90db62cdd4fbea9c633cdbe579df1e3242188b520b9e4de17e095b201a598fc
5
5
  SHA512:
6
- metadata.gz: d8084904b5d4210b9c4e0c8b5a7f7bc8d107fca56ff3669e0d01376cbe5360a644e52b490c677b86baa9672ddb04ba417f4fc4c5ab6c7a6170f381d133558b3b
7
- data.tar.gz: 54bf131a6080c1af282d581c24395cf99ec28c7a26fab84cc09c77d1337f19e1be5600907454470edff6017ac6dd3d1a7b4234da8def6c3700ecd527feee0370
6
+ metadata.gz: f8e50a2169fe30914d20bd8bab234202900e23d336215c93e643edb34ced8a0565996aa7fe873b10a90862ed5b17e1915191b92367bc616c6ff42560b63acd81
7
+ data.tar.gz: 81c7c8d09fcaf7312e3fb256a5d7a991c59e0c1355141ff8487df32544a40716a11f33ea72909c964a1a3fb437d2aaf6510ef7ededf6481b8c4cc75917dafbb6
data/README.md CHANGED
@@ -1,5 +1,3 @@
1
- # This gem is under development, don't use in production
2
-
3
1
  # Blnk
4
2
 
5
3
  Easy way to use the blnkfinance.com API in ruby
@@ -14,23 +12,40 @@ If bundler is not being used to manage dependencies, install the gem by executin
14
12
 
15
13
  $ gem install blnk
16
14
 
17
- ## Usage
15
+ ## TODO
18
16
 
19
- TODO:
20
17
  - [x] Create Ledger
21
18
  - [x] Find Ledger
22
19
  - [x] List Ledgers
23
- - [ ] Search Ledgers
20
+ - [x] Search Ledgers
24
21
  - [x] Create Balances
25
22
  - [x] Find Balance
26
- - [ ] Search Balances
27
- - [ ] Create Transaction
28
- - [ ] Find Transaction
29
- - [ ] Search Transactions
23
+ - [x] Search Balances
24
+ - [x] Create Transaction
25
+ - [x] Multiple sources / destinations Transaction
26
+ - [x] Refund Transaction
27
+ - [x] Commit inflight Transaction
28
+ - [x] Void inflight Transaction
29
+ - [x] Find Transaction
30
+ - [x] Search Transactions
31
+ - [ ] Handler notifications
32
+ - [ ] Create Balance Monitor
33
+ - [ ] Find Balance Monitor
34
+ - [ ] Update Balance Monitor
35
+ - [ ] Backup endpoint
36
+ - [ ] Backup to S3 endpoint
37
+ - [ ] Add Search Contract schema for each resource using they own search attributes
38
+ - [ ] Search Result should convert document resul into resource class
39
+ - [x] Use Dry Monads to get success / failure output
40
+ - [x] Use Dry Validation to validate inputs
41
+ - [ ] Use Dry Schema instead OpenStruct to handle with resource attributes
42
+ - [ ] Use Dry Configuration to better config DSL
30
43
 
31
44
  ## Usage
32
45
 
33
46
  ```ruby
47
+ transaction = Blnk::Transaction.find 'transaction_id'
48
+
34
49
  require 'blnk'
35
50
 
36
51
  # client config
@@ -39,28 +54,40 @@ Blnk.address = '192.168.2.7:5001'
39
54
  Blnk.secret_token = 'your_strong_secret_key'
40
55
  Blnk.search_api_key = Blnk.secret_token
41
56
 
42
- # ledgers integration
57
+ # Ledgers
43
58
 
44
59
  ledger = Blnk::Ledger.create(name: 'foobar')
45
60
  ledger = Blnk::Ledger.find 'ledger_id'
46
61
  ledgers = Blnk::Ledger.all
47
62
 
48
- # search not implemented yet
49
- ledgers = Blnk::Ledger.search(
50
- q: 'USD',
51
- filter_by: 'balances > 1400',
52
- sort_by: 'created_at:desc',
53
- page: 1,
54
- per_page: 50
55
- )
56
-
63
+ ledgers = Blnk::Ledger.search(q: '*')
57
64
 
58
- # Balance integrations
65
+ # Balances
59
66
  balance = Blnk::Balance.find 'balance_id'
60
67
  balance = Blnk::Balance.create(ledger_id: 'ledger_id', currency: 'USD')
61
68
 
69
+ balances = Blnk::Balance.search(q: '*')
70
+
71
+ # Transactions
72
+ transaction = Blnk::Transaction.find 'transaction_id'
73
+ transaction = Blnk::Transaction.create(
74
+ amount: 75,
75
+ reference: 'ref_005',
76
+ currency: 'BRLX',
77
+ precision: 100,
78
+ source: '@world',
79
+ destination: 'bln_469f93bc-40e9-4e0e-b6ab-d11c3638c15d',
80
+ description: 'For fees',
81
+ allow_overdraft: true
82
+ )
83
+
84
+ transaction = Blnk::Transaction.search q: '*'
62
85
  ```
63
86
 
87
+ ## Result
88
+
89
+ All methods return a Dry::Monad::Result, so you can use ```.failure?``` to check if method was executed and returned a failure (can be validation or a server error). ```.success?``` to check if method was executed with successful and access the data from the failure or successful result using ```.value!```. You can check on the dry-monad gem to see other options on Result.
90
+
64
91
 
65
92
  ## Development
66
93
 
data/lib/blnk/balance.rb CHANGED
@@ -3,9 +3,15 @@
3
3
  module Blnk
4
4
  # Balance representation
5
5
  class Balance < Resourceable
6
- def self.resource_name = :balances
6
+ class CreateContract < Dry::Validation::Contract
7
+ schema do
8
+ required(:ledger_id).value(:string)
9
+ required(:currency).value(:string)
10
+ end
11
+ end
7
12
 
8
- def persisted? = !balance_id.nil?
9
- def body_data = { ledger_id:, currency: }
13
+ self.resource_name = :balances
14
+ self.id_field = :balance_id
15
+ self.create_contract = CreateContract
10
16
  end
11
17
  end
data/lib/blnk/ledger.rb CHANGED
@@ -3,9 +3,15 @@
3
3
  module Blnk
4
4
  # Ledger representation
5
5
  class Ledger < Resourceable
6
- def self.resource_name = :ledgers
6
+ class CreateContract < Dry::Validation::Contract
7
+ schema do
8
+ required(:name).value(:string)
9
+ optional(:meta_data).value(:hash)
10
+ end
11
+ end
7
12
 
8
- def persisted? = !ledger_id.nil?
9
- def body_data = { name:, meta_data: }
13
+ self.resource_name = :ledgers
14
+ self.id_field = :ledger_id
15
+ self.create_contract = CreateContract
10
16
  end
11
17
  end
@@ -3,37 +3,127 @@
3
3
  module Blnk
4
4
  # Resoureable module that bring some tweaks for basic REST api integration
5
5
  class Resourceable < OpenStruct
6
- include Client
6
+ extend Client
7
+ extend Forwardable
7
8
 
8
- def self.resource_name = raise NotImplementedError
9
+ def_delegators :'self.class', :put_request, :post_request, :get_request, :with_handler
9
10
 
10
- def self.find(id)
11
- response = new.get_request(path: "/#{resource_name}/#{id}")
12
- return response unless response.status.success?
11
+ class SearchResult < OpenStruct; end
13
12
 
14
- new response.parse
13
+ class DefaultSearchContract < Dry::Validation::Contract
14
+ schema do
15
+ required(:q).value(:string)
16
+ end
15
17
  end
16
18
 
17
- def self.all
18
- response = new.get_request(path: "/#{resource_name}")
19
- return response unless response.status.success?
19
+ class << self
20
+ include Dry::Monads[:result]
21
+
22
+ attr_accessor :resource_name, :id_field, :create_contract, :search_contract
23
+
24
+ def resources_path = "/#{resource_name}"
25
+ def resource_path(id) = "/#{resource_name}/#{id}"
26
+ def search_path = "/search/#{resource_name}"
27
+
28
+ def find(id) = with_handler resp: find_request(id)
29
+ def find_request(id) = get_request(path: resource_path(id))
30
+
31
+ def all
32
+ check_vars
33
+ resp = get_request(path: resources_path)
34
+ with_handler(resp:, block: method(:all_handler))
35
+ end
36
+
37
+ def create(**args)
38
+ wrap_call(create_contract_new, args) do |contract|
39
+ with_handler(resp: post_request(path: resources_path, body: contract.to_h))
40
+ end
41
+ end
42
+
43
+ def search(**args)
44
+ wrap_call(search_contract_new, args) do |contract|
45
+ with_handler(resp: post_request(path: search_path, body: contract.to_h),
46
+ block: method(:search_handler))
47
+ end
48
+ end
49
+
50
+ def check_vars
51
+ raise NotImplementedError, 'missing self.resource_name' unless resource_name
52
+ raise NotImplementedError, 'missing self.id_field' unless id_field
53
+ raise NotImplementedError, 'missing self.create_contract' unless create_contract
54
+ end
55
+
56
+ def wrap_call(contract, args)
57
+ check_vars
58
+ ccall = contract.call(args)
59
+ return Failure(ccall.errors.to_h) if ccall.failure?
60
+
61
+ return yield ccall if block_given?
62
+
63
+ ccall
64
+ end
65
+
66
+ def create_contract_new
67
+ return create_contract.new if create_contract
20
68
 
21
- response.parse.map do |r|
22
- new r
69
+ raise NotImplementedError, 'missing self.create_contract'
70
+ end
71
+
72
+ def search_contract_new = (search_contract || DefaultSearchContract).new
73
+
74
+ def search_handler(parsed, status)
75
+ success = status.success?
76
+ result = SearchResult.new(parsed) if success
77
+
78
+ inj_handler(result:, success:, error: parsed)
79
+ end
80
+
81
+ def all_handler(parsed, status)
82
+ success = status.success?
83
+ result = parsed.map { |r| new(r) } if success
84
+ inj_handler(success:, result:, error: parsed)
85
+ end
86
+
87
+ def handler(parsed, status)
88
+ inj_handler(
89
+ result: new(parsed),
90
+ error: parsed,
91
+ success: status.success?
92
+ )
93
+ end
94
+
95
+ def inj_handler(result:, error:, success:)
96
+ (success ? Success(result) : Failure(error))
97
+ end
98
+
99
+ def with_handler(resp:, kself: nil, block: method(:handler))
100
+ using_resp(resp:, kself:, &block)
101
+ end
102
+
103
+ def using_resp(resp:, kself: nil, &block)
104
+ check_vars
105
+ parsed = resp.parse
106
+ parsed = parsed.transform_keys(&:to_sym) unless parsed.is_a?(Array)
107
+
108
+ kself&.reload
109
+
110
+ block.call(parsed, resp.status)
23
111
  end
24
112
  end
25
113
 
26
- def self.create(*)
27
- response = new.post_request(
28
- path: "/#{resource_name}",
29
- body: new(*).body_data
30
- )
31
- return response unless response.status.success?
114
+ def reload
115
+ self.class.find_request(_id).tap do |res|
116
+ next unless res.status.success?
32
117
 
33
- new(response.parse)
118
+ res.parse.each_pair do |k, v|
119
+ self[k] = v
120
+ end
121
+ end
122
+ self
34
123
  end
35
124
 
36
- def persisted? = raise NotImplementedError
37
- def body_data = raise NotImplementedError
125
+ # table[self.class.id_field]
126
+ def persisted? = !_id.nil?
127
+ def _id = public_send(self.class.id_field)
38
128
  end
39
129
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blnk
4
+ # Transaction representation
5
+ class Transaction < Resourceable
6
+ class CreateContract < Dry::Validation::Contract
7
+ schema do
8
+ required(:amount).value(:integer)
9
+ required(:precision).value(:integer)
10
+ required(:currency).value(:string)
11
+ required(:reference).value(:string)
12
+ optional(:source).value(:string)
13
+ optional(:sources).array(:hash) do
14
+ required(:identifier).value(:string)
15
+ required(:distribution).value(:string)
16
+ required(:narration).value(:string)
17
+ end
18
+ optional(:destination).value(:string)
19
+ optional(:destinations).array(:hash) do
20
+ required(:identifier).value(:string)
21
+ required(:distribution).value(:string)
22
+ required(:narration).value(:string)
23
+ end
24
+ required(:description).value(:string)
25
+ required(:allow_overdraft).value(:bool)
26
+ optional(:inflight).value(:bool)
27
+ optional(:rate).value(:integer)
28
+ optional(:scheduled_for).value(:string)
29
+ end
30
+
31
+ rule do
32
+ base.failure('must only contain one of source, sources') if key?(:source) && key?(:sources)
33
+
34
+ if key?(:destination) && key?(:destinations)
35
+ base.failure('must only contain one of destination, destinations')
36
+ end
37
+
38
+ if values[:source].to_s.empty? && values[:sources].to_s.empty?
39
+ key(:source).failure('missing source')
40
+ end
41
+
42
+ if values[:destination].to_s.empty? && values[:destinations].to_s.empty?
43
+ key(:destination).failure('missing destination')
44
+ end
45
+ end
46
+ end
47
+
48
+ self.resource_name = :transactions
49
+ self.id_field = :transaction_id
50
+ self.create_contract = CreateContract
51
+
52
+ def refund = short_hander(resp: req_refund)
53
+ def void = short_hander(resp: req_inflight(body: { status: 'void' }))
54
+ def commit = short_hander(resp: req_inflight(body: { status: 'commit' }))
55
+
56
+ private
57
+
58
+ def short_hander(resp:) = with_handler(resp:, kself: self)
59
+ def inflight_path = "/transactions/inflight/#{_id}"
60
+ def refund_path = "/refund-transaction/#{_id}"
61
+ def req_inflight(body:) = put_request(path: inflight_path, body:)
62
+ def req_refund = post_request(path: refund_path, body: nil)
63
+ end
64
+ end
data/lib/blnk/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Blnk
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.3'
5
5
  end
data/lib/blnk.rb CHANGED
@@ -2,11 +2,14 @@
2
2
 
3
3
  require 'http'
4
4
  require 'ostruct'
5
+ require 'dry-validation'
6
+ require 'dry/monads'
5
7
  require_relative 'blnk/version'
6
8
  require_relative 'blnk/client'
7
9
  require_relative 'blnk/resourceable'
8
10
  require_relative 'blnk/ledger'
9
11
  require_relative 'blnk/balance'
12
+ require_relative 'blnk/transaction'
10
13
 
11
14
  module Blnk
12
15
  class Error < StandardError; end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blnk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antonio Roberto Silva
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-27 00:00:00.000000000 Z
11
+ date: 2024-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-configurable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-monads
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-validation
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.10.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.10.0
13
55
  - !ruby/object:Gem::Dependency
14
56
  name: http
15
57
  requirement: !ruby/object:Gem::Requirement
@@ -41,6 +83,7 @@ files:
41
83
  - lib/blnk/client.rb
42
84
  - lib/blnk/ledger.rb
43
85
  - lib/blnk/resourceable.rb
86
+ - lib/blnk/transaction.rb
44
87
  - lib/blnk/version.rb
45
88
  - sig/blnk.rbs
46
89
  homepage: https://blnkledger.com/