starling-ruby 0.1.0 → 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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +8 -0
  3. data/.gitignore +1 -0
  4. data/.reek +27 -0
  5. data/.rubocop.yml +6 -4
  6. data/CHANGELOG.md +17 -0
  7. data/Gemfile.lock +37 -2
  8. data/README.md +102 -17
  9. data/bin/console +2 -6
  10. data/lib/starling.rb +40 -7
  11. data/lib/starling/api_service.rb +45 -37
  12. data/lib/starling/client.rb +165 -6
  13. data/lib/starling/errors/api_error.rb +35 -2
  14. data/lib/starling/errors/base_error.rb +6 -24
  15. data/lib/starling/middlewares/raise_starling_errors.rb +11 -10
  16. data/lib/starling/request.rb +17 -19
  17. data/lib/starling/resources/account_balance_resource.rb +14 -6
  18. data/lib/starling/resources/account_resource.rb +10 -0
  19. data/lib/starling/resources/address_resource.rb +26 -0
  20. data/lib/starling/resources/addresses_resource.rb +18 -0
  21. data/lib/starling/resources/base_resource.rb +34 -9
  22. data/lib/starling/resources/card_resource.rb +46 -0
  23. data/lib/starling/resources/contact_account_resource.rb +31 -0
  24. data/lib/starling/resources/contact_resource.rb +16 -0
  25. data/lib/starling/resources/customer_resource.rb +36 -0
  26. data/lib/starling/resources/direct_debit_mandate_resource.rb +43 -0
  27. data/lib/starling/resources/direct_debit_transaction_resource.rb +54 -0
  28. data/lib/starling/resources/inbound_faster_payments_transaction_resource.rb +56 -0
  29. data/lib/starling/resources/mastercard_transaction_resource.rb +73 -0
  30. data/lib/starling/resources/me_resource.rb +26 -0
  31. data/lib/starling/resources/merchant_location_resource.rb +33 -0
  32. data/lib/starling/resources/merchant_resource.rb +34 -0
  33. data/lib/starling/resources/outbound_faster_payments_transaction_resource.rb +56 -0
  34. data/lib/starling/resources/payment_resource.rb +78 -0
  35. data/lib/starling/resources/transaction_resource.rb +42 -0
  36. data/lib/starling/services/account_balance_service.rb +11 -2
  37. data/lib/starling/services/account_service.rb +16 -3
  38. data/lib/starling/services/addresses_service.rb +26 -0
  39. data/lib/starling/services/base_service.rb +22 -1
  40. data/lib/starling/services/card_service.rb +26 -0
  41. data/lib/starling/services/contact_accounts_service.rb +54 -0
  42. data/lib/starling/services/contacts_service.rb +73 -0
  43. data/lib/starling/services/customer_service.rb +25 -0
  44. data/lib/starling/services/direct_debit_mandates_service.rb +61 -0
  45. data/lib/starling/services/direct_debit_transactions_service.rb +46 -0
  46. data/lib/starling/services/inbound_faster_payments_transactions_service.rb +46 -0
  47. data/lib/starling/services/mastercard_transactions_service.rb +30 -0
  48. data/lib/starling/services/me_service.rb +26 -0
  49. data/lib/starling/services/merchant_locations_service.rb +34 -0
  50. data/lib/starling/services/merchants_service.rb +26 -0
  51. data/lib/starling/services/outbound_faster_payments_transactions_service.rb +46 -0
  52. data/lib/starling/services/payments_service.rb +30 -0
  53. data/lib/starling/services/transactions_service.rb +44 -0
  54. data/lib/starling/utils.rb +30 -0
  55. data/lib/starling/version.rb +1 -1
  56. data/starling-ruby.gemspec +2 -0
  57. metadata +63 -2
