iron_bank 0.1.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/.env.example +7 -0
  3. data/.github/CODEOWNERS +1 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  5. data/.gitignore +6 -1
  6. data/.reek +95 -0
  7. data/.rspec +1 -2
  8. data/.rubocop.yml +55 -0
  9. data/.travis.yml +22 -3
  10. data/Gemfile +3 -3
  11. data/LICENSE +176 -0
  12. data/README.md +133 -0
  13. data/Rakefile +39 -3
  14. data/bin/console +13 -1
  15. data/bin/setup +1 -0
  16. data/iron_bank.gemspec +34 -10
  17. data/lib/generators/iron_bank/install/install_generator.rb +20 -0
  18. data/lib/generators/iron_bank/install/templates/README +16 -0
  19. data/lib/generators/iron_bank/install/templates/iron_bank.rb +29 -0
  20. data/lib/iron_bank/action.rb +39 -0
  21. data/lib/iron_bank/actions/amend.rb +16 -0
  22. data/lib/iron_bank/actions/create.rb +19 -0
  23. data/lib/iron_bank/actions/delete.rb +19 -0
  24. data/lib/iron_bank/actions/execute.rb +21 -0
  25. data/lib/iron_bank/actions/generate.rb +19 -0
  26. data/lib/iron_bank/actions/query.rb +16 -0
  27. data/lib/iron_bank/actions/query_more.rb +21 -0
  28. data/lib/iron_bank/actions/subscribe.rb +32 -0
  29. data/lib/iron_bank/actions/update.rb +19 -0
  30. data/lib/iron_bank/associations.rb +73 -0
  31. data/lib/iron_bank/authentication.rb +37 -0
  32. data/lib/iron_bank/authentications/cookie.rb +80 -0
  33. data/lib/iron_bank/authentications/token.rb +82 -0
  34. data/lib/iron_bank/cacheable.rb +52 -0
  35. data/lib/iron_bank/client.rb +96 -0
  36. data/lib/iron_bank/collection.rb +31 -0
  37. data/lib/iron_bank/configuration.rb +86 -0
  38. data/lib/iron_bank/csv.rb +29 -0
  39. data/lib/iron_bank/describe/field.rb +65 -0
  40. data/lib/iron_bank/describe/object.rb +81 -0
  41. data/lib/iron_bank/describe/related.rb +40 -0
  42. data/lib/iron_bank/describe/tenant.rb +50 -0
  43. data/lib/iron_bank/endpoint.rb +36 -0
  44. data/lib/iron_bank/error.rb +45 -0
  45. data/lib/iron_bank/instrumentation.rb +14 -0
  46. data/lib/iron_bank/local.rb +72 -0
  47. data/lib/iron_bank/local_records.rb +52 -0
  48. data/lib/iron_bank/logger.rb +23 -0
  49. data/lib/iron_bank/metadata.rb +36 -0
  50. data/lib/iron_bank/object.rb +89 -0
  51. data/lib/iron_bank/open_tracing.rb +17 -0
  52. data/lib/iron_bank/operation.rb +33 -0
  53. data/lib/iron_bank/operations/billing_preview.rb +16 -0
  54. data/lib/iron_bank/query_builder.rb +72 -0
  55. data/lib/iron_bank/queryable.rb +55 -0
  56. data/lib/iron_bank/resource.rb +70 -0
  57. data/lib/iron_bank/resources/account.rb +62 -0
  58. data/lib/iron_bank/resources/amendment.rb +13 -0
  59. data/lib/iron_bank/resources/catalog_tiers/discount_amount.rb +26 -0
  60. data/lib/iron_bank/resources/catalog_tiers/discount_percentage.rb +26 -0
  61. data/lib/iron_bank/resources/catalog_tiers/price.rb +26 -0
  62. data/lib/iron_bank/resources/contact.rb +13 -0
  63. data/lib/iron_bank/resources/export.rb +11 -0
  64. data/lib/iron_bank/resources/import.rb +12 -0
  65. data/lib/iron_bank/resources/invoice.rb +37 -0
  66. data/lib/iron_bank/resources/invoice_adjustment.rb +13 -0
  67. data/lib/iron_bank/resources/invoice_item.rb +25 -0
  68. data/lib/iron_bank/resources/invoice_payment.rb +13 -0
  69. data/lib/iron_bank/resources/payment.rb +17 -0
  70. data/lib/iron_bank/resources/payment_method.rb +14 -0
  71. data/lib/iron_bank/resources/product.rb +14 -0
  72. data/lib/iron_bank/resources/product_rate_plan.rb +32 -0
  73. data/lib/iron_bank/resources/product_rate_plan_charge.rb +27 -0
  74. data/lib/iron_bank/resources/product_rate_plan_charge_tier.rb +60 -0
  75. data/lib/iron_bank/resources/rate_plan.rb +21 -0
  76. data/lib/iron_bank/resources/rate_plan_charge.rb +30 -0
  77. data/lib/iron_bank/resources/rate_plan_charge_tier.rb +26 -0
  78. data/lib/iron_bank/resources/subscription.rb +28 -0
  79. data/lib/iron_bank/resources/usage.rb +16 -0
  80. data/lib/iron_bank/response/raise_error.rb +16 -0
  81. data/lib/iron_bank/schema.rb +58 -0
  82. data/lib/iron_bank/utils.rb +59 -0
  83. data/lib/iron_bank/version.rb +4 -1
  84. data/lib/iron_bank.rb +152 -2
  85. metadata +300 -12
