iron_bank 3.4.0 → 4.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 195ba10f5c39626868e1d281972ff45e4618f1e72b0d87684aa9c65d1ed1fa10
4
- data.tar.gz: acd56eae404df5027aceeb8081b542da811178a71ff56924531b4d2280fa045d
3
+ metadata.gz: ddab957161d110b39c87104391a5883a5e22091186a0e368ebcd8b03d8d7b4ad
4
+ data.tar.gz: 372c3ecc6be5c37ff50cf08085451244df1c49fa1dae6de9ed4301de18c05511
5
5
  SHA512:
6
- metadata.gz: 4db1e53ef118e2b496a1ae41ea0f880458386767e57a562de235f0e734416df59f441c7f1928aca06cfa3904c49623985a794f5b3bf72121391488c2dc1a1da4
7
- data.tar.gz: 25c9457c4716177d3a21d0f6b993a433558a323a6ba8f63e4cca881660ddca92408a16226d6ae4e42f05f6d280c6340c42c2667a8a2c131afc0a3adee9acd05d
6
+ metadata.gz: adda95d1a9f68e9aa4819244ad322b8b71b8ae19596b8c48ee704a559c835c1646fe4affd9d0b3bafac275641b9fc1f581be7887606657e0d8268c4c08f8303d
7
+ data.tar.gz: a069124becac1289cb2e43e58ae7cf8fc47727a5b0b7860551c46ff6d6531d48e145eab296884bcc1a3a33df7da0660e3bbea8f1309a8d8c404021b7828f8d66
data/.env.example CHANGED
@@ -3,3 +3,4 @@ ZUORA_CLIENT_SECRET=secret
3
3
  ZUORA_AUTH_TYPE=token
4
4
  ZUORA_DOMAIN=rest.apisandbox.zuora.com
5
5
  ZUORA_TENANT_ID=00000
6
+ ZUORA_EXCLUDED_FIELDS_FILE=excluded_fields.yml
data/.gitignore CHANGED
@@ -15,3 +15,6 @@
15
15
 
16
16
  # byebug history
17
17
  .byebug_history
18
+
19
+ # do not version specific excluded fields
20
+ excluded_fields.yml
data/.reek.yml CHANGED
@@ -13,6 +13,7 @@ detectors:
13
13
  - IronBank::Configuration#client_id
14
14
  - IronBank::Configuration#client_secret
15
15
  - IronBank::Configuration#domain
16
+ - IronBank::Configuration#excluded_fields
16
17
  - IronBank::Configuration#export_directory
17
18
  - IronBank::Configuration#logger
18
19
  - IronBank::Configuration#middlewares
@@ -54,13 +55,14 @@ detectors:
54
55
 
55
56
  NestedIterators:
56
57
  exclude:
57
- - IronBank::Schema#self.import
58
+ - IronBank::Authentications::Cookie#connection
59
+ - IronBank::Authentications::Token#connection
60
+ - IronBank::Client#connection
61
+ - IronBank::Describe::ExcludedFields#remove_last_failure_fields
58
62
  - IronBank::Local#where
59
63
  - IronBank::LocalRecords#export
60
64
  - IronBank::Resources::ProductRatePlanChargeTier#self.load_records
61
- - IronBank::Client#connection
62
- - IronBank::Authentications::Token#connection
63
- - IronBank::Authentications::Cookie#connection
65
+ - IronBank::Schema#self.import
64
66
 
65
67
  NilCheck:
66
68
  enabled: false
@@ -71,22 +73,16 @@ detectors:
71
73
  - IronBank::Configuration
72
74
 
73
75
  TooManyStatements:
76
+ max_statements: 10
74
77
  exclude:
75
- - IronBank::Associations::ClassMethods#with_many
76
- - IronBank::Associations::ClassMethods#with_one
77
- - IronBank::Authentications::Cookie#connection
78
- - IronBank::Authentications::Token#connection
79
78
  - IronBank::Client#connection
