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.
- checksums.yaml +5 -5
- data/.env.example +7 -0
- data/.github/CODEOWNERS +1 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +20 -0
- data/.gitignore +6 -1
- data/.reek +95 -0
- data/.rspec +1 -2
- data/.rubocop.yml +55 -0
- data/.travis.yml +22 -3
- data/Gemfile +3 -3
- data/LICENSE +176 -0
- data/README.md +133 -0
- data/Rakefile +39 -3
- data/bin/console +13 -1
- data/bin/setup +1 -0
- data/iron_bank.gemspec +34 -10
- data/lib/generators/iron_bank/install/install_generator.rb +20 -0
- data/lib/generators/iron_bank/install/templates/README +16 -0
- data/lib/generators/iron_bank/install/templates/iron_bank.rb +29 -0
- data/lib/iron_bank/action.rb +39 -0
- data/lib/iron_bank/actions/amend.rb +16 -0
- data/lib/iron_bank/actions/create.rb +19 -0
- data/lib/iron_bank/actions/delete.rb +19 -0
- data/lib/iron_bank/actions/execute.rb +21 -0
- data/lib/iron_bank/actions/generate.rb +19 -0
- data/lib/iron_bank/actions/query.rb +16 -0
- data/lib/iron_bank/actions/query_more.rb +21 -0
- data/lib/iron_bank/actions/subscribe.rb +32 -0
- data/lib/iron_bank/actions/update.rb +19 -0
- data/lib/iron_bank/associations.rb +73 -0
- data/lib/iron_bank/authentication.rb +37 -0
- data/lib/iron_bank/authentications/cookie.rb +80 -0
- data/lib/iron_bank/authentications/token.rb +82 -0
- data/lib/iron_bank/cacheable.rb +52 -0
- data/lib/iron_bank/client.rb +96 -0
- data/lib/iron_bank/collection.rb +31 -0
- data/lib/iron_bank/configuration.rb +86 -0
- data/lib/iron_bank/csv.rb +29 -0
- data/lib/iron_bank/describe/field.rb +65 -0
- data/lib/iron_bank/describe/object.rb +81 -0
- data/lib/iron_bank/describe/related.rb +40 -0
- data/lib/iron_bank/describe/tenant.rb +50 -0
- data/lib/iron_bank/endpoint.rb +36 -0
- data/lib/iron_bank/error.rb +45 -0
- data/lib/iron_bank/instrumentation.rb +14 -0
- data/lib/iron_bank/local.rb +72 -0
- data/lib/iron_bank/local_records.rb +52 -0
- data/lib/iron_bank/logger.rb +23 -0
- data/lib/iron_bank/metadata.rb +36 -0
- data/lib/iron_bank/object.rb +89 -0
- data/lib/iron_bank/open_tracing.rb +17 -0
- data/lib/iron_bank/operation.rb +33 -0
- data/lib/iron_bank/operations/billing_preview.rb +16 -0
- data/lib/iron_bank/query_builder.rb +72 -0
- data/lib/iron_bank/queryable.rb +55 -0
- data/lib/iron_bank/resource.rb +70 -0
- data/lib/iron_bank/resources/account.rb +62 -0
- data/lib/iron_bank/resources/amendment.rb +13 -0
- data/lib/iron_bank/resources/catalog_tiers/discount_amount.rb +26 -0
- data/lib/iron_bank/resources/catalog_tiers/discount_percentage.rb +26 -0
- data/lib/iron_bank/resources/catalog_tiers/price.rb +26 -0
- data/lib/iron_bank/resources/contact.rb +13 -0
- data/lib/iron_bank/resources/export.rb +11 -0
- data/lib/iron_bank/resources/import.rb +12 -0
- data/lib/iron_bank/resources/invoice.rb +37 -0
- data/lib/iron_bank/resources/invoice_adjustment.rb +13 -0
- data/lib/iron_bank/resources/invoice_item.rb +25 -0
- data/lib/iron_bank/resources/invoice_payment.rb +13 -0
- data/lib/iron_bank/resources/payment.rb +17 -0
- data/lib/iron_bank/resources/payment_method.rb +14 -0
- data/lib/iron_bank/resources/product.rb +14 -0
- data/lib/iron_bank/resources/product_rate_plan.rb +32 -0
- data/lib/iron_bank/resources/product_rate_plan_charge.rb +27 -0
- data/lib/iron_bank/resources/product_rate_plan_charge_tier.rb +60 -0
- data/lib/iron_bank/resources/rate_plan.rb +21 -0
- data/lib/iron_bank/resources/rate_plan_charge.rb +30 -0
- data/lib/iron_bank/resources/rate_plan_charge_tier.rb +26 -0
- data/lib/iron_bank/resources/subscription.rb +28 -0
- data/lib/iron_bank/resources/usage.rb +16 -0
- data/lib/iron_bank/response/raise_error.rb +16 -0
- data/lib/iron_bank/schema.rb +58 -0
- data/lib/iron_bank/utils.rb +59 -0
- data/lib/iron_bank/version.rb +4 -1
- data/lib/iron_bank.rb +152 -2
- metadata +300 -12
data/iron_bank.gemspec
CHANGED
@@ -1,15 +1,23 @@
|
|
1
|
-
|
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.
|
9
|
-
spec.
|
10
|
-
spec.
|
11
|
-
spec.
|
12
|
-
|
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 '
|
23
|
-
spec.add_development_dependency '
|
24
|
-
spec.add_development_dependency '
|
25
|
-
spec.add_development_dependency '
|
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
|