belvo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f2399efd4e273fc2d474b7ef0db1c1b8607021ae7fb1268d4dfb0a94907eb808
4
+ data.tar.gz: 2bf08319b43e36e48a3d25788305c809b582d0688b6c8f1fbb624e4d6c3df6d1
5
+ SHA512:
6
+ metadata.gz: '05288d5fda2ae4caa81434ff96505559be7f8097d8ce3c26fce38cb45fd84071228b5f4e946e145339f0687fd112f69d44f3a0748c9bed03ce3d7ff99843fd43'
7
+ data.tar.gz: 7bd0ca6155a93a57cbc51dce73fbf0dee7600fe1cac3b091d6778c518781b0a309e339d11c5498a37ef3e0d1d87fb946a22be3192678ec8fe38c4ffd660a342e
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Description of the problem
11
+
12
+ ## Expected impact
13
+
14
+ ## Proposed solution
15
+ In this story, we will ...
16
+ This is precisely how we will do it:
17
+
18
+ - ...
@@ -0,0 +1,10 @@
1
+ :question: What
2
+ ---
3
+ <!-- Let us know what did you change in the code -->
4
+
5
+
6
+ :speech_balloon: Why
7
+ ---
8
+ <!-- Are there any arguments that will help to understand these changes? Okay, put'em here... -->
9
+
10
+ <!-- If this PR Relates or Fixes an issue, please also add it -->
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /vendor/
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,26 @@
1
+ require: rubocop-rspec
2
+
3
+ RSpec/ExampleLength:
4
+ Max: 20
5
+
6
+ Metrics/MethodLength:
7
+ Max: 20
8
+
9
+ Metrics/BlockLength:
10
+ Exclude:
11
+ - 'spec/**/*.rb'
12
+
13
+ Lint/RaiseException:
14
+ Enabled: true
15
+
16
+ Lint/StructNewOverride:
17
+ Enabled: true
18
+
19
+ Style/HashEachMethods:
20
+ Enabled: true
21
+
22
+ Style/HashTransformKeys:
23
+ Enabled: true
24
+
25
+ Style/HashTransformValues:
26
+ Enabled: true
@@ -0,0 +1,9 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.5.5
6
+ before_install: gem install bundler -v 2.1.4
7
+ script:
8
+ - bundle exec rubocop
9
+ - bundle exec rspec spec
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at hello@belvo.co. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in belvo.gemspec
6
+ gemspec
7
+
8
+ gem 'coveralls', require: false
9
+ gem 'faraday'
10
+ gem 'faraday_middleware'
11
+ gem 'rake', '~> 12.0'
12
+ gem 'rspec', '~> 3.0'
13
+ gem 'rubocop', '~> 0.81.0', require: false
14
+ gem 'rubocop-rspec', require: false
15
+ gem 'typhoeus'
16
+ gem 'webmock'
@@ -0,0 +1,100 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ belvo (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.7.0)
10
+ public_suffix (>= 2.0.2, < 5.0)
11
+ ast (2.4.0)
12
+ coveralls (0.8.23)
13
+ json (>= 1.8, < 3)
14
+ simplecov (~> 0.16.1)
15
+ term-ansicolor (~> 1.3)
16
+ thor (>= 0.19.4, < 2.0)
17
+ tins (~> 1.6)
18
+ crack (0.4.3)
19
+ safe_yaml (~> 1.0.0)
20
+ diff-lcs (1.3)
21
+ docile (1.3.2)
22
+ ethon (0.12.0)
23
+ ffi (>= 1.3.0)
24
+ faraday (1.0.1)
25
+ multipart-post (>= 1.2, < 3)
26
+ faraday_middleware (1.0.0)
27
+ faraday (~> 1.0)
28
+ ffi (1.12.2)
29
+ hashdiff (1.0.1)
30
+ jaro_winkler (1.5.4)
31
+ json (2.3.0)
32
+ multipart-post (2.1.1)
33
+ parallel (1.19.1)
34
+ parser (2.7.0.5)
35
+ ast (~> 2.4.0)
36
+ public_suffix (4.0.3)
37
+ rainbow (3.0.0)
38
+ rake (12.3.3)
39
+ rexml (3.2.4)
40
+ rspec (3.9.0)
41
+ rspec-core (~> 3.9.0)
42
+ rspec-expectations (~> 3.9.0)
43
+ rspec-mocks (~> 3.9.0)
44
+ rspec-core (3.9.1)
45
+ rspec-support (~> 3.9.1)
46
+ rspec-expectations (3.9.1)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.9.0)
49
+ rspec-mocks (3.9.1)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.9.0)
52
+ rspec-support (3.9.2)
53
+ rubocop (0.81.0)
54
+ jaro_winkler (~> 1.5.1)
55
+ parallel (~> 1.10)
56
+ parser (>= 2.7.0.1)
57
+ rainbow (>= 2.2.2, < 4.0)
58
+ rexml
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (>= 1.4.0, < 2.0)
61
+ rubocop-rspec (1.38.1)
62
+ rubocop (>= 0.68.1)
63
+ ruby-progressbar (1.10.1)
64
+ safe_yaml (1.0.5)
65
+ simplecov (0.16.1)
66
+ docile (~> 1.1)
67
+ json (>= 1.8, < 3)
68
+ simplecov-html (~> 0.10.0)
69
+ simplecov-html (0.10.2)
70
+ sync (0.5.0)
71
+ term-ansicolor (1.7.1)
72
+ tins (~> 1.0)
73
+ thor (1.0.1)
74
+ tins (1.24.1)
75
+ sync
76
+ typhoeus (1.3.1)
77
+ ethon (>= 0.9.0)
78
+ unicode-display_width (1.7.0)
79
+ webmock (3.8.3)
80
+ addressable (>= 2.3.6)
81
+ crack (>= 0.3.2)
82
+ hashdiff (>= 0.4.0, < 2.0.0)
83
+
84
+ PLATFORMS
85
+ ruby
86
+
87
+ DEPENDENCIES
88
+ belvo!
89
+ coveralls
90
+ faraday
91
+ faraday_middleware
92
+ rake (~> 12.0)
93
+ rspec (~> 3.0)
94
+ rubocop (~> 0.81.0)
95
+ rubocop-rspec
96
+ typhoeus
97
+ webmock
98
+
99
+ BUNDLED WITH
100
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Belvo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,66 @@
1
+ <h1 align="center">Belvo Ruby Gem</h1>
2
+ <p align="center">
3
+ <a href="https://travis-ci.com/belvo-finance/belvo-ruby"><img alt="Travis (.com)" src="https://img.shields.io/travis/com/belvo-finance/belvo-ruby/master?style=for-the-badge"></a>
4
+ <a href="https://coveralls.io/github/belvo-finance/belvo-ruby"><img alt="Coveralls github" src="https://img.shields.io/coveralls/github/belvo-finance/belvo-ruby?style=for-the-badge"></a>
5
+ <a href="https://codeclimate.com/github/belvo-finance/belvo-ruby"><img alt="CodeClimate maintainability" src="https://img.shields.io/codeclimate/maintainability/belvo-finance/belvo-ruby?style=for-the-badge"></a>
6
+ </p>
7
+ <p align="center"><a href="https://developers.belvo.co">Developers portal</a> | <a href="https://belvo-finance.github.io/belvo-ruby">Documentation</a></p>
8
+
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'belvo'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle install
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install belvo
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ require 'belvo'
30
+
31
+ belvo = Belvo::Client.new(
32
+ 'af6e69ff-43fa-4e10-8d90-3d217309a1e5',
33
+ 'gdi64m68Lc6xUjIKN3aJF2fZd51wD36lTjGVyJO5xQBfL7PRsgFef-ADXBxIhUnd',
34
+ 'https://sandbox.belvo.co'
35
+ )
36
+
37
+ begin
38
+ new_link = belvo.links.retrieve(
39
+ institution: 'banamex_mx_retail',
40
+ username: 'janedoe',
41
+ password: 'super-secret'
42
+ )
43
+
44
+ belvo.accounts.retrieve(link: new_link['id'])
45
+
46
+ puts belvo.accounts.list
47
+ rescue Belvo::RequestError => e
48
+ puts e.status_code
49
+ puts e.detail
50
+ end
51
+ ```
52
+
53
+ ## Development
54
+
55
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
56
+
57
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
58
+
59
+ ## Contributing
60
+
61
+ Bug reports and pull requests are welcome on GitHub at https://github.com/belvo-finance/belvo-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/belvo-finance/belvo-ruby/blob/master/CODE_OF_CONDUCT.md).
62
+
63
+
64
+ ## Code of Conduct
65
+
66
+ Everyone interacting in the Belvo project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/belvo-finance/belvo-ruby/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/belvo/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'belvo'
7
+ spec.version = Belvo::VERSION
8
+ spec.authors = ['Belvo Finance S.L.']
9
+ spec.email = ['hello@belvo.co']
10
+
11
+ spec.summary = 'The Ruby gem for the Belvo API'
12
+ spec.description = %(Belvo is the leading Open Banking API platform in Latin
13
+ America and the easiest way for users to connect their
14
+ account to an app)
15
+ spec.homepage = 'https://github.com/belvo-finance/belvo-ruby'
16
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/belvo-finance/belvo-ruby'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/belvo-finance/belvo-ruby/blob/master/README.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ f.match(%r{^(test|spec|features)/})
26
+ end
27
+ end
28
+ spec.bindir = 'exe'
29
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ['lib']
31
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'belvo'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'belvo/http'
4
+ require 'belvo/exceptions'
5
+ require 'belvo/resources'
6
+
7
+ module Belvo
8
+ # Allows easy access to Belvo API servers.
9
+ class Client
10
+ # Current Belvo API session
11
+ # @return [APISession]
12
+ attr_reader :session
13
+
14
+ # @param secret_key_id [String]
15
+ # @param secret_key_password [String]
16
+ # @param url [String, nil] API host URL, can be set from the environment
17
+ # using the variable name BELVO_API_URL
18
+ # @return [APISession] Authenticated Belvo API session
19
+ def initialize(secret_key_id, secret_key_password, url = nil)
20
+ (belvo_api_url = url) || ENV['BELVO_API_URL']
21
+ raise BelvoAPIError, 'You need to provide a URL.' if belvo_api_url.nil?
22
+
23
+ @session = Belvo::APISession.new(belvo_api_url)
24
+
25
+ return if @session.login(secret_key_id, secret_key_password)
26
+
27
+ raise BelvoAPIError, 'Login failed.'
28
+ end
29
+
30
+ # Provides access to Links resource
31
+ # @return [Link]
32
+ def links
33
+ @links = Link.new @session
34
+ end
35
+
36
+ # Provides access to Accounts resource
37
+ # @return [Account]
38
+ def accounts
39
+ @accounts = Account.new @session
40
+ end
41
+
42
+ # Provides access to Transactions resource
43
+ # @return [Transaction]
44
+ def transactions
45
+ @transactions = Transaction.new @session
46
+ end
47
+
48
+ # Provides access to Owners resource
49
+ # @return [Owner]
50
+ def owners
51
+ @owners = Owner.new @session
52
+ end
53
+
54
+ # Provides access to Balances resource
55
+ # @return [Balance]
56
+ def balances
57
+ @balances = Balance.new @session
58
+ end
59
+
60
+ # Provides access to Statements resource
61
+ # @return [Statement]
62
+ def statements
63
+ @statements = Statement.new @session
64
+ end
65
+
66
+ # Provides access to Invoices resource
67
+ # @return [Invoice]
68
+ def invoices
69
+ @invoices = Invoice.new @session
70
+ end
71
+
72
+ # Provides access to TaxReturns resource
73
+ # @return [TaxReturn]
74
+ def tax_returns
75
+ @tax_returns = TaxReturn.new @session
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Belvo
4
+ # Belvo API base error
5
+ class BelvoAPIError < StandardError
6
+ # @param message [String] Error short description
7
+ def initialize(message)
8
+ super(message)
9
+ end
10
+ end
11
+
12
+ # Generic request error, any response with status different than 2xx.
13
+ class RequestError < BelvoAPIError
14
+ # HTTP code returned by Belvo API
15
+ # @return [Integer]
16
+ attr_reader :status_code
17
+
18
+ # Error message returned by Belvo API
19
+ # @return [JSON]
20
+ attr_reader :detail
21
+
22
+ # @param message [String] Error short description
23
+ # @param status_code [Integer] HTTP code
24
+ # @param detail [JSON] Detailed error(s) description
25
+ def initialize(message, status_code, detail)
26
+ super(message)
27
+ @status_code = status_code
28
+ @detail = detail
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'typhoeus'
6
+ require 'typhoeus/adapters/faraday'
7
+
8
+ require 'belvo/version'
9
+ require 'belvo/exceptions'
10
+
11
+ module Belvo
12
+ # Describes a Belvo API session
13
+ class APISession
14
+ # @param url [String] Belvo API host url
15
+ # @return [Faraday::Connection]
16
+ def initialize(url)
17
+ @url = format('%<url>s/api/', url: url)
18
+ @session = Faraday::Connection.new(url: @url) do |faraday|
19
+ faraday.adapter :typhoeus
20
+ faraday.response :json
21
+ faraday.headers = {
22
+ 'Content-Type' => 'application/json',
23
+ 'User-Agent' => format(
24
+ 'belvo-ruby (%<version>s)',
25
+ version: Belvo::VERSION
26
+ )
27
+ }
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ # Raise an error if the response code is not 2XX.
34
+ # @param response [Faraday::Response]
35
+ # @return [Faraday::Response]
36
+ def raise_for_status(response)
37
+ unless response.success?
38
+ raise RequestError.new(
39
+ 'Request error',
40
+ response.status,
41
+ response.body.to_s
42
+ )
43
+ end
44
+ response
45
+ end
46
+
47
+ # Set and validate authentication credentials
48
+ # @return [Boolean] True if credentials are valid to authenticate
49
+ # to Belvo API else False.
50
+ def authenticate
51
+ @session.basic_auth(@key_id, @key_password)
52
+ response = @session.get('')
53
+ response.success?
54
+ end
55
+
56
+ # Perform GET request to a Belvo API endpoint. Needed for Detail and List.
57
+ # @param path [String] API Endpoint
58
+ # @param params [Hash] Params to be send as querystring
59
+ # @return [Faraday::Response]
60
+ # @raise [RequestError] If response code is different than 2XX
61
+ def get(path, params: nil)
62
+ params = {} if params.nil?
63
+ response = @session.get(path) do |req|
64
+ req.params = params
65
+ end
66
+ raise_for_status response
67
+ end
68
+
69
+ public
70
+
71
+ # Authenticate to Belvo API using the given credentials.
72
+ # @param secret_key_id [String]
73
+ # @param secret_key_password [String]
74
+ # @return [Boolean] True if credentials are valid to authenticate
75
+ # to Belvo API else False.
76
+ def login(secret_key_id, secret_key_password)
77
+ @key_id = secret_key_id
78
+ @key_password = secret_key_password
79
+
80
+ authenticate
81
+ end
82
+
83
+ # List results from an API endpoint. It will yield all results following
84
+ # pagination's next page, if any.
85
+ # @param path [String] API endpoint
86
+ # @param params [Hash] Params to be send as querystring
87
+ # @yield [Hash] Each result
88
+ # @raise [RequestError] If response code is different than 2XX
89
+ def list(path, params: nil)
90
+ params = {} if params.nil?
91
+ loop do
92
+ response = get(path, params: params)
93
+ response.body['results'].each do |item|
94
+ yield item if block_given?
95
+ end
96
+
97
+ break unless response.body['next']
98
+
99
+ path = response.body['next']
100
+ params = nil
101
+ end
102
+ end
103
+
104
+ # Show specific resource details
105
+ # @param path [String] API endpoint
106
+ # @param id [String] Resource UUID
107
+ # @param params [Hash] Params to be send as querystring
108
+ # @return [Hash] Resource details
109
+ # @raise [RequestError] If response code is different than 2XX
110
+ def detail(path, id, params: nil)
111
+ params = {} if params.nil?
112
+
113
+ resource_path = format('%<path>s%<id>s/', path: path, id: id)
114
+ response = get(resource_path, params)
115
+
116
+ raise_for_status response
117
+ response.body
118
+ end
119
+
120
+ # Perform a POST request to an API endpoint
121
+ # @param path [String] API endpoint
122
+ # @param data [object] JSON parseable object
123
+ # @return [Hash] Response details
124
+ # @raise [RequestError] If response code is different than 2XX
125
+ def post(path, data)
126
+ response = @session.post(path, data.to_json)
127
+ raise_for_status response
128
+ response.body
129
+ end
130
+
131
+ # Perform a PUT request to an specific resource
132
+ # @param path [String] API endpoint
133
+ # @param id [String] Resource UUID
134
+ # @param data [object] JSON parseable object
135
+ # @return [Hash] Response details
136
+ # @raise [RequestError] If response code is different than 2XX
137
+ def put(path, id, data)
138
+ url = format('%<path>s%<id>s/', path: path, id: id)
139
+ response = @session.put(url, data.to_json)
140
+ raise_for_status response
141
+ response.body
142
+ end
143
+
144
+ # Perform a PATCH request to an API endpoint
145
+ # @param path [String] API endpoint
146
+ # @param data [object] JSON parseable object
147
+ # @return [Hash] Response details
148
+ # @raise [RequestError] If response code is different than 2XX
149
+ def patch(path, data)
150
+ response = @session.patch(path, data.to_json)
151
+ raise_for_status response
152
+ response.body
153
+ end
154
+
155
+ # Delete existing resource
156
+ # @param path [String] API endpoint
157
+ # @param id [String] Resource UUID
158
+ # @return [Boolean] true if resource is successfully deleted else false
159
+ def delete(path, id)
160
+ resource_path = format('%<path>s%<id>s/', path: path, id: id)
161
+ response = @session.delete(resource_path)
162
+ response.success?
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday/options'
4
+
5
+ module Belvo
6
+ # @!class LinkOptions < Faraday::Options
7
+ # Contains the configurable properties for a Link
8
+ # @!attribute access_mode [rw] Link access mode (SINGLE or RECURRENT)
9
+ # @!attribute token [rw] OTP token required by the institution
10
+ # @!attribute encryption_key [rw] Custom encryption key
11
+ class LinkOptions < Faraday::Options.new(
12
+ :access_mode,
13
+ :token,
14
+ :encryption_key
15
+ )
16
+ end
17
+
18
+ # @!class AccountOptions < Faraday::Options
19
+ # Contains the configurable properties for an Account
20
+ # @!attribute save_data [rw] Should data be persisted or not.
21
+ # @!attribute token [rw] OTP token required by the institution
22
+ # @!attribute encryption_key [rw] Custom encryption key
23
+ class AccountOptions < Faraday::Options.new(
24
+ :save_data,
25
+ :token,
26
+ :encryption_key
27
+ )
28
+ end
29
+
30
+ # @!class TransactionOptions < Faraday::Options
31
+ # Contains configurable properties for a Transaction
32
+ # @!attribute date_to [rw] Date string (YYYY-MM-DD)
33
+ # @!attribute account [rw] Account ID (UUID)
34
+ # @!attribute save_data [rw] Should data be persisted or not.
35
+ # @!attribute token [rw] OTP token required by the institution
36
+ # @!attribute encryption_key [rw] Custom encryption key
37
+ class TransactionOptions < Faraday::Options.new(
38
+ :date_to,
39
+ :account,
40
+ :token,
41
+ :encryption_key,
42
+ :save_data
43
+ )
44
+ end
45
+
46
+ # @!class OwnerOptions < Faraday::Options
47
+ # Contains configurable properties of an Owner
48
+ # @!attribute save_data [rw] Should data be persisted or not.
49
+ # @!attribute token [rw] OTP token required by the institution
50
+ # @!attribute encryption_key [rw] Custom encryption key
51
+ class OwnerOptions < Faraday::Options.new(:token, :encryption_key, :save_data)
52
+ end
53
+
54
+ # @!class BalanceOptions < Faraday::Options
55
+ # Contains configurable properties of a Balance
56
+ # @!attribute date_to [rw] Date string (YYYY-MM-DD)
57
+ # @!attribute account [rw] Account ID (UUID)
58
+ # @!attribute save_data [rw] Should data be persisted or not.
59
+ # @!attribute token [rw] OTP token required by the institution
60
+ # @!attribute encryption_key [rw] Custom encryption key
61
+ class BalanceOptions < Faraday::Options.new(
62
+ :date_to,
63
+ :account,
64
+ :token,
65
+ :encryption_key,
66
+ :save_data
67
+ )
68
+ end
69
+
70
+ # @!class StatementOptions < Faraday::Options
71
+ # Contains configurable properties of a Statement
72
+ # @!attribute save_data [rw] Should data be persisted or not.
73
+ # @!attribute token [rw] OTP token required by the institution
74
+ # @!attribute encryption_key [rw] Custom encryption key
75
+ # @!attribute attach_pdf [rw] Should the PDF file be included in the
76
+ # response or not.
77
+ class StatementOptions < Faraday::Options.new(
78
+ :token,
79
+ :encryption_key,
80
+ :save_data,
81
+ :attach_pdf
82
+ )
83
+ end
84
+
85
+ # @!class InvoiceOptions < Faraday::Options
86
+ # Contains configurable properties of an Invoice
87
+ # @!attribute save_data [rw] Should data be persisted or not.
88
+ # @!attribute token [rw] OTP token required by the institution
89
+ # @!attribute encryption_key [rw] Custom encryption key
90
+ # @!attribute attach_xml [rw] Should the XML file be included in the
91
+ # response or not.
92
+ class InvoiceOptions < Faraday::Options.new(
93
+ :save_data,
94
+ :token,
95
+ :encryption_key,
96
+ :attach_xml
97
+ )
98
+ end
99
+
100
+ # @!class TaxReturnOptions < Faraday::Options
101
+ # Contains configurable properties of a TaxReturn
102
+ # @!attribute save_data [rw] Should data be persisted or not.
103
+ # @!attribute token [rw] OTP token required by the institution
104
+ # @!attribute encryption_key [rw] Custom encryption key
105
+ # @!attribute attach_pdf [rw] Should the PDF file be included in the
106
+ # response or not.
107
+ class TaxReturnOptions < Faraday::Options.new(
108
+ :token,
109
+ :encryption_key,
110
+ :save_data,
111
+ :attach_pdf
112
+ )
113
+ end
114
+ end
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'belvo/options'
5
+
6
+ module Belvo
7
+ # Represents a consumable REST resource from Belvo API
8
+ class Resource
9
+ # Resource API endpoint
10
+ # @return [String] the API endpoint
11
+ attr_reader :endpoint
12
+
13
+ # @param session [APISession] current Belvo API session
14
+ def initialize(session)
15
+ @session = session
16
+ end
17
+
18
+ # Remove nil values from a request body
19
+ # @param body [Hash] request body
20
+ # @return [Hash] body without nil values
21
+ def clean(body:)
22
+ body.delete_if { |_key, value| value.nil? }
23
+ end
24
+
25
+ # List all results
26
+ # @return [Array]
27
+ # @raise [RequestError] If response code is different than 2XX
28
+ def list
29
+ results = []
30
+ @session.list(@endpoint) { |item| results.push item }
31
+ results
32
+ end
33
+
34
+ # Show specific resource details
35
+ # @param id [String] Resource UUID
36
+ # @return [Hash]
37
+ # @raise [RequestError] If response code is different than 2XX
38
+ def detail(id:)
39
+ @session.detail(@endpoint, id)
40
+ end
41
+
42
+ # Delete existing resource
43
+ # @param id [String] Resource UUID
44
+ # @return [Boolean] true if resource is successfully deleted else false
45
+ def delete(id:)
46
+ @session.delete(@endpoint, id)
47
+ end
48
+
49
+ # Resume data extraction session. Use this method after you have received a
50
+ # HTTP 428 response.
51
+ # @param session_id [String] Session UUID included in the 428 response
52
+ # @param link [String, nil] Link UUID
53
+ # @raise [RequestError] If response code is different than 2XX
54
+ def resume(session_id:, token:, link: nil)
55
+ data = { session: session_id, token: token, link: link }
56
+ @session.patch(@endpoint, data)
57
+ end
58
+ end
59
+
60
+ # A Link is a set of credentials associated to a end-user access
61
+ class Link < Resource
62
+ class AccessMode
63
+ # Use single to do ad hoc one-time requests
64
+ SINGLE = 'single'
65
+ # Use recurrent to have Belvo refresh your link data on a daily basis
66
+ RECURRENT = 'recurrent'
67
+ end
68
+
69
+ def initialize(session)
70
+ super(session)
71
+ @endpoint = 'links/'
72
+ end
73
+
74
+ # Register a new link
75
+ # @param institution [String] Institution name
76
+ # @param username [String] End-user username
77
+ # @param password [String] End-user password
78
+ # @param password2 [String, nil] End-user secondary password, if any
79
+ # @param options [LinkOptions] Configurable properties
80
+ # @return [Hash] created link details
81
+ # @raise [RequestError] If response code is different than 2XX
82
+ def register(
83
+ institution:,
84
+ username:,
85
+ password:,
86
+ password2: nil,
87
+ options: nil
88
+ )
89
+ options = LinkOptions.from(options)
90
+ body = {
91
+ institution: institution,
92
+ username: username,
93
+ password: password,
94
+ password2: password2,
95
+ token: options.token,
96
+ encryption_key: options.encryption_key,
97
+ access_mode: options.access_mode || AccessMode::SINGLE
98
+ }.merge(options)
99
+ body = clean body: body
100
+ @session.post(@endpoint, body)
101
+ end
102
+
103
+ # Allows to change password, password2 or setting a custom encryption key
104
+ # @param id [String] Link UUID
105
+ # @param password [String] End-user password
106
+ # @param password2 [String, nil] End-user secondary password, if any
107
+ # @param options [LinkOptions] Configurable properties
108
+ # @return [Hash] link details
109
+ # @raise [RequestError] If response code is different than 2XX
110
+ def update(id:, password:, password2: nil, options: nil)
111
+ options = LinkOptions.from(options)
112
+ body = {
113
+ password: password,
114
+ password2: password2,
115
+ token: options.token,
116
+ encryption_key: options.encryption_key
117
+ }.merge(options)
118
+ body = clean body: body
119
+ @session.put(@endpoint, id, body)
120
+ end
121
+ end
122
+
123
+ # An Account is the representation of a bank account inside a financial
124
+ # institution.
125
+ class Account < Resource
126
+ def initialize(session)
127
+ super(session)
128
+ @endpoint = 'accounts/'
129
+ end
130
+
131
+ # Retrieve accounts from an existing link
132
+ # @param link [String] Link UUID
133
+ # @param options [AccountOptions] Configurable properties
134
+ # @return [Hash] created link details
135
+ # @raise [RequestError] If response code is different than 2XX
136
+ def retrieve(link:, options: nil)
137
+ options = AccountOptions.from(options)
138
+ body = {
139
+ link: link,
140
+ token: options.token,
141
+ encryption_key: options.encryption_key,
142
+ save_data: options.save_data || true
143
+ }.merge(options)
144
+ body = clean body: body
145
+ @session.post(@endpoint, body)
146
+ end
147
+ end
148
+
149
+ # A Transaction contains the detailed information of each movement inside an
150
+ # Account.
151
+ class Transaction < Resource
152
+ def initialize(session)
153
+ super(session)
154
+ @endpoint = 'transactions/'
155
+ end
156
+
157
+ # Retrieve transactions from an existing link
158
+ # @param link [String] Link UUID
159
+ # @param date_from [String] Date string (YYYY-MM-DD)
160
+ # @param options [TransactionOptions] Configurable properties
161
+ # @return [Hash] created link details
162
+ # @raise [RequestError] If response code is different than 2XX
163
+ def retrieve(link:, date_from:, options: nil)
164
+ options = TransactionOptions.from(options)
165
+ date_to = options.date_to || Date.today.to_s
166
+ body = {
167
+ link: link,
168
+ date_from: date_from,
169
+ date_to: date_to,
170
+ token: options.token,
171
+ account: options.account,
172
+ encryption_key: options.encryption_key,
173
+ save_data: options.save_data || true
174
+ }.merge(options)
175
+ body = clean body: body
176
+ @session.post(@endpoint, body)
177
+ end
178
+ end
179
+
180
+ # An Owner represents the person who has access to a Link and is the owner
181
+ # of all the Accounts inside the Link
182
+ class Owner < Resource
183
+ def initialize(session)
184
+ super(session)
185
+ @endpoint = 'owners/'
186
+ end
187
+
188
+ # Retrieve owners from an existing link
189
+ # @param link [String] Link UUID
190
+ # @param options [OwnerOptions] Configurable properties
191
+ # @return [Hash] created link details
192
+ # @raise [RequestError] If response code is different than 2XX
193
+ def retrieve(link:, options: nil)
194
+ options = OwnerOptions.from(options)
195
+ body = {
196
+ link: link,
197
+ token: options.token,
198
+ encryption_key: options.encryption_key,
199
+ save_data: options.save_data || true
200
+ }.merge(options)
201
+ body = clean body: body
202
+ @session.post(@endpoint, body)
203
+ end
204
+ end
205
+
206
+ # A Balance represents the financial status of an Account at a given time.
207
+ class Balance < Resource
208
+ def initialize(session)
209
+ super(session)
210
+ @endpoint = 'balances/'
211
+ end
212
+
213
+ # Retrieve balances from a specific account or all accounts from a
214
+ # specific link
215
+ # @param link [String] Link UUID
216
+ # @param date_from [String] Date string (YYYY-MM-DD)
217
+ # @param options [BalanceOptions] Configurable properties
218
+ # @return [Hash] created link details
219
+ # @raise [RequestError] If response code is different than 2XX
220
+ def retrieve(link:, date_from:, options: nil)
221
+ options = BalanceOptions.from(options)
222
+ date_to = options.date_to || Date.today.to_s
223
+ body = {
224
+ link: link,
225
+ date_from: date_from,
226
+ date_to: date_to,
227
+ token: options.token,
228
+ account: options.account,
229
+ encryption_key: options.encryption_key,
230
+ save_data: options.save_data || true
231
+ }.merge(options)
232
+ body = clean body: body
233
+ @session.post(@endpoint, body)
234
+ end
235
+ end
236
+
237
+ # A Statement contains a resume of monthly Transactions inside an Account.
238
+ class Statement < Resource
239
+ def initialize(session)
240
+ super(session)
241
+ @endpoint = 'statements/'
242
+ end
243
+
244
+ # Retrieve statements information from a specific banking link.
245
+ # @param link [String] Link UUID
246
+ # @param year [Integer]
247
+ # @param month [Integer]
248
+ # @param options [StatementOptions] Configurable properties
249
+ # @return [Hash] created link details
250
+ # @raise [RequestError] If response code is different than 2XX
251
+ def retrieve(link:, account:, year:, month:, options: nil)
252
+ options = StatementOptions.from(options)
253
+ body = {
254
+ link: link,
255
+ account: account,
256
+ year: year,
257
+ month: month,
258
+ token: options.token,
259
+ encryption_key: options.encryption_key,
260
+ save_data: options.save_data || true,
261
+ attach_pdf: options.attach_pdf
262
+ }.merge(options)
263
+ body = clean body: body
264
+ @session.post(@endpoint, body)
265
+ end
266
+ end
267
+
268
+ # An Invoice is the representation of an electronic invoice, that can be
269
+ # received or sent, by a business or an individual and has been uploaded
270
+ # to the fiscal institution website
271
+ class Invoice < Resource
272
+ def initialize(session)
273
+ super(session)
274
+ @endpoint = 'invoices/'
275
+ end
276
+
277
+ # @param link [String] Link UUID
278
+ # @param date_from [String] Date string (YYYY-MM-DD)
279
+ # @param date_to [String] Date string (YYYY-MM-DD)
280
+ # @param options [InvoiceOptions] Configurable properties
281
+ # @return [Hash] created link details
282
+ # @raise [RequestError] If response code is different than 2XX
283
+ def retrieve(link:, date_from:, date_to:, type:, options: nil)
284
+ options = InvoiceOptions.from(options)
285
+ body = {
286
+ link: link,
287
+ date_from: date_from,
288
+ date_to: date_to,
289
+ type: type,
290
+ token: options.token,
291
+ encryption_key: options.encryption_key,
292
+ save_data: options.save_data || true,
293
+ attach_xml: options.attach_xml
294
+ }.merge(options)
295
+ body = clean body: body
296
+ @session.post(@endpoint, body)
297
+ end
298
+ end
299
+
300
+ # A Tax return is the representation of the tax return document sent every
301
+ # year by a person or a business to the tax authority in the country.
302
+ class TaxReturn < Resource
303
+ def initialize(session)
304
+ super(session)
305
+ @endpoint = 'tax-returns/'
306
+ end
307
+
308
+ # Retrieve tax returns information from a specific fiscal link.
309
+ # @param link [String] Link UUID
310
+ # @param year_from [Integer]
311
+ # @param year_to [Integer]
312
+ # @param options [TaxReturnOptions] Configurable properties
313
+ # @return [Hash] created link details
314
+ # @raise [RequestError] If response code is different than 2XX
315
+ def retrieve(link:, year_from:, year_to:, options: nil)
316
+ options = TaxReturnOptions.from(options)
317
+ body = {
318
+ link: link,
319
+ year_from: year_from,
320
+ year_to: year_to,
321
+ token: options.token,
322
+ encryption_key: options.encryption_key,
323
+ save_data: options.save_data || true,
324
+ attach_pdf: options.attach_pdf
325
+ }.merge(options)
326
+ body = clean body: body
327
+ @session.post(@endpoint, body)
328
+ end
329
+
330
+ def resume(_session_id, _token, _link: nil)
331
+ raise NotImplementedError 'TaxReturn does not support resuming a session.'
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Belvo
4
+ # belvo-ruby current version
5
+ VERSION = '0.1.0'
6
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: belvo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Belvo Finance S.L.
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-04-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ Belvo is the leading Open Banking API platform in Latin
15
+ America and the easiest way for users to connect their
16
+ account to an app
17
+ email:
18
+ - hello@belvo.co
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
24
+ - ".github/pull_request_template.md"
25
+ - ".gitignore"
26
+ - ".rspec"
27
+ - ".rubocop.yml"
28
+ - ".travis.yml"
29
+ - CODE_OF_CONDUCT.md
30
+ - Gemfile
31
+ - Gemfile.lock
32
+ - LICENSE
33
+ - README.md
34
+ - Rakefile
35
+ - belvo.gemspec
36
+ - bin/console
37
+ - bin/setup
38
+ - lib/belvo.rb
39
+ - lib/belvo/exceptions.rb
40
+ - lib/belvo/http.rb
41
+ - lib/belvo/options.rb
42
+ - lib/belvo/resources.rb
43
+ - lib/belvo/version.rb
44
+ homepage: https://github.com/belvo-finance/belvo-ruby
45
+ licenses: []
46
+ metadata:
47
+ homepage_uri: https://github.com/belvo-finance/belvo-ruby
48
+ source_code_uri: https://github.com/belvo-finance/belvo-ruby
49
+ changelog_uri: https://github.com/belvo-finance/belvo-ruby/blob/master/README.md
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.3.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.1.2
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: The Ruby gem for the Belvo API
69
+ test_files: []