80
- - IronBank::Configuration#schema_directory=
81
- - IronBank::Configuration#initialize
82
- - IronBank::Error#message_from_body
83
- - IronBank::Queryable#find_each
84
- - IronBank::Queryable#where
85
79
  - IronBank::Utils#camelize
86
- - IronBank::Utils#underscore
87
- - IronBank::Resources::ProductRatePlanChargeTier#self.load_records
88
80
  - IronBank::Error#self.from_response
89
81
 
82
+ UncommunicativeVariableName:
83
+ exclude:
84
+ - IronBank::Describe::ExcludedFields#valid_query?
85
+
90
86
  UtilityFunction:
91
87
  exclude:
92
88
  - IronBank::Cacheable::ClassMethods#cache
data/.rubocop.yml CHANGED
@@ -56,6 +56,10 @@ Style/OptionalArguments:
56
56
  - lib/iron_bank/queryable.rb
57
57
  - lib/iron_bank/resource.rb
58
58
 
59
+ Style/RegexpLiteral:
60
+ Exclude:
61
+ - lib/iron_bank/describe/excluded_fields.rb
62
+
59
63
  Style/RescueStandardError:
60
64
  Exclude:
61
65
  - lib/iron_bank/csv.rb
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- iron_bank (3.4.0)
4
+ iron_bank (4.0.0)
5
5
  faraday (~> 0)
6
6
  faraday_middleware (~> 0)
7
7
  nokogiri (~> 1)
data/README.md CHANGED
@@ -132,7 +132,7 @@ https://github.com/zendesk/iron_bank.
132
132
 
133
133
  - `AutoPay` field on the `Invoice` object is not queryable using ZOQL despite
134
134
  the metadata showing `<selectable>true</selectable>`, hence it has been added
135
- to the `exclude_fields` method.
135
+ to the `excluded_fields` method.
136
136
  - Exporting the local records through `IronBank::LocalRecords.export` can take a
137
137
  long time (especially the `ProductRatePlanChargeTier` records). In case the
138
138
  task fails because the export is still processing, you can manually download
data/Rakefile CHANGED
@@ -21,16 +21,31 @@ task default: %i[rubocop reek spec]
21
21
 
22
22
  desc "Export the Zuora Schema using the Describe API"
23
23
  task :export_schema do
24
+ setup_iron_bank
25
+ IronBank::Schema.export
26
+ end
27
+
28
+ desc "Query Zuora for fields that we should not use in a query"
29
+ task :excluded_fields, [:filename] do |_t, args|
30
+ require "psych"
31
+ setup_iron_bank
32
+
33
+ destination = args[:filename] || IronBank.configuration.excluded_fields_file
34
+ fields = IronBank::Schema.excluded_fields
35
+
36
+ File.open(destination, "w") { |file| file.write(Psych.dump(fields)) }
37
+ end
38
+
39
+ # Helper function to set up an `IronBank::Client` instance
40
+ def setup_iron_bank
24
41
  require "dotenv/load"
25
42
  require "iron_bank"
26
43
 
27
- # Set up the client
28
- IronBank.client = IronBank::Client.new(
29
- domain: ENV["ZUORA_DOMAIN"],
30
- client_id: ENV["ZUORA_CLIENT_ID"],
31
- client_secret: ENV["ZUORA_CLIENT_SECRET"],
32
- auth_type: ENV["ZUORA_AUTH_TYPE"]
33
- )
34
-
35
- IronBank::Schema.export
44
+ IronBank.configure do |config|
45
+ config.client_id = ENV["ZUORA_CLIENT_ID"]
46
+ config.client_secret = ENV["ZUORA_CLIENT_SECRET"]
47
+ config.auth_type = ENV.fetch("ZUORA_AUTH_TYPE", "token")
48
+ config.domain = ENV["ZUORA_DOMAIN"]
49
+ config.excluded_fields_file = ENV["ZUORA_EXCLUDED_FIELDS_FILE"]
50
+ end
36
51
  end
