tide-api 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ module Tide
2
+ module API
3
+ # A business company owned by the account holder
4
+ class Company < Dry::Struct::Value
5
+ # Tide's unique ID of the company.
6
+ #
7
+ # @return [Integer]
8
+ #
9
+ attribute :company_id, Types::Strict::Integer
10
+
11
+ # Registration number in Companies House.
12
+ #
13
+ # @return [String]
14
+ #
15
+ attribute :number, Types::Strict::String
16
+
17
+ # Legal name of the company.
18
+ #
19
+ # @return [Integer]
20
+ #
21
+ attribute :name, Types::Strict::String
22
+
23
+ # Trading name of the company.
24
+ #
25
+ # @return [Integer]
26
+ #
27
+ attribute :trading_name, Types::Strict::String
28
+
29
+ # SIC code.
30
+ #
31
+ # @return [Integer|nil]
32
+ #
33
+ attribute :sic_code, Types::Strict::Integer.optional
34
+
35
+ # VAT number.
36
+ #
37
+ # @return [String|nil]
38
+ #
39
+ attribute :vat_number, Types::Strict::String.optional
40
+
41
+ # Wether the company is registered or not? TODO
42
+ #
43
+ # @return [Boolean]
44
+ #
45
+ attribute :registered, Types::Strict::Bool
46
+
47
+ # Date when the company was created in Tide.
48
+ #
49
+ # @return [DateTime]
50
+ #
51
+ attribute :iso_created_on, Types::Params::DateTime
52
+
53
+ # Date when the company was was updated in Tide.
54
+ #
55
+ # @return [DateTime]
56
+ #
57
+ attribute :iso_updated_on, Types::Params::DateTime
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,24 @@
1
+ module Tide
2
+ module API
3
+ # An error resulting from an API call
4
+ class Error < Dry::Struct::Value
5
+ # A code that uniquely identifies the error
6
+ #
7
+ # @return [Integer]
8
+ #
9
+ attribute :code, Types::Strict::Integer
10
+
11
+ # A human readable description of the code
12
+ #
13
+ # @return [String]
14
+ #
15
+ attribute :message, Types::Strict::String
16
+
17
+ # A long description of the error. Unfortunately, it is null most of the time.
18
+ #
19
+ # @return [String|nil]
20
+ #
21
+ attribute :details, Types::Strict::String.optional
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ require 'http'
2
+ require 'tide/api/response'
3
+
4
+ module Tide
5
+ module API
6
+ # Responsible for the HTTP interactions. The only entity aware of HTTP concerns such as status codes and headers.
7
+ #
8
+ # @api private
9
+ #
10
+ class HTTPClient
11
+ # An OAuth2 access token
12
+ attr_accessor :access_token
13
+
14
+ # An OAuth2 refresh token
15
+ attr_accessor :refresh_token
16
+
17
+ # Performs a GET request to Tide's API. Every request returns the status code 200.
18
+ #
19
+ # @example Retrieving a resource
20
+ # client = HTTPClient.new
21
+ # result = client.get('https://api.tide.co/tide-backend/rest/api/v1/oauth2/tokens')
22
+ #
23
+ # @param [String] endpoint URL of the API endpoint of the GET request
24
+ #
25
+ # @return [Response] Generic response of a request to Tide's API
26
+ #
27
+ def get(endpoint)
28
+ response = HTTP.headers(headers).get(endpoint)
29
+
30
+ Response.new(JSON.parse(response.body), response.status != 200)
31
+ end
32
+
33
+ private
34
+
35
+ def headers
36
+ return {} if access_token.nil?
37
+
38
+ { Authorization: "Bearer #{access_token}" }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ require 'tide/api/util'
2
+
3
+ module Tide
4
+ module API
5
+ # Transforms arrays of hashes into concrete instances of classes.
6
+ #
7
+ # @api private
8
+ #
9
+ class Mapper
10
+ # Creates objects from an API response. The objects will be an instance of +object_class+.
11
+ #
12
+ # @param [Class] object_class Class of the final objects
13
+ # @param [Array<Hash>] items Array of attributes to instantiate the final objects
14
+ #
15
+ # @return An array of objects of the given class
16
+ #
17
+ def map(items, object_class)
18
+ items.map { |item| map_one(item, object_class) }
19
+ end
20
+
21
+ # Creates a single object from an API response. The object will be an instance of +object_class+.
22
+ #
23
+ # @param [Class] object_class Class of the final object
24
+ # @param [Hash] item Attributes to instantiate the final object
25
+ #
26
+ # @return An object of the given class
27
+ #
28
+ def map_one(item, object_class)
29
+ attributes = item.transform_keys { |attribute| Util.underscore(attribute) }
30
+ object_class.new(attributes)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ module Tide
2
+ module API
3
+ # Generic response of a request to Tide's API
4
+ #
5
+ # @api private
6
+ #
7
+ class Response
8
+ # Hash or array of hashes representing each item in the response body.
9
+ #
10
+ # @return [Hash|Array<Hash>]
11
+ #
12
+ attr_reader :payload
13
+
14
+ # Instantiates a new API response
15
+ #
16
+ # @param [Hash|Array<Hash>] payload Hash or array of hashes representing each item in the response body.
17
+ # @param [Boolean] error Whether the request failed
18
+ #
19
+ def initialize(payload, error)
20
+ @payload = payload
21
+ @error = error
22
+ end
23
+
24
+ # Whether the response contains errors
25
+ #
26
+ # @return [Boolean] true if the response has errors and false otherwise
27
+ #
28
+ def error?
29
+ @error
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Tide
2
+ module API
3
+ # OAuth2 access and refresh tokens
4
+ class Tokens < Dry::Struct::Value
5
+ # OAuth2 Access Token
6
+ #
7
+ # @return [String]
8
+ #
9
+ attribute :access_token, Types::Strict::String
10
+
11
+ # OAuth2 Refresh Token
12
+ #
13
+ # @return [String]
14
+ #
15
+ attribute :refresh_token, Types::Strict::String
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,127 @@
1
+ module Tide
2
+ module API
3
+ # Bank account transaction
4
+ class Transaction < Dry::Struct::Value
5
+ # Categories of the transaction
6
+ CategoryType = Types::Strict::String.enum('EXPENDITURE', 'INCOME')
7
+
8
+ # Statuses of the transaction
9
+ Status = Types::Strict::String.enum('cleared', 'pending')
10
+
11
+ # Payment type codes
12
+ Type = Types::Strict::String.enum(
13
+ 'PYI', # Faster Payment In
14
+ 'PYI_REV', # Faster Payment In Reversal
15
+ 'PYO', # Faster Payment Out
16
+ 'TRD', # Inter-Account Transfer Out
17
+ 'TRC', # Inter-Account Transfer In
18
+ 'RED', # Card Payment Out
19
+ 'RED_REV', # Card Payment Reversal
20
+ 'WTH', # Cash Withdrawal
21
+ 'WTH_REV', # Cash Withdrawal Reversal
22
+ 'REF', # Refund
23
+ 'REF_REV', # Refund Reversal
24
+ 'FEE', # Fee
25
+ 'TOP', # Tide Credit
26
+ 'TOP_REV' # Tide Credit Reversal
27
+ )
28
+
29
+ # Tide's unique transaction ID.
30
+ #
31
+ # @return [Integer]
32
+ #
33
+ attribute :transaction_id, Types::Strict::Integer
34
+
35
+ # ID of the parent account.
36
+ #
37
+ # @return [Integer]
38
+ #
39
+ attribute :account_id, Types::Strict::Integer
40
+
41
+ # Transaction amount. Can be positive or negative.
42
+ #
43
+ # @return [BigDecimal]
44
+ #
45
+ attribute :amount, Types::Params::Decimal
46
+
47
+ # Transaction Type code.
48
+ #
49
+ # @see +Type+.
50
+ # @return [String]
51
+ #
52
+ attribute :type, Type
53
+
54
+ # Unique reference of the transaction.
55
+ #
56
+ # @return [String]
57
+ #
58
+ attribute :txn_ref, Types::Strict::String
59
+
60
+ # Time of the transaction.
61
+ #
62
+ # @return [DateTime]
63
+ #
64
+ attribute :iso_transaction_date_time, Types::Params::DateTime
65
+
66
+ # Time when the transaction was applied
67
+ #
68
+ # @return [DateTime]
69
+ #
70
+ attribute :iso_applied_date_time, Types::Params::DateTime
71
+
72
+ # Time when the transaction was cleared.
73
+ #
74
+ # @return [DateTime]
75
+ #
76
+ attribute :iso_cleared_date_time, Types::Params::DateTime
77
+
78
+ # Masked debit/credit card number.
79
+ #
80
+ # @return [String]
81
+ #
82
+ attribute :masked_pan, Types::Strict::String.optional
83
+
84
+ # TODO
85
+ #
86
+ # @return [String]
87
+ #
88
+ attribute :status, Status
89
+
90
+ # TODO
91
+ #
92
+ # @return [String]
93
+ #
94
+ attribute :description, Types::Strict::String
95
+
96
+ # TODO
97
+ #
98
+ # @return [DateTime]
99
+ #
100
+ attribute :iso_created_on, Types::Params::DateTime
101
+
102
+ # TODO
103
+ #
104
+ # @return [DateTime]
105
+ #
106
+ attribute :iso_updated_on, Types::Params::DateTime
107
+
108
+ # TODO
109
+ #
110
+ # @return [Integer]
111
+ #
112
+ attribute :category_id, Types::Strict::Integer
113
+
114
+ # TODO
115
+ #
116
+ # @return [String|String]
117
+ #
118
+ attribute :category_name, Types::Strict::String.optional
119
+
120
+ # TODO
121
+ #
122
+ # @return [String|nil]
123
+ #
124
+ attribute :category_type, CategoryType.optional
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,14 @@
1
+ require 'dry/types'
2
+ require 'dry/struct'
3
+
4
+ module Tide
5
+ module API
6
+ # Dry-types container.
7
+ #
8
+ # @api private
9
+ #
10
+ module Types
11
+ include Dry.Types
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,41 @@
1
+ module Tide
2
+ module API
3
+ # Provides methods for string manipulation
4
+ #
5
+ # @api private
6
+ #
7
+ module Util
8
+ module_function
9
+
10
+ # Converts a string to snake case
11
+ #
12
+ # @param [String|Symbol] term The term to be converted.
13
+ #
14
+ # @return [String] A snake-cased version of the input
15
+ #
16
+ def underscore(term)
17
+ term
18
+ .to_s
19
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
20
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
21
+ .tr('-', '_')
22
+ .downcase
23
+ .to_sym
24
+ end
25
+
26
+ # Converts a string to camel case
27
+ #
28
+ # @param [String|Symbol] term The term to be converted.
29
+ #
30
+ # @return [String] A camel-cased version of the input
31
+ #
32
+ def camelize(term)
33
+ string = term.to_s
34
+ string = string.sub(/^[a-z\d]*/, &:capitalize)
35
+ string.gsub!(%r{(?:_|(\/))([a-z\d]*)}) { "#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}" }
36
+ string.gsub!('/'.freeze, '::'.freeze)
37
+ string
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module Tide
2
+ module API
3
+ VERSION = '0.2.0'.freeze
4
+ end
5
+ end
data/lib/tide/api.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'tide/api/version'
2
+ require 'tide/api/types'
3
+ require 'tide/api/client'
4
+
5
+ # Encapsulates all the code of the gem in a meaningful namespace.
6
+ module Tide
7
+ # Operations related to Tide's API.
8
+ module API
9
+ end
10
+ end
data/tide-api.gemspec ADDED
@@ -0,0 +1,49 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'tide/api/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'tide-api'
7
+ spec.version = Tide::API::VERSION
8
+ spec.authors = ['Wilson Silva']
9
+ spec.email = ['me@wilsonsilva.net']
10
+
11
+ spec.summary = "Client for Tide's bank RESTful API"
12
+ spec.description = "Client for Tide's bank RESTful API"
13
+ spec.homepage = 'https://github.com/wilsonsilva/tide-api'
14
+ spec.license = 'MIT'
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+
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 'dry-struct', '~> 1.0'
27
+ spec.add_dependency 'http', '~> 4.1'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.17'
30
+ spec.add_development_dependency 'bundler-audit', '~> 0.6'
31
+ spec.add_development_dependency 'guard', '~> 2.15'
32
+ spec.add_development_dependency 'guard-bundler', '~> 2.2'
33
+ spec.add_development_dependency 'guard-bundler-audit', '~> 0.1'
34
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
35
+ spec.add_development_dependency 'guard-rubocop', '~> 1.3'
36
+ spec.add_development_dependency 'overcommit', '~> 0.48'
37
+ spec.add_development_dependency 'pry', '~> 0.12'
38
+ spec.add_development_dependency 'rake', '~> 10.0'
39
+ spec.add_development_dependency 'rspec', '~> 3.0'
40
+ spec.add_development_dependency 'rubocop', '~> 0.72'
41
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.33'
42
+ spec.add_development_dependency 'simplecov', '~> 0.16'
43
+ spec.add_development_dependency 'simplecov-console', '~> 0.5'
44
+ spec.add_development_dependency 'vcr', '~> 4.0'
45
+ spec.add_development_dependency 'webmock', '~> 3.5'
46
+ spec.add_development_dependency 'yard', '~> 0.9'
47
+ spec.add_development_dependency 'yard-junk', '~> 0.0.7'
48
+ spec.add_development_dependency 'yardstick', '~> 0.9'
49
+ end