iron_bank 5.4.0 → 5.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 853859a45aaabdcfc831dca27a70882b28aa19f68b214a666cb0830ce4fa702f
4
- data.tar.gz: 3c755dd00fad7455b402dd04ae7f649f554fce6127608e11f28c2e16f31c85d9
3
+ metadata.gz: c5f447a2b12293785811cd3e1f6d170e4ff3186b7de4ce4cac6d12b4322171a4
4
+ data.tar.gz: '068e99b0fc9fb71eaa3adfa252bf455d2622579feaa6ee1893922d7667130e01'
5
5
  SHA512:
6
- metadata.gz: 50f8865e78282701c705fd9e5b1b0f9978f152d54b7594784806153d9ffc3654d57871f6a9c4dbe1917611853915e27ff16e9b893e2bc2d848a27f963596aa32
7
- data.tar.gz: 41e18fea0a4047734d49c8a495dfb28c2c13b7a40ef4a4737f6ccf909e4894bca44048124793cb9924ed375000e12245be67eefe559967f943b1391f7f2024f3
6
+ metadata.gz: 1daac2047a0798f1471aeeb5872830f413b00135b25fd88f325ffe6b882d192cd9ab30df2bfe2971b3fd8409283935ff5075da6712bad2dd6b79f2c7bc6a6a34
7
+ data.tar.gz: b09ddb6a6873b5564ba6f552a24228e2e8cb951479125e6605580db0ed1780d8370e5b215084a5217a8f481ddc8fc92a7ea59a262b00ff71252948d3bbeb4d23
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- iron_bank (5.4.0)
4
+ iron_bank (5.4.1)
5
5
  faraday (~> 1)
6
6
  faraday_middleware (~> 1)
7
7
  nokogiri (~> 1)
@@ -9,19 +9,29 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- activesupport (7.0.4.3)
12
+ activesupport (7.1.1)
13
+ base64
14
+ bigdecimal
13
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ connection_pool (>= 2.2.5)
17
+ drb
14
18
  i18n (>= 1.6, < 2)
15
19
  minitest (>= 5.1)
20
+ mutex_m
16
21
  tzinfo (~> 2.0)
17
22
  ast (2.4.2)
23
+ base64 (0.2.0)
24
+ bigdecimal (3.1.4)
18
25
  bump (0.10.0)
19
26
  byebug (11.1.3)
20
27
  coderay (1.1.3)
21
28
  concurrent-ruby (1.2.2)
29
+ connection_pool (2.4.1)
22
30
  diff-lcs (1.5.0)
23
31
  docile (1.4.0)
24
32
  dotenv (2.8.1)
33
+ drb (2.2.0)
34
+ ruby2_keywords
25
35
  factory_bot (6.2.1)
26
36
  activesupport (>= 5.0.0)
27
37
  faraday (1.10.3)
@@ -57,6 +67,7 @@ GEM
57
67
  mini_portile2 (2.8.2)
58
68
  minitest (5.18.0)
59
69
  multipart-post (2.3.0)
70
+ mutex_m (0.2.0)
60
71
  nokogiri (1.14.3)
61
72
  mini_portile2 (~> 2.8.0)
62
73
  racc (~> 1.4)
@@ -141,4 +152,4 @@ DEPENDENCIES
141
152
  timecop (~> 0.9)
142
153
 
143
154
  BUNDLED WITH
144
- 2.3.16
155
+ 2.4.19
data/Rakefile CHANGED
@@ -31,6 +31,14 @@ task :excluded_fields, [:filename] do |_t, args|
31
31
  setup_iron_bank
32
32
 
33
33
  destination = args[:filename] || IronBank.configuration.excluded_fields_file
34
+ # NOTE: In some instances the error message from Zuora will not include the
35
+ # unqueryable fields that need to be excluded. When that happens IronBank's
36
+ # strategy will be to perform a binary search through the fields listed in the
37
+ # query -- at the cost of performance due to repeated requests sent to Zuora
38
+ # as it tries to identify the offending field.
39
+ #
40
+ # See:
41
+ # - https://github.com/zendesk/iron_bank/pull/107
34
42
  fields = IronBank::Schema.excluded_fields.sort.to_h
35
43
 
36
44
  File.write(destination, Psych.dump(fields))
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Describe
5
+ # NOTE: Beware there could be a performance hit as the search repeatedly
6
+ # executes queries to perform the search for invalid fields in the query
7
+ # included in the query statement.
8
+
9
+ # rubocop:disable Style/ClassAndModuleChildren
10
+ class ExcludedFields::DeduceFromQuery
11
+ INVALID_OBJECT_ID = "InvalidObjectId"
12
+
13
+ private_class_method :new
14
+
15
+ def self.call(object)
16
+ new(object).call
17
+ end
18
+
19
+ def call
20
+ query_fields = object.query_fields.clone
21
+ divide_and_execute(query_fields)
22
+ object.query_fields.concat(query_fields)
23
+ invalid_fields
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :object, :valid_fields, :invalid_fields
29
+
30
+ def initialize(object)
31
+ @object = object
32
+ @valid_fields = []
33
+ @invalid_fields = []
34
+ end
35
+
36
+ def divide_and_execute(query_fields)
37
+ # clear state before queries
38
+ object.query_fields.clear
39
+
40
+ # we repeat dividing until only a single field remains
41
+ invalid_fields.push(query_fields.pop) if query_fields.one?
42
+ return if query_fields.empty?
43
+
44
+ left, right = divide_fields(query_fields)
45
+ execute_or_divide_again(left)
46
+ execute_or_divide_again(right)
47
+ end
48
+
49
+ def divide_fields(query_fields)
50
+ mid = query_fields.size / 2
51
+ [query_fields[0..mid - 1], query_fields[mid..]]
52
+ end
53
+
54
+ def execute_or_divide_again(fields)
55
+ if execute_query(fields)
56
+ valid_fields.concat(fields)
57
+ else
58
+ divide_and_execute(fields)
59
+ end
60
+ end
61
+
62
+ def execute_query(fields)
63
+ object.query_fields.concat(valid_fields + fields)
64
+ object.where({ id: INVALID_OBJECT_ID })
65
+ true
66
+ rescue IronBank::InternalServerError
67
+ false
68
+ end
69
+ end
70
+ # rubocop:enable Style/ClassAndModuleChildren
71
+ end
72
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IronBank
4
+ module Describe
5
+ # Extracts invalid fields from an exception message.
6
+ # Returns from a call if an exception message does not contain invalid field
7
+
8
+ # rubocop:disable Style/ClassAndModuleChildren
9
+ class ExcludedFields::ExtractFromMessage
10
+ FAULT_FIELD_MESSAGES = Regexp.union(
11
+ # Generic fault field
12
+ /invalid field for query: \w+\.(\w+)/,
13
+ # Invoice Bill Run is not selectable in ZOQL
14
+ /Cannot use the (BillRunId) field in the select clause/,
15
+ # Invoice Body, implemented as a separate call
16
+ /Can only query one invoice (body) at a time/,
17
+ # Catalog tier should only query the price field
18
+ /use Price or (DiscountAmount) or (DiscountPercentage)/,
19
+ # Catalog plan currencies, implemented as a separate call
20
+ /When querying for (active currencies)/,
21
+ # Catalog charge rollover balance
22
+ /You can only query (RolloverBalance) in particular/,
23
+ # (Subscription) charge should only query the price field
24
+ /
25
+ (OveragePrice),
26
+ \ Price,
27
+ \ (IncludedUnits),
28
+ \ (DiscountAmount)
29
+ \ or\ (DiscountPercentage)
30
+ /x
31
+ ).freeze
32
+
33
+ private_class_method :new
34
+
35
+ def self.call(message)
36
+ new(message).call
37
+ end
38
+
39
+ def call
40
+ return unless FAULT_FIELD_MESSAGES.match(message)
41
+
42
+ Regexp.last_match.captures.compact.
43
+ map { |capture| capture.delete(" ") }
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :message
49
+
50
+ def initialize(message)
51
+ @message = message
52
+ end
53
+ end
54
+ # rubocop:enable Style/ClassAndModuleChildren
55
+ end
56
+ end
@@ -5,32 +5,10 @@ module IronBank
5
5
  # Returns an array of non-queryable fields for the given object in the
6
6
  # current Zuora tenant, despites Zuora clearly marking these fields as
7
7
  # `<selectable>true</true>` in their Describe API... /rant
8
- #
9
8
  class ExcludedFields
10
9
  extend Forwardable
11
10
 
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
11
+ INVALID_OBJECT_ID = "InvalidObjectId"
34
12
 
35
13
  private_class_method :new
36
14
 
@@ -39,43 +17,36 @@ module IronBank
39
17
  end
40
18
 
41
19
  def call
42
- remove_last_failure_fields until valid_query?
43
-
20
+ remove_invalid_fields until valid_query?
44
21
  (excluded_fields - single_resource_query_fields).sort
45
22
  end
46
23
 
47
24
  private
48
25
 
49
- INVALID_OBJECT_ID = "InvalidObjectId"
50
-
51
- attr_reader :object_name, :last_failed_fields
26
+ attr_reader :object_name, :invalid_fields
52
27
 
53
28
  def_delegators "IronBank.logger", :info
54
29
  def_delegators :object, :single_resource_query_fields
55
30
 
56
31
  def initialize(object_name)
57
- @object_name = object_name
58
- @last_failed_fields = nil
32
+ @object_name = object_name
33
+ @invalid_fields = []
59
34
  end
60
35
 
61
36
  def object
62
- IronBank::Resources.const_get(object_name)
37
+ @object ||= IronBank::Resources.const_get(object_name)
63
38
  end
64
39
 
65
40
  def excluded_fields
66
41
  @excluded_fields ||= object.excluded_fields.dup
67
42
  end
68
43
 
69
- def remove_last_failure_fields
70
- query_fields = object.query_fields
71
-
44
+ def remove_invalid_fields
45
+ query_fields = object.query_fields
72
46
  failed_fields = query_fields.select do |field|
73
- last_failed_fields.any? { |failed| field.casecmp?(failed) }
47
+ invalid_fields.any? { |failed| field.casecmp?(failed) }
74
48
  end
75
-
76
49
  excluded_fields.push(*failed_fields)
77
-
78
- # Remove the field for the next query
79
50
  query_fields.delete_if { |field| failed_fields.include?(field) }
80
51
  end
81
52
 
@@ -83,30 +54,16 @@ module IronBank
83
54
  # Querying using the ID (which is an indexed field) should return an
84
55
  # empty collection very quickly when successful
85
56
  object.where({ id: INVALID_OBJECT_ID })
86
-
87
57
  info "Successful query for #{object_name}"
88
-
89
58
  true
90
- rescue IronBank::InternalServerError, IronBank::BadRequestError => e
91
- @last_failed_fields = extract_fields_from_exception(e)
92
-
59
+ rescue IronBank::BadRequestError, InternalServerError => e
60
+ @invalid_fields = Array(
61
+ ExtractFromMessage.call(e.message) ||
62
+ DeduceFromQuery.call(object)
63
+ )
64
+ info "Invalid fields '#{@invalid_fields}' for #{object_name} query"
93
65
  false
94
66
  end
95
-
96
- def extract_fields_from_exception(exception)
97
- message = exception.message
98
-
99
- raise "Could not parse error message: #{message}" unless FAULT_FIELD_MESSAGES.match(message)
100
-
101
- failed_fields = Regexp.last_match.
102
- captures.
103
- compact.
104
- map { |capture| capture.delete(" ") }
105
-
106
- info "Invalid fields '#{failed_fields}' for #{object_name} query"
107
-
108
- failed_fields
109
- end
110
67
  end
111
68
  end
112
69
  end
@@ -39,6 +39,14 @@ module IronBank
39
39
  end
40
40
 
41
41
  def self.excluded_fields
42
+ # NOTE: In some instances the error message from Zuora will not include the
43
+ # unqueryable fields that need to be excluded. When that happens IronBank's
44
+ # strategy will be to perform a binary search through the fields listed in the
45
+ # query -- at the cost of performance due to repeated requests sent to Zuora
46
+ # as it tries to identify the offending field.
47
+ #
48
+ # See:
49
+ # - https://github.com/zendesk/iron_bank/pull/107
42
50
  @excluded_fields ||= IronBank::Resources.constants.each.with_object({}) do |resource, fields|
43
51
  fields[resource.to_s] =
44
52
  IronBank::Describe::ExcludedFields.call(object_name: resource)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IronBank
4
- VERSION = "5.4.0"
4
+ VERSION = "5.4.1"
5
5
  API_VERSION = "v1"
6
6
  end
data/lib/iron_bank.rb CHANGED
@@ -83,6 +83,8 @@ require "iron_bank/authentications/token"
83
83
  require "iron_bank/client"
84
84
  require "iron_bank/describe/field"
85
85
  require "iron_bank/describe/excluded_fields"
86
+ require "iron_bank/describe/excluded_fields/deduce_from_query"
87
+ require "iron_bank/describe/excluded_fields/extract_from_message"
86
88
  require "iron_bank/describe/object"
87
89
  require "iron_bank/describe/related"
88
90
  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: 5.4.0
4
+ version: 5.4.1
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: 2023-05-11 00:00:00.000000000 Z
14
+ date: 2024-01-02 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: faraday
@@ -107,6 +107,8 @@ files:
107
107
  - lib/iron_bank/configuration.rb
108
108
  - lib/iron_bank/csv.rb
109
109
  - lib/iron_bank/describe/excluded_fields.rb
110
+ - lib/iron_bank/describe/excluded_fields/deduce_from_query.rb
111
+ - lib/iron_bank/describe/excluded_fields/extract_from_message.rb
110
112
  - lib/iron_bank/describe/field.rb
111
113
  - lib/iron_bank/describe/object.rb
112
114
  - lib/iron_bank/describe/related.rb