data/bin/console CHANGED
@@ -6,14 +6,12 @@ require "bundler/setup"
6
6
  require "iron_bank"
7
7
  require "pry-byebug"
8
8
 
9
- credentials = {
10
- domain: ENV["ZUORA_DOMAIN"],
11
- client_id: ENV["ZUORA_CLIENT_ID"],
12
- client_secret: ENV["ZUORA_CLIENT_SECRET"],
13
- auth_type: ENV["ZUORA_AUTH_TYPE"]
14
- }
15
-
16
- # If credentials are present in environment variables
17
- IronBank.client = IronBank::Client.new(credentials) if credentials.values.all?
9
+ IronBank.configure do |config|
10
+ config.client_id = ENV["ZUORA_CLIENT_ID"]
11
+ config.client_secret = ENV["ZUORA_CLIENT_SECRET"]
12
+ config.auth_type = ENV.fetch("ZUORA_AUTH_TYPE", "token")
13
+ config.domain = ENV["ZUORA_DOMAIN"]
14
+ config.excluded_fields_file = ENV["ZUORA_EXCLUDED_FIELDS_FILE"]
15
+ end
18
16
 
19
17
  Pry.start
@@ -25,6 +25,9 @@ module IronBank
25
25
  # Cache store instance, optionally used by certain resources.
26
26
  attr_accessor :cache
27
27
 
28
+ # File path for excluded fields (when querying using ZOQL)
29
+ attr_accessor :excluded_fields_file
30
+
28
31
  # Directory where the XML describe files are located.
29
32
  attr_reader :schema_directory
30
33
 
@@ -78,5 +81,21 @@ module IronBank
78
81
  def retry_options
79
82
  @retry_options ||= IronBank::Client::DEFAULT_RETRY_OPTIONS
80
83
  end
84
+
85
+ def excluded_fields
86
+ return {} unless excluded_fields_file
87
+
88
+ unless File.exist?(excluded_fields_file)
89
+ IronBank.logger.warn "File does not exist: #{excluded_fields_file}"
90
+
91
+ return {}
92
+ end
93
+
94
+ @excluded_fields ||= begin
95
+ Psych.load_file(excluded_fields_file).tap do |fields|
96
+ raise "Excluded fields must be a hash" unless fields.is_a?(Hash)
97
+ end
98
+ end
99
+ end
81
100
  end
82
101
  end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Describe