data/iron_bank.gemspec CHANGED
@@ -1,15 +1,23 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'iron_bank/version'
4
6
 
5
7
  Gem::Specification.new do |spec|
6
8
  spec.name = 'iron_bank'
7
9
  spec.version = IronBank::VERSION
8
- spec.authors = ['Mickael Pham']
9
- spec.email = ['mickael.pham@gmail.com']
10
- spec.summary = 'The Iron Bank is always glad to be of service.'
11
- spec.homepage = 'https://github.com/mickaelpham/iron_bank'
12
- spec.license = 'Nonstandard'
10
+ spec.summary = 'An opinionated Ruby interface to the Zuora API.'
11
+ spec.homepage = 'https://github.com/zendesk/iron_bank'
12
+ spec.license = 'Apache-2.0'
13
+ spec.authors = ['Mickael Pham', 'Cheng Cui', 'Ryan Ringler', 'Mustafa Turan']
14
+
15
+ spec.email = [
16
+ 'mickael@zendesk.com',
17
+ 'ccui@zendesk.com',
18
+ 'rringler@zendesk.com',
19
+ 'mturan@zendesk.com'
20
+ ]
13
21
 
14
22
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
15
23
  f.match(%r{^(test|spec|features)/})
@@ -19,8 +27,24 @@ Gem::Specification.new do |spec|
19
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
28
  spec.require_paths = ['lib']
21
29
 
