truelayer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []