iron_bank 3.4.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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