22
- spec.add_development_dependency 'bundler', '~> 1.15'
23
- spec.add_development_dependency 'rake', '~> 10.0'
24
- spec.add_development_dependency 'rspec', '~> 3.0'
25
- spec.add_development_dependency 'pry', '~> 0.10'
30
+ spec.add_development_dependency 'bump', '~> 0.5'
31
+ spec.add_development_dependency 'bundler', '~> 1.15'
32
+ spec.add_development_dependency 'dotenv', '~> 2.2'
33
+ spec.add_development_dependency 'factory_bot', '~> 4.10'
34
+ spec.add_development_dependency 'private_gem', '~> 1.1'
35
+ spec.add_development_dependency 'pry-byebug', '~> 3.4'
36
+ spec.add_development_dependency 'rake', '~> 12.0'
37
+ spec.add_development_dependency 'reek', '~> 4.6'
38
+ spec.add_development_dependency 'rspec', '~> 3.0'
39
+ spec.add_development_dependency 'rubocop', '~> 0.52'
40
+ spec.add_development_dependency 'shoulda-matchers', '~> 3.1'
41
+ spec.add_development_dependency 'simplecov', '~> 0.15'
42
+ spec.add_development_dependency 'timecop', '~> 0.9.0'
43
+ spec.add_development_dependency 'vcr', '~> 4.0'
44
+ spec.add_development_dependency 'webmock', '~> 3.0'
45
+
46
+ spec.add_dependency 'ddtrace', '~> 0'
47
+ spec.add_dependency 'faraday', '~> 0'
48
+ spec.add_dependency 'faraday_middleware', '~> 0'
49
+ spec.add_dependency 'nokogiri', '~> 1'
26
50
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+
5
+ module IronBank
6
+ module Generators
7
+ # Allow us to run `rails generate iron_bank:install`
8
+ class InstallGenerator < Rails::Generators::Base
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ def create_iron_bank_initializer
12
+ copy_file 'iron_bank.rb', 'config/initializers/iron_bank.rb'
13
+ end
14
+
15
+ def display_readme_in_terminal
16
+ readme 'README'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ *******************************************************************************
2
+
3
+ Next steps:
4
+
5
+ 1. Edit the config/initializers/iron_bank.rb file with your Zuora credentials
6
+
7
+ 2. Export the schema for your Zuora tenant by running in the Rails console:
8
+
9
+ IronBank::Schema.export
10
+
11
+ 3. (optional) Export your product catalog by running in the Rails console:
12
+
13
+ IronBank::LocalRecords.export
14
+ exit # need to exit the session to finish exporting the local records
15
+
16
+ *******************************************************************************
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ IronBank.configure do |config|
4
+ # Zuora OAuth client ID
5
+ config.client_id = '<my-client-id-from-zuora>'
6
+
7
+ # Zuora OAuth client secret
8
+ config.client_secret = '<my-secret-from-zuora>'
9
+
10
+ # Zuora API domain (apisandbox, production, etc.)
11
+ config.domain = 'rest.apisandbox.zuora.com'
12
+
13
+ # Directory where the metadata XML files (Zuora schema) will be stored
14
+ config.schema_directory = 'config/zuora/schema'
15
+
16
+ # Directory where the local export CSV files will be stored
17
+ config.export_directory = 'config/zuora/local_records'
18
+
19
+ # Zuora authentication type:
20
+ # - `token` uses OAuth and is the *recommended* approach
21
+ # - `cookie` uses username/password for Zuora environments that do not
22
+ # support OAuth authentication, e.g., services environment. If
23
+ # using `cookie` authentication, then use an API user username as
24
+ # the `client_id` and the API user password as `client_secret`
25
+ config.auth_type = 'token'
26
+
27
+ # Set the gem to use the Rails logger
28
+ config.logger = Rails.logger
29
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ # Base class for Zuora actions, e.g., subscribe
5
+ #
6
+ class Action
7
+ private_class_method :new
8
+
9
+ def self.call(args)
10
+ new(args).call
11
+ end
12
+
13
+ def call
14
+ IronBank.client.connection.post(endpoint, params).body
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :args
20
+
21
+ def initialize(args)
22
+ @args = args
23
+ end
24
+
25
+ def endpoint
26
+ "v1/action/#{name.downcase}"
27
+ end
28
+
29
+ def name
30
+ self.class.name.split('::').last
31
+ end
32
+
33
+ def requests(type: :upper)
34
+ args.fetch(:objects).map do |object|
35
+ IronBank::Object.new(object).deep_camelize(type: type)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Use the amend call to change a subscription
6
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTamend
7
+ #
8
+ class Amend < Action
9
+ private
10
+
11
+ def params
12
+ { requests: args }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Create one or more objects of a specific type
6
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTcreate
7
+ #
8
+ class Create < Action
9
+ private
10
+
11
+ def params
12
+ {
13
+ objects: requests,
14
+ type: args.fetch(:type)
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Delete one or more objects of the same type
6
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTdelete
7
+ #
8
+ class Delete < Action
9
+ private
10
+
11
+ def params
12
+ {
13
+ ids: args.fetch(:ids),
14
+ type: args.fetch(:type)
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Use the execute call to execute the process to split an invoice into
6
+ # multiple invoices (the original invoice must be in draft status)
7
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTexecute
8
+ #
9
+ class Execute < Action
10
+ private
11
+
12
+ def params
13
+ {
14
+ ids: args.fetch(:ids),
15
+ synchronous: args.fetch(:synchronous),
16
+ type: args.fetch(:type)
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Generate an on-demand invoice for a specific customer
6
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTgenerate
7
+ #
8
+ class Generate < Action
9
+ private
10
+
11
+ def params
12
+ {
13
+ objects: args.fetch(:objects).map(&:deep_camelize),
14
+ type: args.fetch(:type)
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Query Zuora using ZOQL
6
+ # https://knowledgecenter.zuora.com/DC_Developers/K_Zuora_Object_Query_Language
7
+ #
8
+ class Query < Action
9
+ private
10
+
11
+ def params
12
+ { 'queryString' => args }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Query More call to Zuora REST API
6
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTqueryMore
7
+ #
8
+ class QueryMore < Action
9
+ private
10
+
11
+ def params
12
+ { 'queryLocator' => args }
13
+ end
14
+
15
+ # NOTE: Zuora API endpoint is case-sensitive.
16
+ def endpoint
17
+ 'v1/action/queryMore'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Use the subscribe call to bundle information required to create at least
6
+ # one new subscription
7
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTsubscribe
8
+ #
9
+ class Subscribe < Action
10
+ def call
11
+ body = IronBank.client.connection.post(endpoint, params).body
12
+
13
+ if body.is_a?(Array)
14
+ body.map { |result| IronBank::Object.new(result).deep_underscore }
15
+ else
16
+ IronBank::Object.new(body).deep_underscore
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def params
23
+ { subscribes: subscribe_requests }
24
+ end
25
+
26
+ def subscribe_requests
27
+ requests = [args].flatten
28
+ requests.map { |request| IronBank::Object.new(request).deep_camelize }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Actions
5
+ # Update the information in one or more objects of the same type
6
+ # https://www.zuora.com/developer/api-reference/#operation/Action_POSTupdate
7
+ #
8
+ class Update < Action
9
+ private
10
+
11
+ def params
12
+ {
13
+ objects: requests,
14
+ type: args.fetch(:type)
15
+ }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ # Define association methods for Zuora resources.
5
+ #
6
+ module Associations
7
+ # Define class methods
8
+ #
9
+ module ClassMethods
10
+ def with_one(name, options = {})
11
+ resource_name = options.fetch(
12
+ :resource_name,
13
+ IronBank::Utils.camelize(name)
14
+ )
15
+ foreign_key = options.fetch(:foreign_key, name.to_s + '_id')
16
+
17
+ define_method(name) do
18
+ return unless (foreign_key_value = public_send(foreign_key))
19
+
20
+ with_memoization(name) do
21
+ # NOTE: we retrieve the constant here to prevent the need to order
22
+ # our `require <file>` statements in `iron_bank.rb`
23
+ klass = IronBank::Resources.const_get(resource_name)
24
+ klass.find(foreign_key_value)
25
+ end
26
+ end
27
+
28
+ # Association is "also known as"
29
+ aka = options[:aka]
30
+ alias_method aka, name if aka
31
+ end
32
+
33
+ def with_many(name, options = {})
34
+ resource_name = options.fetch(
35
+ :resource_name,
36
+ IronBank::Utils.camelize(name.to_s.chop)
37
+ )
38
+
39
+ foreign_key = options.fetch(
40
+ :foreign_key,
41
+ IronBank::Utils.underscore(object_name) + '_id'
42
+ )
43
+
44
+ define_method(name) do
45
+ with_memoization(name) do
46
+ # NOTE: we retrieve the constant here to prevent the need to order
47
+ # our `require <file>` statements in `iron_bank.rb`
48
+ klass = IronBank::Resources.const_get(resource_name)
49
+ conditions = options.fetch(:conditions, {}).
50
+ merge("#{foreign_key}": id)
51
+ items = klass.where(conditions)
52
+ IronBank::Collection.new(klass, conditions, items)
53
+ end
54
+ end
55
+
56
+ # Association is "also known as"
57
+ aka = options[:aka]
58
+ alias_method aka, name if aka
59
+ end
60
+ end
61
+
62
+ def with_memoization(name)
63
+ # NOTE: We use `instance_variables.include?` instead of `defined?`.
64
+ # Later it will always evaluate to `true` because it's an expression.
65
+ if instance_variables.include?(:"@#{name}")
66
+ return instance_variable_get(:"@#{name}")
67
+ end
68
+
69
+ memoizable = yield
70
+ instance_variable_set(:"@#{name}", memoizable)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ # Get a valid token or session HTTP request header for IronBank
5
+ #
6
+ class Authentication
7
+ extend Forwardable
8
+
9
+ attr_reader :session
10
+
11
+ def_delegators :session, :header, :expired?
12
+
13
+ def initialize(params)
14
+ @auth_type = params.delete(:auth_type)
15
+ @params = params
16
+ create_session
17
+ end
18
+
19
+ def create_session
20
+ @session = adapter.new(params)
21
+ end
22
+ alias renew_session create_session
23
+
24
+ private
25
+
26
+ attr_reader :auth_type, :params
27
+
28
+ def adapter
29
+ @adapter ||=
30
+ if auth_type == 'cookie'
31
+ IronBank::Authentications::Cookie
32
+ else
33
+ IronBank::Authentications::Token
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Authentications
5
+ # Get a cookie to enable authenticated calls to Zuora through Session.
6
+ #
7
+ class Cookie
8
+ include IronBank::OpenTracing
9
+
10
+ TEN_MINUTES = 600
11
+ ONE_HOUR = 3600
12
+
13
+ def initialize(client_id:, client_secret:, base_url:)
14
+ @client_id = client_id
15
+ @client_secret = client_secret
16
+ @base_url = base_url
17
+ fetch_cookie
18
+ end
19
+
20
+ def expired?
21
+ # Ten minutes as a margin of error in order to renew token in time
22
+ expires_at < Time.now + TEN_MINUTES
23
+ end
24
+
25
+ def header
26
+ { 'Cookie' => use }
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :client_id, :client_secret, :base_url, :cookie, :zsession,
32
+ :expires_at
33
+
34
+ def use
35
+ refetch_cookie if expired?
36
+ zsession
37
+ end
38
+
39
+ def fetch_cookie
40
+ response = authenticate
41
+ @cookie = response.headers['set-cookie']
42
+ @zsession = fetch_zsession
43
+ @expires_at = Time.now + ONE_HOUR
44
+ end
45
+ alias refetch_cookie fetch_cookie
46
+
47
+ def fetch_zsession
48
+ /ZSession=([^\;]+)/.match(cookie)[0]
49
+ end
50
+
51
+ def authenticate
52
+ connection.post('v1/connections', {})
53
+ end
54
+
55
+ def connection
56
+ @connection ||= Faraday.new(faraday_config) do |conn|
57
+ conn.use :ddtrace, open_tracing_options if open_tracing_enabled?
58
+ conn.request :url_encoded
59
+ conn.response :logger, IronBank.logger
60
+ conn.response :json
61
+ conn.adapter Faraday.default_adapter
62
+ end
63
+ end
64
+
65
+ def faraday_config
66
+ {
67
+ url: base_url,
68
+ headers: authentication_headers
69
+ }
70
+ end
71
+
72
+ def authentication_headers
73
+ {
74
+ apiaccesskeyid: client_id,
75
+ apisecretaccesskey: client_secret
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Authentications
5
+ # Get a bearer token to enable authenticated calls to Zuora through OAuth.
6
+ #
7
+ class Token
8
+ include IronBank::OpenTracing
9
+
10
+ # Generic token error.
11
+ #
12
+ class Error < StandardError; end
13
+
14
+ # Thrown when the access_token is not valid.
15
+ #
16
+ class InvalidAccessToken < Error; end
17
+
18
+ TEN_MINUTES = 600
19
+ ONE_HOUR = 3600
20
+
21
+ def initialize(client_id:, client_secret:, base_url:)
22
+ @client_id = client_id
23
+ @client_secret = client_secret
24
+ @base_url = base_url
25
+ fetch_token
26
+ end
27
+
28
+ def expired?
29
+ # Ten minutes as a margin of error in order to renew token in time
30
+ expires_at < Time.now + TEN_MINUTES
31
+ end
32
+
33
+ def header
34
+ { 'Authorization' => "Bearer #{use}" }
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :client_id, :client_secret, :base_url, :access_token,
40
+ :expires_at
41
+
42
+ def use
43
+ refetch_token if expired?
44
+ access_token
45
+ end
46
+
47
+ def fetch_token
48
+ response = authenticate || {}
49
+ @access_token = response.fetch('access_token', nil)
50
+ @expires_at = Time.now + response.fetch('expires_in', ONE_HOUR).to_i
51
+ validate_access_token
52
+ end
53
+ alias refetch_token fetch_token
54
+
55
+ def authenticate
56
+ connection.post('/oauth/token', authentication_params).body
57
+ end
58
+
59
+ def connection
60
+ @connection ||= Faraday.new(url: base_url) do |conn|
61
+ conn.use :ddtrace, open_tracing_options if open_tracing_enabled?
62
+ conn.request :url_encoded
63
+ conn.response :logger, IronBank.logger
64
+ conn.response :json
65
+ conn.adapter Faraday.default_adapter
66
+ end
67
+ end
68
+
69
+ def authentication_params
70
+ {
71
+ client_id: client_id,
72
+ client_secret: client_secret,
73
+ grant_type: 'client_credentials'
74
+ }
75
+ end
76
+
77
+ def validate_access_token
78
+ raise InvalidAccessToken, access_token unless access_token
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ # Use the provided cache if present.
5
+ #
6
+ module Cacheable
7
+ def reload
8
+ remove_instance_vars
9
+ @remote = self.class.find(id, force: true).remote
10
+ end
11
+
12
+ private
13
+
14
+ def remove_instance_vars
15
+ # Substract predefined variables from the instance variables
16
+ (instance_variables - [:@remote]).each do |var|
17
+ remove_instance_variable(:"#{var}")
18
+ end
19
+ end
20
+
21
+ def cache
22
+ self.class.cache
23
+ end
24
+
25
+ # Override queryable class methods to use cache if present.
26
+ #
27
+ module ClassMethods
28
+ def find(id, force: false)
29
+ return super(id) unless cache
30
+
31
+ cache.fetch(id, force: force) { super(id) }
32
+ end
33
+
34
+ def where(conditions)
35
+ # Conditions can be empty when called from #all, it does not make sense
36
+ # to try to cache all records returned then.
37
+ return super if conditions.empty?
38
+
39
+ return super unless cache
40
+
41
+ # Ensure we are not colliding when the conditions are similar for two
42
+ # different resources, like Account and Subscription.
43
+ cache_key = conditions.merge(object_name: name)
44
+ cache.fetch(cache_key) { super }
45
+ end
46
+
47
+ def cache
48
+ IronBank.configuration.cache
49
+ end
50
+ end
51
+ end
52
+ end