tide-api 0.2.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.
@@ -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