truelayer 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9433f24480b37dbca16d0b284c2e4903c6f5a7bc0e2f02da44a469f33248b86a
4
+ data.tar.gz: 287b0ac98b7739d47ad1f29d585c83c440d77af8d7e043cbb296552bca43215a
5
+ SHA512:
6
+ metadata.gz: f66a064c262b63f17d42f6d82a76179ba1c5a716cd14d9fe70f52d49afe9a74ae70d38ca318107fc7aff4f35634f1c8e54d9530f696afe18e3a997d8dcf2c437
7
+ data.tar.gz: 590aacbe0052bff0f50584789131e9891967796cdb55e4c1e60bee12dbe9fb31a29c6f95d034407d360ade1430a59a9f75b4118ba43e4272e4700c9102133e0f
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /.rspec_status
10
+ /.ruby-gemset
data/.rubocop.yml ADDED
@@ -0,0 +1,96 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.6
5
+ NewCops: enable
6
+ Exclude:
7
+ - 'db/**/*'
8
+ - 'ansible/**/*'
9
+ - 'vendor/**/*'
10
+ - 'bin/**/*'
11
+ - 'node_modules/**/*'
12
+
13
+ # Don't force top level comments in every class
14
+ Style/Documentation:
15
+ Enabled: false
16
+
17
+ # A good line length is 100 chars, comments are ignored
18
+ Layout/LineLength:
19
+ Max: 100
20
+ AllowURI: true
21
+ IgnoredPatterns: ['\A(\s+)?#']
22
+
23
+ Metrics/BlockLength:
24
+ Enabled: false
25
+
26
+ Metrics/ClassLength:
27
+ Max: 300
28
+
29
+ Metrics/MethodLength:
30
+ Max: 20
31
+
32
+ Metrics/AbcSize:
33
+ Max: 30
34
+
35
+ RSpec/ExampleLength:
36
+ Enabled: false
37
+
38
+ RSpec/MultipleExpectations:
39
+ Max: 10
40
+
41
+ RSpec/LetSetup:
42
+ Enabled: false
43
+
44
+ RSpec/MessageSpies:
45
+ Enabled: false
46
+
47
+ RSpec/RepeatedExample:
48
+ Exclude:
49
+ - 'spec/policies/**/*'
50
+
51
+ RSpec/RepeatedDescription:
52
+ Exclude:
53
+ - 'spec/policies/**/*'
54
+
55
+ Layout/EmptyLinesAroundAttributeAccessor:
56
+ Enabled: true
57
+
58
+ Layout/SpaceAroundMethodCallOperator:
59
+ Enabled: true
60
+
61
+ Lint/DeprecatedOpenSSLConstant:
62
+ Enabled: true
63
+
64
+ Lint/RaiseException:
65
+ Enabled: true
66
+
67
+ Lint/StructNewOverride:
68
+ Enabled: true
69
+
70
+ Style/ExponentialNotation:
71
+ Enabled: true
72
+
73
+ Style/HashEachMethods:
74
+ Enabled: true
75
+
76
+ Style/HashTransformKeys:
77
+ Enabled: true
78
+
79
+ Style/HashTransformValues:
80
+ Enabled: true
81
+
82
+ Style/SlicingWithRange:
83
+ Enabled: true
84
+
85
+ # TODO: we should rename our fields from line_1, address_line_2, etc
86
+ Naming/VariableNumber:
87
+ Enabled: false
88
+
89
+ Metrics/CyclomaticComplexity:
90
+ Max: 9
91
+
92
+ Metrics/PerceivedComplexity:
93
+ Max: 10
94
+
95
+ RSpec/MultipleMemoizedHelpers:
96
+ Max: 8
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.6
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in truelayer.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,94 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ truelayer (0.1.0)
5
+ faraday
6
+ faraday_middleware
7
+ faraday_middleware-multi_json
8
+ multi_json
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ addressable (2.7.0)
14
+ public_suffix (>= 2.0.2, < 5.0)
15
+ coderay (1.1.3)
16
+ crack (0.4.5)
17
+ rexml
18
+ diff-lcs (1.4.4)
19
+ dotenv (2.7.6)
20
+ faraday (1.10.0)
21
+ faraday-em_http (~> 1.0)
22
+ faraday-em_synchrony (~> 1.0)
23
+ faraday-excon (~> 1.1)
24
+ faraday-httpclient (~> 1.0)
25
+ faraday-multipart (~> 1.0)
26
+ faraday-net_http (~> 1.0)
27
+ faraday-net_http_persistent (~> 1.0)
28
+ faraday-patron (~> 1.0)
29
+ faraday-rack (~> 1.0)
30
+ faraday-retry (~> 1.0)
31
+ ruby2_keywords (>= 0.0.4)
32
+ faraday-em_http (1.0.0)
33
+ faraday-em_synchrony (1.0.0)
34
+ faraday-excon (1.1.0)
35
+ faraday-httpclient (1.0.1)
36
+ faraday-multipart (1.0.3)
37
+ multipart-post (>= 1.2, < 3)
38
+ faraday-net_http (1.0.1)
39
+ faraday-net_http_persistent (1.2.0)
40
+ faraday-patron (1.0.0)
41
+ faraday-rack (1.0.0)
42
+ faraday-retry (1.0.3)
43
+ faraday_middleware (1.2.0)
44
+ faraday (~> 1.0)
45
+ faraday_middleware-multi_json (0.0.6)
46
+ faraday_middleware
47
+ multi_json
48
+ hashdiff (1.0.1)
49
+ method_source (1.0.0)
50
+ multi_json (1.15.0)
51
+ multipart-post (2.1.1)
52
+ pry (0.13.1)
53
+ coderay (~> 1.1)
54
+ method_source (~> 1.0)
55
+ public_suffix (4.0.6)
56
+ rake (13.0.3)
57
+ rexml (3.2.4)
58
+ rspec (3.10.0)
59
+ rspec-core (~> 3.10.0)
60
+ rspec-expectations (~> 3.10.0)
61
+ rspec-mocks (~> 3.10.0)
62
+ rspec-core (3.10.1)
63
+ rspec-support (~> 3.10.0)
64
+ rspec-expectations (3.10.1)
65
+ diff-lcs (>= 1.2.0, < 2.0)
66
+ rspec-support (~> 3.10.0)
67
+ rspec-mocks (3.10.2)
68
+ diff-lcs (>= 1.2.0, < 2.0)
69
+ rspec-support (~> 3.10.0)
70
+ rspec-support (3.10.2)
71
+ ruby2_keywords (0.0.5)
72
+ vcr (6.0.0)
73
+ webmock (3.12.1)
74
+ addressable (>= 2.3.6)
75
+ crack (>= 0.3.2)
76
+ hashdiff (>= 0.4.0, < 2.0.0)
77
+
78
+ PLATFORMS
79
+ -darwin-21
80
+ x86_64-darwin-19
81
+ x86_64-linux
82
+
83
+ DEPENDENCIES
84
+ bundler (~> 2.2)
85
+ dotenv
86
+ pry
87
+ rake
88
+ rspec
89
+ truelayer!
90
+ vcr
91
+ webmock
92
+
93
+ BUNDLED WITH
94
+ 2.3.10
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Truelayer
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/truelayer`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'truelayer'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install truelayer
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/truelayer.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'truelayer'
5
+ require 'json'
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
data/bin/setup ADDED
@@ -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,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'truelayer/base_model'
5
+ require 'truelayer/account_number'
6
+
7
+ module Truelayer
8
+ class Account < BaseModel
9
+ attributes :update_timestamp, :account_id, :account_type, :display_name,
10
+ :currency, :account_number, :provider, :provider_id
11
+
12
+ def self.build(json:)
13
+ super.tap do |record|
14
+ record.account_number = AccountNumber.build(json: (record.account_number || {}))
15
+ record.update_timestamp = Time.parse(record.update_timestamp) if record.update_timestamp
16
+ record.provider_id = record.provider[:provider_id] if record.provider
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class AccountNumber < BaseModel
7
+ attributes :iban, :number, :sort_code, :swift_bic
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_repository'
4
+ require 'truelayer/response'
5
+ require 'truelayer/account'
6
+ require 'truelayer/balance'
7
+ require 'truelayer/transaction'
8
+
9
+ module Truelayer
10
+ class AccountsRepository < BaseRepository
11
+ def all_accounts(webhook_uri: nil)
12
+ response = get('/data/v1/accounts', params: async_params(webhook_uri))
13
+
14
+ Response.build_with_results(json: response.body, results_class: Account)
15
+ end
16
+
17
+ def find_account(account_id, webhook_uri: nil)
18
+ url = format_url('/data/v1/accounts/:account_id', account_id: account_id)
19
+ response = get(url, params: async_params(webhook_uri))
20
+
21
+ Response.build_with_results(json: response.body, results_class: Account)
22
+ end
23
+
24
+ def current_balance(account_id, webhook_uri: nil)
25
+ url = format_url('/data/v1/accounts/:account_id/balance', account_id: account_id)
26
+ response = get(url, params: async_params(webhook_uri))
27
+
28
+ Response.build_with_results(json: response.body, results_class: Balance)
29
+ end
30
+
31
+ def transactions(account_id, from: nil, to: nil, webhook_uri: nil)
32
+ url = format_url('/data/v1/accounts/:account_id/transactions', account_id: account_id)
33
+ response = get(url, params: { from: from, to: to }.merge(async_params(webhook_uri)))
34
+
35
+ Response.build_with_results(json: response.body, results_class: Transaction)
36
+ end
37
+
38
+ private
39
+
40
+ def async_params(webhook_uri)
41
+ {}.tap do |params|
42
+ if webhook_uri
43
+ params[:async] = true
44
+ params[:webhook_uri] = webhook_uri
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_repository'
4
+ require 'truelayer/response'
5
+
6
+ module Truelayer
7
+ class AsyncRepository < BaseRepository
8
+ def refresh(task_id, results_class)
9
+ response = get(format_url('/data/v1/results/:task_id', task_id: task_id))
10
+
11
+ Response.build_with_results(json: response.body, results_class: results_class)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class AuthLink < BaseModel
7
+ attributes :result, :success
8
+ end
9
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware/multi_json'
5
+ require 'truelayer/configuration'
6
+ require 'truelayer/errors'
7
+ require 'truelayer/version'
8
+ require 'truelayer/token'
9
+ require 'truelayer/auth_link'
10
+ require 'truelayer/provider'
11
+
12
+ module Truelayer
13
+ class AuthenticationRepository
14
+ def generate_direct_bank_link(provider_id:, consent_id:, redirect_uri:, scopes:, state: nil, tracking_id: nil, data_use_description: nil)
15
+ params = {
16
+ response_type: 'code',
17
+ client_id: Truelayer.configuration.client_id,
18
+ provider_id: provider_id,
19
+ redirect_uri: redirect_uri,
20
+ scope: scopes,
21
+ consent_id: consent_id
22
+ }
23
+
24
+ params[:state] = state if state
25
+ params[:tracking_id] = tracking_id if tracking_id
26
+ params[:data_use_description] = data_use_description if data_use_description
27
+
28
+ result = post('/v1/authuri', params: nil) do |req|
29
+ req.headers[:content_type] = 'application/json'
30
+ req.body = MultiJson.dump(params)
31
+ end
32
+
33
+ AuthLink.build(json: result.body)
34
+ end
35
+
36
+ def exchange_code(code, redirect_uri)
37
+ params = {
38
+ grant_type: 'authorization_code',
39
+ client_id: Truelayer.configuration.client_id,
40
+ client_secret: Truelayer.configuration.client_secret,
41
+ redirect_uri: redirect_uri,
42
+ code: code
43
+ }
44
+
45
+ result = post('/connect/token', params: params)
46
+
47
+ Token.build(json: result.body)
48
+ end
49
+
50
+ def refresh_token(refresh_token)
51
+ params = {
52
+ grant_type: 'refresh_token',
53
+ client_id: Truelayer.configuration.client_id,
54
+ client_secret: Truelayer.configuration.client_secret,
55
+ refresh_token: refresh_token
56
+ }
57
+
58
+ result = post('/connect/token', params: params)
59
+
60
+ Token.build(json: result.body)
61
+ end
62
+
63
+ def providers
64
+ response = get('/api/providers', params: { clientId: Truelayer.configuration.client_id })
65
+
66
+ (response.body || []).map do |result|
67
+ Provider.build(json: result)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def get(path, params:)
74
+ response = connection.get(path, params)
75
+
76
+ return response if response.success?
77
+
78
+ raise GenericError, "#{response.status}: #{response.body}"
79
+ end
80
+
81
+ def post(path, params:, &block)
82
+ response = connection.post(path, params, &block)
83
+
84
+ return response if response.success?
85
+
86
+ if response.status == 400
87
+ raise InvalidRequestError, response.body
88
+ else
89
+ raise GenericError, "#{response.status}: #{response.body}"
90
+ end
91
+ end
92
+
93
+ def connection
94
+ auth_url = Truelayer.configuration.auth_url
95
+
96
+ @connection ||= Faraday.new(url: auth_url) do |conn|
97
+ conn.request :url_encoded
98
+
99
+ conn.headers.merge!(
100
+ user_agent: "finpoint/#{Truelayer::VERSION}"
101
+ )
102
+ conn.response :multi_json, symbolize_keys: true
103
+ if Truelayer.configuration.debug
104
+ conn.response :logger, nil, { headers: true, bodies: Truelayer.configuration.verbose }
105
+ end
106
+ conn.adapter Faraday.default_adapter
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'truelayer/base_model'
5
+
6
+ module Truelayer
7
+ class Balance < BaseModel
8
+ attributes :currency, :available, :current, :overdraft, :update_timestamp
9
+
10
+ def self.build(json:)
11
+ super.tap do |record|
12
+ record.update_timestamp = Time.parse(record.update_timestamp) if record.update_timestamp
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truelayer
4
+ class BaseModel
5
+ class << self
6
+ def attributes(*attributes)
7
+ @attributes ||= []
8
+
9
+ return @attributes unless attributes
10
+
11
+ attr_accessor(*attributes)
12
+
13
+ @attributes += attributes
14
+ end
15
+
16
+ def attribute_aliases(aliases = nil)
17
+ @attribute_aliases ||= {}
18
+
19
+ return @attribute_aliases unless aliases
20
+
21
+ @attribute_aliases.merge!(aliases)
22
+ end
23
+
24
+ # Sets all the instance variables by reading the JSON from API
25
+ def build(json: {})
26
+ new.tap do |record|
27
+ attributes.each do |attr|
28
+ key = attribute_aliases.key?(attr) ? attribute_aliases[attr] : attr
29
+ record.public_send("#{attr}=", json[key])
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def initialize(attributes = {})
36
+ attributes.each do |attr, value|
37
+ public_send("#{attr}=", value)
38
+ end
39
+ end
40
+
41
+ def as_json
42
+ self.class.attributes.each_with_object({}) do |attr, hash|
43
+ value = public_send(attr)
44
+
45
+ value = value.as_json if value.respond_to?(:as_json)
46
+ if value.is_a?(Array)
47
+ value = value.map { |v| v.respond_to?(:as_json) ? v.as_json : v }
48
+ end
49
+ value = value.iso8601 if value.respond_to?(:iso8601)
50
+
51
+ hash[attr] = value
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/errors'
4
+ require 'truelayer/client'
5
+
6
+ module Truelayer
7
+ class BaseRepository
8
+ def initialize(access_token:, client: nil)
9
+ @client = client || Client.new(access_token)
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :client
15
+
16
+ def get(path, params: {})
17
+ result = client.get(path.strip, params: params)
18
+
19
+ raise UnauthorizedError, result.body[:error] if result.status == 401
20
+
21
+ result
22
+ end
23
+
24
+ def post(path, params: {})
25
+ result = client.post(path.strip, params: params)
26
+
27
+ raise UnauthorizedError, result.body[:error] if result.status == 401
28
+
29
+ result
30
+ end
31
+
32
+ def format_url(url, params)
33
+ formatted = url.dup.strip
34
+
35
+ params.each { |key, value| formatted.sub!(":#{key}", value) }
36
+
37
+ formatted
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware/multi_json'
5
+ require 'truelayer/configuration'
6
+ require 'truelayer/errors'
7
+
8
+ module Truelayer
9
+ class Client
10
+ def initialize(access_token)
11
+ @access_token = access_token
12
+ end
13
+
14
+ def get(path, headers: {}, params: {})
15
+ connection.get(path, params, headers).tap do |response|
16
+ handle_response(response)
17
+ end
18
+ end
19
+
20
+ def post(path, headers: {}, params: {})
21
+ connection.post(path, params, headers).tap do |response|
22
+ handle_response(response)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :access_token
29
+
30
+ def base_url
31
+ Truelayer.configuration.base_url
32
+ end
33
+
34
+ def connection
35
+ @connection ||= Faraday.new(url: base_url) do |conn|
36
+ conn.request :truelayer_auth, access_token
37
+ conn.response :multi_json, symbolize_keys: true
38
+ if Truelayer.configuration.debug
39
+ conn.response :logger, nil, { headers: true, bodies: Truelayer.configuration.verbose }
40
+ end
41
+ conn.adapter Faraday.default_adapter
42
+ end
43
+ end
44
+
45
+ def handle_response(response)
46
+ return if response.success?
47
+
48
+ case response.status
49
+ when 400
50
+ raise InvalidRequestError, response.body
51
+ when 401
52
+ raise UnauthorizedError, response.body
53
+ when 404
54
+ raise NotFoundError, response.body
55
+ else
56
+ raise GenericError, "#{response.status}: #{response.body}"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/faraday_auth'
4
+
5
+ module Truelayer
6
+ class Configuration
7
+ BASE_URLS = {
8
+ production: 'https://api.truelayer.com',
9
+ sandbox: 'https://api.truelayer-sandbox.com'
10
+ }.freeze
11
+ AUTH_URLS = {
12
+ production: 'https://auth.truelayer.com',
13
+ sandbox: 'https://auth.truelayer-sandbox.com'
14
+ }.freeze
15
+
16
+ attr_accessor :client_id, :client_secret, :environment, :debug, :verbose
17
+
18
+ def initialize
19
+ @client_id = ''
20
+ @client_secret = ''
21
+ @environment = :sandbox # can be either :sandbox or :production
22
+ @debug = false
23
+ @verbose = false
24
+
25
+ return if Faraday::Request.fetch_middleware(:truelayer_auth)
26
+ Faraday::Request.register_middleware truelayer_auth: -> { Truelayer::FaradayAuth }
27
+ end
28
+
29
+ def base_url
30
+ BASE_URLS[environment.to_sym]
31
+ end
32
+
33
+ def auth_url
34
+ AUTH_URLS[environment.to_sym]
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truelayer
4
+ class UnauthorizedError < StandardError; end
5
+
6
+ class GenericError < StandardError; end
7
+
8
+ class InvalidRequestError < StandardError; end
9
+
10
+ class NotFoundError < StandardError; end
11
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'truelayer/version'
5
+
6
+ module Truelayer
7
+ class FaradayAuth < Faraday::Middleware
8
+ def initialize(app, token, options = {})
9
+ super(app)
10
+ @token = token
11
+ @options = options
12
+ end
13
+
14
+ def call(env)
15
+ env[:request_headers]['Authorization'] = "Bearer #{@token}"
16
+ env[:request_headers]['Content-Type'] = 'application/json'
17
+ env[:request_headers]['User-Agent'] = "finpoint/#{Truelayer::VERSION}"
18
+
19
+ @app.call(env)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class Provider < BaseModel
7
+ attributes :provider_id, :display_name, :country, :logo_url, :scopes
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class Response < BaseModel
7
+ attributes :results_uri, :status, :task_id
8
+
9
+ attr_accessor :results
10
+
11
+ def self.build_with_results(json:, results_class:)
12
+ build(json: json).tap do |response|
13
+ if response.success?
14
+ response.results = json.fetch(:results, []).map do |result|
15
+ results_class.build(json: result)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def initialize(*args)
22
+ @results = []
23
+ super
24
+ end
25
+
26
+ def async?
27
+ task_id && !task_id.empty?
28
+ end
29
+
30
+ def success?
31
+ status == 'Succeeded'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class RunningBalance < BaseModel
7
+ attributes :amount, :currency
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class Token < BaseModel
7
+ attributes :access_token, :expires_in, :token_type, :refresh_token, :scope
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'truelayer/base_model'
5
+ require 'truelayer/running_balance'
6
+ require 'truelayer/transaction_meta'
7
+
8
+ module Truelayer
9
+ class Transaction < BaseModel
10
+ attributes :transaction_id, :normalised_provider_transaction_id, :provider_transaction_id,
11
+ :timestamp, :description, :amount, :currency,
12
+ :transaction_type, :transaction_category, :transaction_classification,
13
+ :merchant_name, :running_balance, :meta
14
+
15
+ def self.build(json:)
16
+ super.tap do |record|
17
+ record.running_balance = RunningBalance.build(json: (record.running_balance || {}))
18
+ record.meta = TransactionMeta.build(json: (record.meta || {}))
19
+ record.timestamp = Time.parse(record.timestamp) if record.timestamp
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class TransactionMeta < BaseModel
7
+ attributes :bank_transaction_id, :provider_transaction_category
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Truelayer
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/base_model'
4
+
5
+ module Truelayer
6
+ class WebhookPayload < BaseModel
7
+ attributes :request_timestamp, :request_uri,
8
+ :credentials_id, :results_uri, :status, :task_id,
9
+ :error, :error_description
10
+
11
+ def success?
12
+ status == 'Succeeded'
13
+ end
14
+ end
15
+ end
data/lib/truelayer.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'truelayer/version'
4
+ require 'truelayer/configuration'
5
+ require 'truelayer/authentication_repository'
6
+ require 'truelayer/accounts_repository'
7
+ require 'truelayer/async_repository'
8
+
9
+ module Truelayer
10
+ module_function
11
+
12
+ def configure
13
+ yield(configuration)
14
+ end
15
+
16
+ def configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+ end
data/truelayer.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'truelayer/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'truelayer'
9
+ spec.version = Truelayer::VERSION
10
+ spec.authors = ['Finpoint']
11
+ spec.email = ['nenad@finpoint.co.uk']
12
+
13
+ spec.summary = 'Truelayer client'
14
+ spec.description = 'Truelayer client'
15
+ spec.homepage = 'https://www.finpoint.co.uk'
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = 'exe'
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ['lib']
25
+
26
+ spec.add_dependency 'faraday'
27
+ spec.add_dependency 'faraday_middleware'
28
+ spec.add_dependency 'faraday_middleware-multi_json'
29
+ spec.add_dependency 'multi_json'
30
+ spec.add_development_dependency 'bundler', '~> 2.2'
31
+ spec.add_development_dependency 'dotenv'
32
+ spec.add_development_dependency 'pry'
33
+ spec.add_development_dependency 'rake'
34
+ spec.add_development_dependency 'rspec'
35
+ spec.add_development_dependency 'vcr'
36
+ spec.add_development_dependency 'webmock'
37
+ end
metadata ADDED
@@ -0,0 +1,228 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: truelayer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Finpoint
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday_middleware
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday_middleware-multi_json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: multi_json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: dotenv
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: vcr
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: webmock
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: Truelayer client
168
+ email:
169
+ - nenad@finpoint.co.uk
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".gitignore"
175
+ - ".rubocop.yml"
176
+ - ".ruby-version"
177
+ - Gemfile
178
+ - Gemfile.lock
179
+ - README.md
180
+ - Rakefile
181
+ - bin/console
182
+ - bin/setup
183
+ - lib/truelayer.rb
184
+ - lib/truelayer/account.rb
185
+ - lib/truelayer/account_number.rb
186
+ - lib/truelayer/accounts_repository.rb
187
+ - lib/truelayer/async_repository.rb
188
+ - lib/truelayer/auth_link.rb
189
+ - lib/truelayer/authentication_repository.rb
190
+ - lib/truelayer/balance.rb
191
+ - lib/truelayer/base_model.rb
192
+ - lib/truelayer/base_repository.rb
193
+ - lib/truelayer/client.rb
194
+ - lib/truelayer/configuration.rb
195
+ - lib/truelayer/errors.rb
196
+ - lib/truelayer/faraday_auth.rb
197
+ - lib/truelayer/provider.rb
198
+ - lib/truelayer/response.rb
199
+ - lib/truelayer/running_balance.rb
200
+ - lib/truelayer/token.rb
201
+ - lib/truelayer/transaction.rb
202
+ - lib/truelayer/transaction_meta.rb
203
+ - lib/truelayer/version.rb
204
+ - lib/truelayer/webhook_payload.rb
205
+ - truelayer.gemspec
206
+ homepage: https://www.finpoint.co.uk
207
+ licenses: []
208
+ metadata: {}
209
+ post_install_message:
210
+ rdoc_options: []
211
+ require_paths:
212
+ - lib
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ required_rubygems_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ requirements: []
224
+ rubygems_version: 3.0.3
225
+ signing_key:
226
+ specification_version: 4
227
+ summary: Truelayer client
228
+ test_files: []