5
+ # Returns an array of non-queryable fields for the given object in the
6
+ # current Zuora tenant, despites Zuora clearly marking these fields as
7
+ # `<selectable>true</true>` in their Describe API... /rant
8
+ #
9
+ class ExcludedFields
10
+ extend Forwardable
11
+
12
+ FAULT_FIELD_MESSAGES = Regexp.union(
13
+ # Generic fault field
14
+ /invalid field for query: \w+\.(\w+)/,
15
+ # Invoice Bill Run is not selectable in ZOQL
16
+ /Cannot use the (BillRunId) field in the select clause/,
17
+ # Invoice Body, implemented as a separate call
18
+ /Can only query one invoice (body) at a time/,
19
+ # Catalog tier should only query the price field
20
+ /use Price or (DiscountAmount) or (DiscountPercentage)/,
21
+ # Catalog plan currencies, implemented as a separate call
22
+ /When querying for (active currencies)/,
23
+ # Catalog charge rollover balance
24
+ /You can only query (RolloverBalance) in particular/,
25
+ # (Subscription) charge should only query the price field
26
+ %r{
27
+ (OveragePrice),
28
+ \ Price,
29
+ \ (IncludedUnits),
30
+ \ (DiscountAmount)
31
+ \ or\ (DiscountPercentage)
32
+ }x
33
+ ).freeze
34
+
35
+ private_class_method :new
36
+
37
+ def self.call(object_name:)
38
+ new(object_name).call
39
+ end
40
+
41
+ def call
42
+ remove_last_failure_fields until valid_query?
43
+
44
+ excluded_fields
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :object_name, :excluded_fields, :last_failed_fields
50
+
51
+ def_delegators "IronBank.logger", :info
52
+
53
+ def initialize(object_name)
54
+ @object_name = object_name
55
+ @excluded_fields = []
56
+ @last_failed_fields = nil
57
+ end
58
+
59
+ def object
60
+ IronBank::Resources.const_get(object_name)
61
+ end
62
+
63
+ def remove_last_failure_fields
64
+ query_fields = object.query_fields
65
+
66
+ failed_fields = query_fields.select do |field|
67
+ last_failed_fields.any? { |failed| field.casecmp?(failed) }
68
+ end
69
+
70
+ @excluded_fields += failed_fields
71
+
72
+ # Remove the field for the next query
73
+ query_fields.delete_if { |field| failed_fields.include?(field) }
74
+ end
75
+
76
+ def valid_query?
77
+ object.first
78
+ info "Successful query for #{object_name}"
79
+
80
+ true
81
+ rescue IronBank::InternalServerError => e
82
+ @last_failed_fields = extract_fields_from_exception(e)
83
+
84
+ false
85
+ end
86
+
87
+ def extract_fields_from_exception(exception)
88
+ message = exception.message
89
+
90
+ unless FAULT_FIELD_MESSAGES.match(message)
91
+ raise "Could not parse error message: #{message}"
92
+ end
93
+
94
+ failed_fields = Regexp.last_match.
95
+ captures.
96
+ compact.
97
+ map { |capture| capture.delete(" ") }
98
+
99
+ info "Invalid fields '#{failed_fields}' for #{object_name} query"
100
+
101
+ failed_fields
102
+ end
103
+ end
104
+ end
105
+ end
@@ -4,22 +4,24 @@ module IronBank
4
4
  # Metadata to provide accessors to Zuora resources.
5
5
  #
6
6
  module Metadata
7
- # Can be overriden to exclude specific fields for a given resource, see
8
- # `Account` class for an example
9
- def exclude_fields
10
- []
7
+ def excluded_fields
8
+ return [] unless (fields = IronBank.configuration.excluded_fields)
9
+
10
+ # Return the field for the given resource name
11
+ # (where the module is extended from)
12
+ fields.fetch(object_name, [])
11
13
  end
12
14
 
13
15
  def fields
14
16
  return [] unless schema
15
17
 
16
- @fields ||= schema.fields.map(&:name) - exclude_fields
18
+ @fields ||= schema.fields.map(&:name) - excluded_fields
17
19
  end
18
20
 
19
21
  def query_fields
20
22
  return [] unless schema
21
23
 
22
- @query_fields ||= schema.query_fields - exclude_fields
24
+ @query_fields ||= schema.query_fields - excluded_fields
23
25
  end
24
26
 
25
27
  def schema
@@ -9,18 +9,6 @@ module IronBank
9
9
  # method (usually a credit card, but PayPal is also accepted by Zuora).
10
10
  #
11
11
  class Account < Resource
12
- # Tenants without credit memo activated cannot query these fields BUT they
13
- # are still described as `selectable` through the metadata.
14
- #
15
- # Similarly, accounts with `TaxExemptStatus` set to `No` cannot query
16
- # the `TaxExemptEntityUseCode` related fields.
17
- def self.exclude_fields
18
- %w[
19
- TaxExemptEntityUseCode
20
- TotalDebitMemoBalance
21
- UnappliedCreditMemoAmount
22
- ]
23
- end
24
12
  with_schema
25
13
 
26
14
  # Contacts
@@ -7,6 +7,7 @@ module IronBank
7
7
  #
8
8
  class Amendment < Resource
9
9
  with_schema
10
+
10
11
  with_one :subscription
11
12
  end
12
13
  end
@@ -7,6 +7,7 @@ module IronBank
7
7
  #
8
8
  class Contact < Resource
9
9
  with_schema
10
+
10
11
  with_one :account
11
12
  end
12
13
  end
@@ -6,6 +6,7 @@ module IronBank
6
6
  #
7
7
  class Import < Resource
8
8
  with_schema
9
+
9
10
  with_many :usages
10
11
  end
11
12
  end
@@ -6,19 +6,11 @@ module IronBank
6
6
  # holds many invoice items.
7
7
  #
8
8
  class Invoice < Resource
9
- # These fields are declared as `<selectable>true</selectable>` but Zuora
10
- # returns a QueryError when trying to query an invoice with them. Also,
11
- # the `Body` field can only be retrieved for a single invoice at a time.
12
- def self.exclude_fields
13
- %w[
14
- AutoPay
15
- BillRunId
16
- BillToContactSnapshotId
17
- Body
18
- RegenerateInvoicePDF
19
- SoldToContactSnapshotId
20
- ]
9
+ # See the comment for the instance method `#body`
10
+ def self.excluded_fields
11
+ super + %w[Body]
21
12
  end
13
+
22
14
  with_schema
23
15
 
24
16
  with_one :account
@@ -6,6 +6,7 @@ module IronBank
6
6
  #
7
7
  class InvoiceAdjustment < Resource
8
8
  with_schema
9
+
9
10
  with_one :account
10
11
  with_one :invoice
11
12
  end
@@ -5,12 +5,6 @@ module IronBank
5
5
  # An invoice item holds a charge that is billed to a customer.
6
6
  #
7
7
  class InvoiceItem < Resource
8
- def self.exclude_fields
9
- %w[
10
- AppliedToChargeNumber
11
- Balance
12
- ]
13
- end
14
8
  with_schema
15
9
 
16
10
  with_one :invoice
@@ -6,6 +6,7 @@ module IronBank
6
6
  #
7
7
  class InvoicePayment < Resource
8
8
  with_schema
9
+
9
10
  with_one :invoice
10
11
  with_one :payment
11
12
  end
@@ -8,6 +8,7 @@ module IronBank
8
8
  #
9
9
  class PaymentMethod < Resource
10
10
  with_schema
11
+
11
12
  with_one :account
12
13
  end
13
14
  end
@@ -8,6 +8,7 @@ module IronBank
8
8
  with_schema
9
9
  with_local_records
10
10
  with_cache
11
+
11
12
  with_many :product_rate_plans, alias: :plans
12
13
  end
13
14
  end
@@ -6,10 +6,10 @@ module IronBank
6
6
  # charges. It represents what a customer is subscribing to.
7
7
  #
8
8
  class ProductRatePlan < Resource
9
- # Zuora limitation: we cannot query for `ActiveCurrencies` with any other
10
- # fields, so instead we define a separate `#active_currencies` method.
11
- def self.exclude_fields
12
- %w[ActiveCurrencies]
9
+ # NOTE: Zuora doesn't let us query for more than one product rate plan
10
+ # `ActiveCurrencies` at a time
11
+ def self.excluded_fields
12
+ super + %w[ActiveCurrencies]
13
13
  end
14
14
 
15
15
  with_schema
@@ -10,11 +10,6 @@ module IronBank
10
10
  # is *at least* one tier per active currency.
11
11
  #
12
12
  class ProductRatePlanCharge < Resource
13
- # `DiscountClass` seems to not be a queryable field, despite the result
14
- # from the describe call for product rate plan charge.
15
- def self.exclude_fields
16
- %w[DiscountClass]
17
- end
18
13
  with_schema
19
14
  with_local_records
20
15
  with_cache
@@ -6,20 +6,6 @@ module IronBank
6
6
  # charge.
7
7
  #
8
8
  class ProductRatePlanChargeTier < Resource
9
- # These fields are declared as `<selectable>true</selectable>` but only
10
- # for the `export` context (ie., via ZOQL export). To let live queries go
11
- # through when the local records are not exported, we only allow querying
12
- # the non-discount tiers.
13
- def self.exclude_fields
14
- %w[
15
- Active
16
- IncludedUnits
17
- OveragePrice
18
- DiscountAmount
19
- DiscountPercentage
20
- ]
21
- end
22
-
23
9
  with_schema
24
10
  with_local_records
25
11
  with_cache
@@ -5,18 +5,10 @@ module IronBank
5
5
  # A rate plan charge belongs to a subscription rate plan.
6
6
  #
7
7
  class RatePlanCharge < Resource
8
- def self.exclude_fields
9
- %w[
10
- DiscountAmount
11
- DiscountClass
12
- DiscountPercentage
13
- IncludedUnits
14
- OveragePrice
15
- Price
16
- RevenueRecognitionRuleName
17
- RolloverBalance
18
- ]
8
+ def self.excluded_fields
9
+ super + %w[RolloverBalance]
19
10
  end
11
+
20
12
  with_schema
21
13
  with_cache
22
14
 
@@ -25,6 +17,10 @@ module IronBank
25
17
  with_one :rate_plan, alias: :plan
26
18
 
27
19
  with_many :rate_plan_charge_tiers, alias: :tiers
20
+
21
+ def rollover_balance
22
+ remote[:rollover_balance] || reload.remote[:rollover_balance]
23
+ end
28
24
  end
29
25
  end
30
26
  end
@@ -6,17 +6,6 @@ module IronBank
6
6
  # billed for, in his/her currency.
7
7
  #
8
8
  class RatePlanChargeTier < Resource
9
- # Excluding 'DiscountAmount' & 'DiscountPercentage', as Zuora stores these
10
- # values in the 'Price' field.
11
- def self.exclude_fields
12
- %w[
13
- Currency
14
- DiscountAmount
15
- DiscountPercentage
16
- IncludedUnits
17
- OveragePrice
18
- ]
19
- end
20
9
  with_schema
21
10
  with_cache
22
11
 
@@ -6,13 +6,6 @@ module IronBank
6
6
  # item.
7
7
  #
8
8
  class TaxationItem < Resource
9
- def self.exclude_fields
10
- %w[
11
- Balance
12
- CreditAmount
13
- PaymentAmount
14
- ]
15
- end
16
9
  with_schema
17
10
 
18
11
  with_one :invoice
@@ -34,6 +34,15 @@ module IronBank
34
34
  @import = nil
35
35
  end
36
36
 
37
+ def self.excluded_fields
38
+ @excluded_fields ||= begin
39
+ IronBank::Resources.constants.each.with_object({}) do |resource, fields|
40
+ fields[resource.to_s] =
41
+ IronBank::Describe::ExcludedFields.call(object_name: resource)
42
+ end
43
+ end
44
+ end
45
+
37
46
  def export
38
47
  tenant.objects.compact.each do |object|
39
48
  begin
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IronBank
4
- VERSION = "3.4.0"
4
+ VERSION = "4.0.0"
5
5
  API_VERSION = "v1"
6
6
  end
data/lib/iron_bank.rb CHANGED
@@ -80,6 +80,7 @@ require "iron_bank/authentications/cookie"
80
80
  require "iron_bank/authentications/token"
81
81
  require "iron_bank/client"
82
82
  require "iron_bank/describe/field"
83
+ require "iron_bank/describe/excluded_fields"
83
84
  require "iron_bank/describe/object"
84
85
  require "iron_bank/describe/related"
85
86
  require "iron_bank/describe/tenant"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iron_bank
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mickael Pham
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2019-09-03 00:00:00.000000000 Z
14
+ date: 2019-09-05 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bump
@@ -287,6 +287,7 @@ files:
287
287
  - lib/iron_bank/collection.rb
288
288
  - lib/iron_bank/configuration.rb
289
289
  - lib/iron_bank/csv.rb
290
+ - lib/iron_bank/describe/excluded_fields.rb
290
291
  - lib/iron_bank/describe/field.rb
291
292
  - lib/iron_bank/describe/object.rb
292
293
  - lib/iron_bank/describe/related.rb