iron_bank 0.1.0 → 0.7.1

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 (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