@@ -0,0 +1,26 @@
1
+ module Starling
2
+ module Resources
3
+ # A resource representing a response returned from the Who Am I (Me) API
4
+ class MeResource < BaseResource
5
+ # @return [String] the Starling internal ID of the user
6
+ def customer_uid
7
+ parsed_data['customerUid']
8
+ end
9
+
10
+ # @return [true, false] whether the user is authenticated
11
+ def authenticated
12
+ parsed_data['authenticated']
13
+ end
14
+
15
+ # @return [Integer, Fixnum] the time until the user's access token expires
16
+ def expires_in_seconds
17
+ parsed_data['expiresInSeconds']
18
+ end
19
+
20
+ # @return [Array<String>] the scopes of the user's access token
21
+ def scopes
22
+ parsed_data['scopes']
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ module Starling
2
+ module Resources
3
+ # A resource representing a Merchant Location returned from the Merchant Locations API
4
+ class MerchantLocationResource < BaseResource
5
+ # @return [String] the Starling internal ID of the merchant
6
+ def merchant_uid
7
+ parsed_data['merchantUid']
8
+ end
9
+
10
+ # @return [String] the Starling internal ID of the merchant location
11
+ def merchant_location_uid
12
+ parsed_data['merchantLocationUid']
13
+ end
14
+
15
+ # @return [String] the name of the merchant
16
+ def merchant_name
17
+ parsed_data['merchantName']
18
+ end
19
+
20
+ # @return [String] the name of the merchant location
21
+ def location_name
22
+ parsed_data['locationName']
23
+ end
24
+
25
+ # @return [Integer, Fixnum] the MasterCard merchant category code - see
26
+ # {https://www.mastercard.us/content/dam/mccom/en-us/docments/rules/quick-reference-booklet-me-nov_2015.pdf here}
27
+ # for details
28
+ def mastercard_merchant_category_code
29
+ parsed_data['mastercardMerchantCategoryCode']
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Starling
2
+ module Resources
3
+ # A resource representing a Merchant returned from the Merchants API
4
+ class MerchantResource < BaseResource
5
+ # @return [String] the Starling internal ID of the merchant
6
+ def merchant_uid
7
+ parsed_data['merchantUid']
8
+ end
9
+
10
+ # @return [String] the name of the merchant
11
+ def name
12
+ parsed_data['name']
13
+ end
14
+
15
+ # @return [String, nil] the website address of the merchant, or nil if this is not
16
+ # known
17
+ def website
18
+ parsed_data['website']
19
+ end
20
+
21
+ # @return [String, nil] the phone number of the merchant, or nil if this is not
22
+ # known
23
+ def phone_number
24
+ parsed_data['phoneNumber']
25
+ end
26
+
27
+ # @return [String, nil] the Twitter username of the merchant, prefixed with "@", or
28
+ # nil if this is not known
29
+ def twitter_username
30
+ parsed_data['twitterUsername']
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,56 @@
1
+ module Starling
2
+ module Resources
3
+ # A resource representing a Transaction returned from the Transactions Faster
4
+ # Payments Out API
5
+ class OutboundFasterPaymentsTransactionResource < BaseResource
6
+ # @return [String] the Starling internal ID of the transaction
7
+ def id
8
+ parsed_data['id']
9
+ end
10
+
11
+ # @return [String] the currency of the transaction (e.g. "GBP" or "UAH")
12
+ def currency
13
+ parsed_data['currency']
14
+ end
15
+
16
+ # @return [Float] the amount of the transaction
17
+ def amount
18
+ present_float(parsed_data['amount'])
19
+ end
20
+
21
+ # @return [Symbol] the direction of the transaction (e.g. `:outbound`)
22
+ def direction
23
+ present_enum(parsed_data['direction'])
24
+ end
25
+
26
+ # @return [Time] the date and time when the transaction was recorded
27
+ def created
28
+ present_datetime(parsed_data['created'])
29
+ end
30
+ alias created_at created
31
+
32
+ # @return [String] the narrative of the transaction
33
+ def narrative
34
+ parsed_data['narrative']
35
+ end
36
+
37
+ # @return [Symbol] the source of the transaction (e.g. `:master_card`)
38
+ def source
39
+ present_enum(parsed_data['source'])
40
+ end
41
+
42
+ # @return [String, nil] the Starling internal ID of the contact who the payment was
43
+ # sent to, or nil if they are not one of the user's contacts
44
+ def receiving_contact_id
45
+ parsed_data['receivingContactId']
46
+ end
47
+
48
+ # @return [String, nil] the Starling internal ID of the contact account the payment
49
+ # was sent to, or nil if they are not one of the user's
50
+ # contacts
51
+ def receiving_contact_account_id
52
+ parsed_data['receivingContactAccountId']
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,78 @@
1
+ module Starling
2
+ module Resources
3
+ # A resource representing a Payment returned from the Payment API
4
+ class PaymentResource < BaseResource
5
+ # @return [String] the Starling internal ID of the payment
6
+ def payment_order_id
7
+ parsed_data['paymentOrderId']
8
+ end
9
+ alias id payment_order_id
10
+
11
+ # @return [String] the currency of the payment
12
+ def currency
13
+ parsed_data['currency']
14
+ end
15
+
16
+ # @return [Float] the amount of the payment
17
+ def amount
18
+ present_float(parsed_data['amount'])
19
+ end
20
+
21
+ # @return [String] the reference of the payment
22
+ def reference
23
+ parsed_data['reference']
24
+ end
25
+
26
+ # @return [String] the Starling internal ID of the contact account the payment
27
+ # is/was sent to
28
+ def receiving_contact_account_id
29
+ parsed_data['receivingContactAccountId']
30
+ end
31
+
32
+ # @return [String] the name of the recipient of the payment
33
+ def recipient_name
34
+ parsed_data['recipientName']
35
+ end
36
+
37
+ # @return [true, false] where the payment is immediate or scheduled for the future
38
+ def immediate
39
+ parsed_data['immediate']
40
+ end
41
+
42
+ # Returns a Hash describing the recurrence behaviour of the payment, or nil if the
43
+ # payment is not recurring. The values in the Hash will be parsed directly from
44
+ # JSON (e.g. dates will appear as strings).
45
+ #
46
+ # TODO: Consider replacing this with its own resource
47
+ #
48
+ # @return [Hash, nil] a Hash describing the recurrence behaviour of the payment,
49
+ # with values parsed directly from JSON, or nil if the payment
50
+ # is not recurring
51
+ def recurrence_rule
52
+ parsed_data['recurrenceRule']
53
+ end
54
+
55
+ # @return [Date] the start date of the payment
56
+ def start_date
57
+ present_date(parsed_data['startDate'])
58
+ end
59
+
60
+ # @return [Date] the date of the next payment in the series
61
+ def next_date
62
+ present_date(parsed_data['nextDate'])
63
+ end
64
+
65
+ # @return [Time, nil] the time when the payment was cancelled, or nil if it has not
66
+ # been cancelled
67
+ def cancelled_at
68
+ present_datetime(parsed_data['cancelledAt'])
69
+ end
70
+
71
+ # @return [Symbol] the type of the payment (e.g. `:standing_order`) (these values
72
+ # do not seem to be accurate!)
73
+ def payment_type
74
+ present_enum(parsed_data['paymentType'])
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,42 @@
1
+ module Starling
2
+ module Resources
3
+ # A resource representing a Transaction returned from the Transactions API
4
+ class TransactionResource < BaseResource
5
+ # @return [String] the Starling internal ID of the transaction
6
+ def id
7
+ parsed_data['id']
8
+ end
9
+
10
+ # @return [String] the currency of the transaction (e.g. "GBP" or "UAH")
11
+ def currency
12
+ parsed_data['currency']
13
+ end
14
+
15
+ # @return [Float] the amount of the transaction
16
+ def amount
17
+ present_float(parsed_data['amount'])
18
+ end
19
+
20
+ # @return [Symbol] the direction of the transaction (e.g. `:outbound`)
21
+ def direction
22
+ present_enum(parsed_data['direction'])
23
+ end
24
+
25
+ # @return [Time] the date and time when the transaction was recorded
26
+ def created
27
+ present_datetime(parsed_data['created'])
28
+ end
29
+ alias created_at created
30
+
31
+ # @return [String] the narrative of the transaction
32
+ def narrative
33
+ parsed_data['narrative']
34
+ end
35
+
36
+ # @return [Symbol] the source of the transaction (e.g. `:master_card`)
37
+ def source
38
+ present_enum(parsed_data['source'])
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,8 +1,17 @@
1
1
  module Starling
