iron_bank 5.4.0 → 5.4.1

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