2
2
  module Services
3
+ # A service for accessing the Account API's Get Balance endpoint
3
4
  class AccountBalanceService < BaseService
4
- def get(options = {})
5
- response = @api_service.make_request(:get, '/accounts/balance', options)
5
+ # @param params [Hash] Parameters which will be included in the HTTP request,
6
+ # included in the URL as a query string
7
+ # @param headers [Hash] Headers which be included in the HTTP request, merged on
8
+ # top of the headers set at the {Client} level
9
+ # @return [Resources::AccountBalanceResource]
10
+ # @raise [Errors::ApiError] if the HTTP request returns a status indicating that it
11
+ # was unsuccessful
12
+ def get(params: {}, headers: {})
13
+ response = api_service.make_request(:get, '/accounts/balance', params: params,
14
+ headers: headers)
6
15
  resource.new(response: response)
7
16
  end
8
17
 
@@ -1,12 +1,25 @@
1
1
  module Starling
2
2
  module Services
3
+ # A service for accessing the Account API's Get Account endpoint
3
4
  class AccountService < BaseService
4
- def get(options = {})
5
- resource.new(response: @api_service.make_request(:get, '/accounts', options))
5
+ # @param params [Hash] Parameters which will be included in the HTTP request,
6
+ # included in the URL as a query string
7
+ # @param headers [Hash] Headers which be included in the HTTP request, merged on
8
+ # top of the headers set at the {Client} level
9
+ # @return [Resources::AccountResource]
10
+ # @raise [Errors::ApiError] if the HTTP request returns a status indicating that it
11
+ # was unsuccessful
12
+ def get(params: {}, headers: {})
13
+ response = api_service.make_request(:get, '/accounts', params: params,
14
+ headers: headers)
15
+
16
+ resource.new(response: response)
6
17
  end
7
18
 
19
+ # @return [Services::AccountBalanceService] a configured service for accessing the
20
+ # Account Balance API
8
21
  def balance
9
- Services::AccountBalanceService.new(@api_service)
22
+ Services::AccountBalanceService.new(api_service)
10
23
  end
11
24
 
12
25
  private
@@ -0,0 +1,26 @@
1
+ module Starling
2
+ module Services
3
+ # A service for accessing the Address API
4
+ class AddressesService < BaseService
5
+ # @param params [Hash] Parameters which will be included in the HTTP request,
6
+ # included in the URL as a query string
7
+ # @param headers [Hash] Headers which be included in the HTTP request, merged on
8
+ # top of the headers set at the {Client} level
9
+ # @return [Resources::AddressesResource]
10
+ # @raise [Errors::ApiError] if the HTTP request returns a status indicating that it
11
+ # was unsuccessful
12
+ def get(params: {}, headers: {})
13
+ response = api_service.make_request(:get, '/addresses', params: params,
14
+ headers: headers)
15
+
16
+ resource.new(response: response)
17
+ end
18
+
19
+ private
20
+
21
+ def resource
22
+ Resources::AddressesResource
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,20 +2,41 @@ require 'json'
2
2
 
3
3
  module Starling
4
4
  module Services
5
+ # A basic implementation of a service for interacting with the
6
+ # Starling Bank API, mapping HTTP request to endpoints to Ruby methods
5
7
  class BaseService
8
+ # @param api_service [ApiService]
6
9
  def initialize(api_service)
7
10
  @api_service = api_service
8
11
  end
9
12
 
10
13
  private
11
14
 
12
- def build_collection(response, key:, resource:)
15
+ attr_reader :api_service
16
+
17
+ def build_collection_from_key(response, key:, resource:)
18
+ JSON.parse(response.body)
19
+ .fetch(key)
20
+ .map do |parsed_data|
21
+ resource.new(parsed_data: parsed_data)
22
+ end
23
+ end
24
+
25
+ def build_collection_from_embedded_key(response, key:, resource:)
13
26
  JSON.parse(response.body)
14
27
  .fetch('_embedded', {})
15
28
  .fetch(key, []).map do |parsed_data|
16
29
  resource.new(parsed_data: parsed_data)
17
30
  end
18
31
  end
32
+
33
+ def options_without_params(options)
34
+ options.reject { |key, _| key == :params }
35
+ end
36
+
37
+ def convert_location_header_to_relative_path(location)
38
+ location.gsub(%r{^/api/v1}, '')
39
+ end
19
40
  end
20
41
  end
21
42
  end
@@ -0,0 +1,26 @@
1
+ module Starling
2
+ module Services
3
+ # A service for accessing the Card API
4
+ class CardService < BaseService
5
+ # @param params [Hash] Parameters which will be included in the HTTP request,
6
+ # included in the URL as a query string
7
+ # @param headers [Hash] Headers which be included in the HTTP request, merged on
8
+ # top of the headers set at the {Client} level
9
+ # @return [Resources::CardResource]
10
+ # @raise [Errors::ApiError] if the HTTP request returns a status indicating that it
11
+ # was unsuccessful
12
+ def get(params: {}, headers: {})
13
+ response = api_service.make_request(:get, '/cards', params: params,
14
+ headers: headers)
15
+
16
+ resource.new(response: response)
17
+ end
18
+
19
+ private
20
+
21
+ def resource
22
+ Resources::CardResource
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,54 @@
1
+ module Starling
2
+ module Services
3
+ # A service for accessing the Contact API's Get Contact Accounts and Get Contact
4
+ # endpoints
5
+ class ContactAccountsService < BaseService
6
+ # @param contact_id [String] The Starling internal ID of the contact the contact
7
+ # account belongs to
8
+ # @param contact_account_id [String] The Starling internal ID of the contact
9
+ # account
10
+ # @param params [Hash] Parameters which will be included in the HTTP request,
11
+ # included in the URL as a query string
12
+ # @param headers [Hash] Headers which be included in the HTTP request, merged on
13
+ # top of the headers set at the {Client} level
14
+ # @return [Resources::ContactAccountResource]
15
+ # @raise [Errors::ApiError] if the HTTP request returns a status indicating that it
16
+ # was unsuccessful
17
+ def get(contact_id, contact_account_id, params: {}, headers: {})
18
+ response = api_service.make_request(
19
+ :get,
20
+ "/contacts/#{contact_id}/accounts/#{contact_account_id}",
21
+ params: params,
22
+ headers: headers
23
+ )
24
+
25
+ resource.new(response: response)
26
+ end
27
+
28
+ # @param contact_id [String] The Starling internal ID of the contact
29
+ # @param params [Hash] Parameters which will be included in the HTTP request,
30
+ # included in the URL as a query string
31
+ # @param headers [Hash] Headers which be included in the HTTP request, merged on
32
+ # top of the headers set at the {Client} level
33
+ # @return [Array<Resources::ContactAccountResource>]
34
+ # @raise [Errors::ApiError] if the HTTP request returns a status indicating that it
35
+ # was unsuccessful
36
+ def list(contact_id, params: {}, headers: {})
37
+ response = api_service.make_request(
38
+ :get,
39
+ "/contacts/#{contact_id}/accounts",
40
+ params: params,
41
+ headers: headers
42
+ )
43
+
44
+ build_collection_from_key(response, key: 'contactAccounts', resource: resource)
45
+ end
46
+
47
+ private
48
+
49
+ def resource
50
+ Resources::ContactAccountResource
51
+ end
52
+ end
53
+ end
54
+ end