zuora_api 1.7.01 → 1.7.7a

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: 98ada8f975604d10b0cb60b4b58eacef95a917f2509c3ee578b44cb9ae00dac2
4
- data.tar.gz: f114390047aeec864c19a1b04e9e8ab93cdf2a4c8c46c84fc471593d893651fe
3
+ metadata.gz: 2697794114e89b4d76001a4746c1148229aeada880a18ab2cd3a97673549d2c6
4
+ data.tar.gz: 7a66c8c6b0d604bc9d1229424ecca8a557c6c36a84304049970988cbf7e6c771
5
5
  SHA512:
6
- metadata.gz: 85c08a97f372728e3d40ab433bf18d30ad58dddd1297e9cb241b8b031ce3baa1cd60f21405c4f8909fc350177fb8294897aee0baba06194d54365e0fa987d0ed
7
- data.tar.gz: 50f70103f0984d99a1e2ca3df4889c58c3c2c5387db2ffbc37b56db3e46fb7cb02ccfa25071a4820184ff62b1909b88106eda2d665e9ec7ddd2f9b6bc991eea0
6
+ metadata.gz: d7a8ae407cd8d86a1249b404ef27be7ee13e13e8f620ec7cda03d84db3c17c077ead6cbf8f90d6bb8be66fb5594d90adf7184e707a7ac1f1639b84434868fe4f
7
+ data.tar.gz: b9732ae7b0397cc983e87e3459f80fa013c81170f2fd1fec15256a51e79e6722eeac58dcda457eb7d4ec323cbe5efd869094f3abbc29124e737d95689d4150a1
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .idea
@@ -1,4 +1,4 @@
1
- image: ruby:2.6
1
+ image: ruby:2.7
2
2
  stages:
3
3
  - setup
4
4
  - test
@@ -35,6 +35,7 @@ rspec-testing:
35
35
  script:
36
36
  - bundle install
37
37
  - rspec
38
+ coverage: '/\(\d+.\d+\%\) covered/'
38
39
 
39
40
  rubygems-deploy:
40
41
  stage: deploy
@@ -48,8 +49,8 @@ rubygems-deploy:
48
49
  - if [[ "staging" == $CI_BUILD_REF_SLUG ]];then export VERSION=`git describe --match "[0-9]*\.[0-9]*\.[0-9]*[a-z]" --abbrev=0 --tags HEAD`; fi
49
50
  - if [[ "master" == $CI_BUILD_REF_SLUG ]];then export VERSION=`git describe --exclude "[0-9]*\.[0-9]*\.[0-9]*[a-z]" --abbrev=0 --tags HEAD`; fi
50
51
  - echo $VERSION
51
- - sed -i "s/0.0.1/$VERSION/" /Connect/zuora-gem/lib/zuora_api/version.rb
52
- - git add /Connect/zuora-gem/lib/zuora_api/version.rb
52
+ - sed -i "s/0.0.1/$VERSION/" lib/zuora_api/version.rb
53
+ - git add lib/zuora_api/version.rb
53
54
  - git config --global user.email "connect@zuora.com"
54
55
  - git config --global user.name "Connect Automation"
55
56
  - git commit -m "Automated Version Update $VERSION"
@@ -1,6 +1,23 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [1.7.07] - 2018-9-10
5
+ ### Changed
6
+ - Cookie name for service endpoint integration
7
+
8
+ ## [1.7.06] - 2018-8-25
9
+ ### Added
10
+ - Retry for 502/503
11
+ - Standard exception for 504
12
+
13
+ ## [1.7.05] - 2018-8-25
14
+ ### Added
15
+ - Added support for oauth token forbidden
16
+
17
+ ## [1.7.02] - 2018-8-23
18
+ ### Added
19
+ - Added mulit part support for rest call
20
+
4
21
  ## [1.7.01] - 2018-8-06
5
22
  ### Changed
6
23
  - Changed library used to determine host
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
-
2
+ ruby "2.7.1"
3
3
  # Specify your gem's dependencies in zuora.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Zuora Gem
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/zuora_api.svg)](https://badge.fury.io/rb/zuora_api) [![coverage report](https://gitlab.zuora.com/Connect/zuora-gem/badges/master/coverage.svg)](https://gitlab.zuora.com/Connect/zuora-gem/commits/master)
3
+ [![Gem Version](https://badge.fury.io/rb/zuora_api.svg)](https://badge.fury.io/rb/zuora_api) [![coverage report](https://gitlab.0.ecc.auw2.zuora/extension-products/shared-libraries/zuora-gem/badges/master/coverage.svg)](https://gitlab.0.ecc.auw2.zuora/extension-products/shared-libraries/zuora-gem/commits/master)
4
4
 
5
5
  ## Installation
6
6
  Add this line to your application's Gemfile:
@@ -190,12 +190,11 @@ module InsightsAPI
190
190
  end
191
191
 
192
192
  rescue => ex
193
- if !(tries -= 1).zero?
194
- sleep 3
195
- retry
196
- else
197
- raise ex
198
- end
193
+ raise ex if tries.zero?
194
+
195
+ tries -= 1
196
+ sleep 3
197
+ retry
199
198
  else
200
199
  return temp_file
201
200
  end
@@ -1,14 +1,38 @@
1
1
  module ZuoraAPI
2
2
  module Exceptions
3
- class Error < StandardError; end
3
+ class Error < StandardError;
4
+ def parse_message(message)
5
+ case message
6
+ when /^Payment status should be Processed. Invalid payment is P-\d*./
7
+ @message = "Payment status should be Processed."
8
+ when /^Adjustment cannot be created for invoice(.*) with a zero balance./
9
+ @message = "Adjustment cannot be created for invoice with a zero balance."
10
+ when /^The balance of all the invoice items and tax items is 0. No write-off is needed for the invoice .*./
11
+ @message = "The balance of all the invoice items and tax items is 0. No write-off is needed for the invoice."
12
+ when /^Json input does not match schema. Error(s): string ".*" is too long .*/
13
+ @message = "Json input does not match schema. Error(s): String is too long."
14
+ when /^Query failed \(#[\d\w_]*\): line [0-9]+:[0-9]+: (.*)$/
15
+ @message = "Query failed: #{$1}"
16
+ when /^Query failed \(#[\d\w_]*\): (.*)$/
17
+ @message = "Query failed: #{$1}"
18
+ when /^Could not find [\w\d]{32}.$/
19
+ @message = "Could not find object."
20
+ when /^Subscription [\w\d]{32} is in expired status. It is not supported to generate billing documents for expired subscriptions./
21
+ @message = "Subscription is in expired status. It is not supported to generate billing documents for expired subscriptions."
22
+ else
23
+ @message = message
24
+ end
25
+ end
26
+ end
27
+ class FileDownloadError < StandardError; end
4
28
  class AuthorizationNotPerformed < Error; end
5
29
  class ZuoraAPISessionError < Error
6
30
  attr_reader :code, :response
7
31
  attr_writer :default_message
8
32
 
9
- def initialize(message = nil,response=nil)
10
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
11
- @message = message
33
+ def initialize(message = nil,response=nil, errors = [], successes = [])
34
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
35
+ @message = parse_message(message)
12
36
  @response = response
13
37
  @default_message = "Error with Zuora Session."
14
38
  end
@@ -16,6 +40,19 @@ module ZuoraAPI
16
40
  def to_s
17
41
  @message || @default_message
18
42
  end
43
+
44
+ def parse_message(message)
45
+ case message
46
+ when /^Invalid Oauth Client Id$/, /^Unable to generate token.$/
47
+ @message = "Invalid login, please check client ID and Client Secret or URL endpoint"
48
+ when /^Forbidden$/
49
+ @message = "The user associated to OAuth credential set has been deactivated."
50
+ when /^Invalid login. User name and password do not match.$/
51
+ @message = "Invalid login, please check username and password or URL endpoint"
52
+ else
53
+ @message = message
54
+ end
55
+ end
19
56
  end
20
57
 
21
58
  class BadEntityError < Error
@@ -23,8 +60,8 @@ module ZuoraAPI
23
60
  attr_writer :default_message
24
61
 
25
62
  def initialize(message = nil,response=nil, errors = [], successes = [], *args)
26
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
27
- @message = message
63
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
64
+ @message = parse_message(message)
28
65
  @response = response
29
66
  @default_message = "Error with Zuora Entity"
30
67
  @errors = errors
@@ -41,8 +78,8 @@ module ZuoraAPI
41
78
  attr_writer :default_message
42
79
 
43
80
  def initialize(message = nil,response=nil, errors = [], successes = [], *args)
44
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
45
- @message = message
81
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
82
+ @message = parse_message(message)
46
83
  @response = response
47
84
  @default_message = "Error communicating with Zuora."
48
85
  @errors = errors
@@ -54,13 +91,31 @@ module ZuoraAPI
54
91
  end
55
92
  end
56
93
 
94
+ class ZuoraAPIInternalServerError < Error
95
+ attr_reader :code, :response, :errors, :successes
96
+ attr_writer :default_message
97
+
98
+ def initialize(message = nil,response = nil, errors = [], successes = [], *args)
99
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
100
+ @message = parse_message(message)
101
+ @response = response
102
+ @default_message = "Zuora Internal Server Error."
103
+ @errors = errors
104
+ @successes = successes
105
+ end
106
+
107
+ def to_s
108
+ @message || @default_message
109
+ end
110
+ end
111
+
57
112
  class ZuoraAPIRequestLimit < Error
58
113
  attr_reader :code, :response
59
114
  attr_writer :default_message
60
115
 
61
- def initialize(message = nil,response=nil, *args)
62
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
63
- @message = message
116
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
117
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
118
+ @message = parse_message(message)
64
119
  @response = response
65
120
  @default_message = "Your request limit has been exceeded for zuora."
66
121
  end
@@ -70,13 +125,29 @@ module ZuoraAPI
70
125
  end
71
126
  end
72
127
 
128
+ class ZuoraAPIUnkownError < Error
129
+ attr_reader :code, :response
130
+ attr_writer :default_message
131
+
132
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
133
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
134
+ @message = parse_message(message)
135
+ @response = response
136
+ @default_message = "An unkown error occured. Workflow is not responsible. Please contact Support."
137
+ end
138
+
139
+ def to_s
140
+ @message || @default_message
141
+ end
142
+ end
143
+
73
144
  class ZuoraAPILockCompetition < Error
74
145
  attr_reader :code, :response
75
146
  attr_writer :default_message
76
147
 
77
- def initialize(message = nil,response=nil, *args)
78
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
79
- @message = message
148
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
149
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
150
+ @message = parse_message(message)
80
151
  @response = response
81
152
  @default_message = "Operation failed due to lock competition. Please retry"
82
153
  end
@@ -90,9 +161,9 @@ module ZuoraAPI
90
161
  attr_reader :code, :response
91
162
  attr_writer :default_message
92
163
 
93
- def initialize(message = nil,response=nil, *args)
94
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
95
- @message = message
164
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
165
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
166
+ @message = parse_message(message)
96
167
  @response = response
97
168
  @default_message = "Operation failed due to lock competition. Please retry"
98
169
  end
@@ -102,15 +173,32 @@ module ZuoraAPI
102
173
  end
103
174
  end
104
175
 
105
- class ZuoraAPITemporaryError < Error
176
+ class ZuoraUnexpectedError < Error
106
177
  attr_reader :code, :response
107
178
  attr_writer :default_message
108
179
 
109
- def initialize(message = nil,response=nil, *args)
110
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
111
- @message = message
180
+ def initialize(message = nil, response=nil, errors = [], successes = [], *args)
181
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
182
+ @message = parse_message(message)
183
+ @response = response
184
+ @default_message = "An unexpected error occurred"
185
+ end
186
+
187
+ def to_s
188
+ @message || @default_message
189
+ end
190
+ end
191
+
192
+ class ZuoraAPITemporaryError < Error
193
+ attr_reader :code, :response, :errors
194
+ attr_writer :default_message
195
+
196
+ def initialize(message = nil, response = nil, errors = [], successes = [], *args)
197
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
198
+ @message = parse_message(message)
112
199
  @response = response
113
200
  @default_message = "There is a temporary error with zuora system."
201
+ @errors = errors
114
202
  end
115
203
 
116
204
  def to_s
@@ -122,10 +210,43 @@ module ZuoraAPI
122
210
  attr_reader :code, :response
123
211
  attr_writer :default_message
124
212
 
125
- def initialize(message = nil,response=nil, *args)
126
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
213
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
214
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
215
+ @message = parse_message(message)
216
+ @response = response
217
+ @default_message = "Authentication type is not supported by this Login"
218
+ end
219
+
220
+ def to_s
221
+ @message || @default_message
222
+ end
223
+ end
224
+
225
+ class ZuoraAPIConnectionTimeout < Net::OpenTimeout
226
+ attr_reader :code, :response
227
+ attr_writer :default_message
228
+
229
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
230
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
231
+ @message = message
232
+ @response = response
233
+ @default_message = "Authentication type is not supported by this Login"
234
+ end
235
+
236
+ def to_s
237
+ @message || @default_message
238
+ end
239
+ end
240
+
241
+ class ZuoraAPIReadTimeout < Net::ReadTimeout
242
+ attr_reader :code, :response, :request
243
+ attr_writer :default_message
244
+
245
+ def initialize(message = nil, response = nil, request = nil, errors = [], successes = [], *args)
246
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
127
247
  @message = message
128
248
  @response = response
249
+ @request = request
129
250
  @default_message = "Authentication type is not supported by this Login"
130
251
  end
131
252
 
@@ -5,13 +5,44 @@ require 'zuora_api/exceptions'
5
5
 
6
6
  module ZuoraAPI
7
7
  class Login
8
- ENVIRONMENTS = [SANDBOX = 'Sandbox', PRODUCTION = 'Production', PREFORMANCE = 'Preformance', SERVICES = 'Services', UNKNOWN = 'Unknown', STAGING = 'Staging' ]
8
+ ENVIRONMENTS = [TEST = 'Test', SANDBOX = 'Sandbox', PRODUCTION = 'Production', PREFORMANCE = 'Preformance', SERVICES = 'Services', UNKNOWN = 'Unknown', STAGING = 'Staging' ]
9
9
  REGIONS = [EU = 'EU', US = 'US', NA = 'NA' ]
10
10
  MIN_Endpoint = '96.0'
11
11
  XML_SAVE_OPTIONS = Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
12
- CONNECTION_EXCEPTIONS = [Net::OpenTimeout, OpenSSL::SSL::SSLError, Errno::ECONNREFUSED, SocketError, Errno::EHOSTUNREACH, Errno::EADDRNOTAVAIL]
13
- CONNECTION_READ_EXCEPTIONS = [Net::ReadTimeout, Errno::ECONNRESET, Errno::EPIPE]
14
- ZUORA_API_ERRORS = [ZuoraAPI::Exceptions::ZuoraAPIError, ZuoraAPI::Exceptions::ZuoraAPIRequestLimit, ZuoraAPI::Exceptions::ZuoraAPILockCompetition, ZuoraAPI::Exceptions::ZuoraAPITemporaryError, ZuoraAPI::Exceptions::ZuoraDataIntegrity]
12
+
13
+ CONNECTION_EXCEPTIONS = [
14
+ Net::OpenTimeout,
15
+ OpenSSL::SSL::SSLError,
16
+ Errno::ECONNREFUSED,
17
+ SocketError,
18
+ Errno::EHOSTUNREACH,
19
+ Errno::EADDRNOTAVAIL,
20
+ Errno::ETIMEDOUT,
21
+ ].freeze
22
+
23
+ CONNECTION_READ_EXCEPTIONS = [
24
+ Net::ReadTimeout,
25
+ Errno::ECONNRESET,
26
+ Errno::EPIPE
27
+ ].freeze
28
+
29
+ ZUORA_API_ERRORS = [
30
+ ZuoraAPI::Exceptions::ZuoraAPIError,
31
+ ZuoraAPI::Exceptions::ZuoraAPIRequestLimit,
32
+ ZuoraAPI::Exceptions::ZuoraAPILockCompetition,
33
+ ZuoraAPI::Exceptions::ZuoraAPITemporaryError,
34
+ ZuoraAPI::Exceptions::ZuoraDataIntegrity,
35
+ ZuoraAPI::Exceptions::ZuoraAPIInternalServerError,
36
+ ZuoraAPI::Exceptions::ZuoraUnexpectedError,
37
+ ZuoraAPI::Exceptions::ZuoraAPIUnkownError
38
+ ].freeze
39
+
40
+ ZUORA_SERVER_ERRORS = [
41
+ ZuoraAPI::Exceptions::ZuoraAPIInternalServerError,
42
+ ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout,
43
+ ZuoraAPI::Exceptions::ZuoraAPIReadTimeout,
44
+ ZuoraAPI::Exceptions::ZuoraUnexpectedError
45
+ ].freeze
15
46
 
16
47
  attr_accessor :region, :url, :wsdl_number, :current_session, :bearer_token, :oauth_session_expires_at, :environment, :status, :errors, :current_error, :user_info, :tenant_id, :tenant_name, :entity_id, :timeout_sleep, :hostname, :zconnect_provider
17
48
 
@@ -19,10 +50,10 @@ module ZuoraAPI
19
50
  raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
20
51
  # raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
21
52
  self.hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
22
- if !/apps\/services\/a\/\d{2}\.\d$/.match(url.strip)
53
+ if !/apps\/services\/a\/\d+\.\d$/.match(url.strip)
23
54
  self.url = "https://#{hostname}/apps/services/a/#{MIN_Endpoint}"
24
- elsif MIN_Endpoint.to_f > url.scan(/(\d{2}\.\d)$/).dig(0,0).to_f
25
- self.url = url.gsub(/(\d{2}\.\d)$/, MIN_Endpoint)
55
+ elsif MIN_Endpoint.to_f > url.scan(/(\d+\.\d)$/).dig(0,0).to_f
56
+ self.url = url.gsub(/(\d+\.\d)$/, MIN_Endpoint)
26
57
  else
27
58
  self.url = url
28
59
  end
@@ -41,27 +72,12 @@ module ZuoraAPI
41
72
 
42
73
  def get_identity(cookies)
43
74
  zsession = cookies["ZSession"]
44
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
45
75
  begin
46
76
  if !zsession.blank?
47
77
  response = HTTParty.get("https://#{self.hostname}/apps/v1/identity", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
48
78
  output_json = JSON.parse(response.body)
49
- elsif zconnect_accesstoken.present?
50
- begin
51
- code = zconnect_accesstoken.split("#!").last
52
- encrypted_token, tenant_id = Base64.decode64(code).split(":")
53
- body = {'token' => encrypted_token}.to_json
54
- rescue => ex
55
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Invalid ZConnect Cookie")
56
- end
57
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/identity", :body => body, :headers => { 'Content-Type' => 'application/json' })
58
- output_json = JSON.parse(response.body)
59
79
  else
60
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
61
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
62
- else
63
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
64
- end
80
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
65
81
  end
66
82
  rescue JSON::ParserError => ex
67
83
  output_json = {}
@@ -72,20 +88,12 @@ module ZuoraAPI
72
88
 
73
89
  def get_full_nav(cookies)
74
90
  zsession = cookies["ZSession"]
75
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
76
91
  begin
77
92
  if zsession.present?
78
93
  response = HTTParty.get("https://#{self.hostname}/apps/v1/navigation", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
79
94
  output_json = JSON.parse(response.body)
80
- elsif zconnect_accesstoken.present?
81
- response = HTTParty.get("https://#{self.hostname}/apps/zconnectsession/navigation", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}",'Content-Type' => 'application/json'})
82
- output_json = JSON.parse(response.body)
83
95
  else
84
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
85
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
86
- else
87
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
88
- end
96
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
89
97
  end
90
98
  rescue JSON::ParserError => ex
91
99
  output_json = {}
@@ -96,20 +104,12 @@ module ZuoraAPI
96
104
 
97
105
  def set_nav(state, cookies)
98
106
  zsession = cookies["ZSession"]
99
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
100
107
  begin
101
108
  if !zsession.blank?
102
109
  response = HTTParty.put("https://#{self.hostname}/apps/v1/preference/navigation", :body => state.to_json, :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
103
110
  output_json = JSON.parse(response.body)
104
- elsif !zconnect_accesstoken.blank?
105
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/navigationstate", :body => state.to_json, :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
106
- output_json = JSON.parse(response.body)
107
111
  else
108
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
109
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
110
- else
111
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
112
- end
112
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
113
113
  end
114
114
  rescue JSON::ParserError => ex
115
115
  output_json = {}
@@ -120,20 +120,12 @@ module ZuoraAPI
120
120
 
121
121
  def refresh_nav(cookies)
122
122
  zsession = cookies["ZSession"]
123
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
124
123
  begin
125
124
  if !zsession.blank?
126
125
  response = HTTParty.post("https://#{self.hostname}/apps/v1/navigation/fetch", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
127
126
  output_json = JSON.parse(response.body)
128
- elsif !zconnect_accesstoken.blank?
129
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/refresh-navbarcache", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
130
- output_json = JSON.parse(response.body)
131
127
  else
132
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
133
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
134
- else
135
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
136
- end
128
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
137
129
  end
138
130
  rescue JSON::ParserError => ex
139
131
  output_json = {}
@@ -142,27 +134,21 @@ module ZuoraAPI
142
134
  return output_json
143
135
  end
144
136
 
145
- def get_zconnect_accesstoken(cookies)
146
- accesstoken = nil
147
- self.update_zconnect_provider
148
- if !cookies[self.zconnect_provider].nil? && !cookies[self.zconnect_provider].empty?
149
- accesstoken = cookies[self.zconnect_provider]
150
- end
151
- return accesstoken
152
- end
153
-
154
137
  def reporting_url(path)
155
138
  map = {"US" => {"Sandbox" => "https://zconnectsandbox.zuora.com/api/rest/v1/",
156
139
  "Production" => "https://zconnect.zuora.com/api/rest/v1/",
157
- "Services"=> ""},
140
+ "Test" => "https://reporting-sbx.zan.0001.sbx.auw2.zuora.com/api/rest/v1/",
141
+ "Staging" => "https://reporting-stg11.zan.svc.auw2.zuora.com/api/rest/v1/",
142
+ "Performance" => "https://zconnectpt1.zuora.com/api/rest/v1/",
143
+ "Services" => "https://reporting-svc08.svc.auw2.zuora.com/api/rest/v1/"},
158
144
  "EU" => {"Sandbox" => "https://zconnect.sandbox.eu.zuora.com/api/rest/v1/",
159
145
  "Production" => "https://zconnect.eu.zuora.com/api/rest/v1/",
160
- "Services"=> ""},
146
+ "Services"=> "https://reporting-sbx0000.sbx.aec1.zuora.com/api/rest/v1/"},
161
147
  "NA" => {"Sandbox" => "https://zconnect.sandbox.na.zuora.com/api/rest/v1/",
162
148
  "Production" => "https://zconnect.na.zuora.com/api/rest/v1/",
163
149
  "Services"=> ""}
164
150
  }
165
- return map[zuora_client.region][zuora_client.environment].insert(-1, path)
151
+ return map[self.region][self.environment].insert(-1, path)
166
152
  end
167
153
 
168
154
  # There are two ways to call this method. The first way is best.
@@ -212,7 +198,7 @@ module ZuoraAPI
212
198
  end
213
199
 
214
200
  def self.environments
215
- %w(Sandbox Production Services Performance Staging)
201
+ %w(Sandbox Production Services Performance Staging Test)
216
202
  end
217
203
 
218
204
  def self.regions
@@ -224,11 +210,13 @@ module ZuoraAPI
224
210
  "Production" => "https://www.zuora.com/apps/services/a/",
225
211
  "Performance" => "https://pt1.zuora.com/apps/services/a/",
226
212
  "Services" => "https://services347.zuora.com/apps/services/a/",
227
- "Staging" => "https://staging2.zuora.com/apps/services/a/"},
213
+ "Staging" => "https://staging2.zuora.com/apps/services/a/",
214
+ "Test" => "https://test.zuora.com/apps/services/a/"},
228
215
  "EU" => {"Sandbox" => "https://sandbox.eu.zuora.com/apps/services/a/",
229
216
  "Production" => "https://eu.zuora.com/apps/services/a/",
230
217
  "Performance" => "https://pt1.eu.zuora.com/apps/services/a/",
231
- "Services" => "https://services347.eu.zuora.com/apps/services/a/"},
218
+ "Services" => "https://services347.eu.zuora.com/apps/services/a/",
219
+ "Test" => "https://test.eu.zuora.com/apps/services/a/"},
232
220
  "NA" => {"Sandbox" => "https://sandbox.na.zuora.com/apps/services/a/",
233
221
  "Production" => "https://na.zuora.com/apps/services/a/",
234
222
  "Performance" => "https://pt1.na.zuora.com/apps/services/a/",
@@ -265,15 +253,18 @@ module ZuoraAPI
265
253
 
266
254
  def update_environment
267
255
  if !self.url.blank?
268
- if /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/ === self.hostname
256
+ case self.hostname
257
+ when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
269
258
  self.environment = 'Sandbox'
270
- elsif /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/ === self.hostname
259
+ when /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/
271
260
  self.environment = 'Services'
272
- elsif /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/ === self.hostname
261
+ when /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/
273
262
  self.environment = 'Performance'
274
- elsif /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/ === self.hostname
263
+ when /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/
275
264
  self.environment = 'Staging'
276
- elsif is_prod_env
265
+ when /(?<=\.|\/|^)(test)(?=\.|\/|$)/
266
+ self.environment = 'Test'
267
+ when /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/, /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/
277
268
  self.environment = 'Production'
278
269
  else
279
270
  self.environment = 'Unknown'
@@ -283,25 +274,14 @@ module ZuoraAPI
283
274
  end
284
275
  end
285
276
 
286
- def is_prod_env
287
- is_prod = false
288
- www_or_api = /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/ === self.hostname
289
- host_prefix_match = /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/ === self.hostname
290
- if www_or_api || host_prefix_match
291
- is_prod = true
292
- end
293
- return is_prod
294
- end
295
-
296
277
  def update_zconnect_provider
297
278
  region = update_region
298
279
  environment = update_environment
299
- mappings = {"US" => {"Sandbox" => "ZConnectSbx", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev", "Services" => "ZConnectQA", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Staging" => "ZConnectQA"},
300
- "NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectQANA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
301
- "EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectQAEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU"},
280
+ mappings = {"US" => {"Sandbox" => "ZConnectSbx", "Services" => "ZConnectSvcUS", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Test" => "ZConnectTest", "Staging" => "ZConnectQA", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev"},
281
+ "NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectSvcNA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
282
+ "EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectSvcEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU", "Test" => "ZConnectTest"},
302
283
  "Unknown" => {"Unknown" => "Unknown"}}
303
284
  self.zconnect_provider = mappings[region][environment]
304
- # raise "Can't find ZConnect Provider for #{region} region and #{environment} environment" if self.zconnect_provider.nil?
305
285
  end
306
286
 
307
287
  def aqua_endpoint(url="")
@@ -314,45 +294,35 @@ module ZuoraAPI
314
294
  return "#{url_slash_apps_slash}api/#{url}"
315
295
  end
316
296
 
317
- def rest_endpoint(url="")
297
+ def rest_endpoint(url="", domain=true, prefix='/v1/')
318
298
  update_environment
319
299
  endpoint = url
320
-
300
+ url_postfix = {"US" => ".", "EU" => ".eu.", "NA" => ".na."}[self.region]
301
+
321
302
  case self.environment
303
+ when 'Test'
304
+ endpoint = "https://rest.test#{url_postfix}zuora.com"
322
305
  when 'Sandbox'
323
- case self.region
324
- when 'US'
325
- endpoint = "https://rest.apisandbox.zuora.com/v1/".concat(url)
326
- when 'EU'
327
- endpoint = "https://rest.sandbox.eu.zuora.com/v1/".concat(url)
328
- when 'NA'
329
- endpoint = "https://rest.sandbox.na.zuora.com/v1/".concat(url)
330
- end
306
+ endpoint = "https://rest.sandbox#{url_postfix}zuora.com"
307
+ endpoint = "https://rest.apisandbox.zuora.com" if self.region == "US"
331
308
  when 'Production'
332
- case self.region
333
- when 'US'
334
- endpoint = "https://rest.zuora.com/v1/".concat(url)
335
- when 'EU'
336
- endpoint = "https://rest.eu.zuora.com/v1/".concat(url)
337
- when 'NA'
338
- endpoint = "https://rest.na.zuora.com/v1/".concat(url)
339
- end
309
+ endpoint = "https://rest#{url_postfix}zuora.com"
340
310
  when 'Performance'
341
- endpoint = "https://rest.pt1.zuora.com/v1/".concat(url)
311
+ endpoint = "https://rest.pt1.zuora.com"
342
312
  when 'Services'
343
313
  https = /https:\/\/|http:\/\//.match(self.url)[0]
344
314
  host = self.hostname
345
- endpoint = "#{https}rest#{host}/v1/#{url}"
315
+ endpoint = "#{https}rest#{host}"
346
316
  when 'Staging'
347
- endpoint = "https://rest-staging2.zuora.com/v1/".concat(url)
317
+ endpoint = "https://rest-staging2.zuora.com"
348
318
  when 'Unknown'
349
319
  raise "Environment unknown, returning passed in parameter unaltered"
350
320
  end
351
- return endpoint
321
+ return domain ? endpoint.concat(prefix).concat(url) : prefix.concat(url)
352
322
  end
353
323
 
354
- def rest_domain
355
- return URI(self.rest_endpoint).host
324
+ def rest_domain(endpoint: self.rest_endpoint)
325
+ return URI(endpoint).host
356
326
  end
357
327
 
358
328
  def fileURL(url="")
@@ -363,43 +333,89 @@ module ZuoraAPI
363
333
  return self.wsdl_number > 68 ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'
364
334
  end
365
335
 
366
- def new_session(auth_type: :basic, debug: false)
336
+ def new_session(auth_type: :basic, debug: false, zuora_track_id: nil)
337
+ retries ||= 2
338
+ yield
339
+
340
+ rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
341
+ self.status = 'Invalid'
342
+ self.current_error = ex.message
343
+ raise
344
+ rescue ZuoraAPI::Exceptions::ZuoraAPIError => ex
345
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(ex.message, ex.response)
346
+
347
+ rescue ZuoraAPI::Exceptions::ZuoraAPIInternalServerError => ex
348
+ raise ex if retries.zero?
349
+
350
+ retries -= 1
351
+ sleep(self.timeout_sleep)
352
+ retry
353
+
354
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
355
+ self.log(location: "BasicLogin", exception: ex, message: "Timed out", level: :error)
356
+
357
+ self.current_error = "Request timed out. Try again"
358
+ self.status = 'Timeout'
359
+
360
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
361
+
362
+ rescue EOFError
363
+ if self.url.match?(/.*services\d{1,}.zuora.com*/)
364
+ self.current_error = "Services tenant '#{self.url.scan(/.*\/\/(services\d{1,}).zuora.com*/).last.first}' is no longer available."
365
+ self.status = 'Not Available'
366
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
367
+ end
368
+
367
369
  end
368
370
 
369
- def get_session(prefix: false, auth_type: :basic)
370
- Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}") if Rails.env.to_s == 'development'
371
+ def get_session(prefix: false, auth_type: :basic, zuora_track_id: nil)
371
372
  case auth_type
372
373
  when :basic
373
374
  if self.current_session.blank?
374
375
  case self.class.to_s
375
376
  when 'ZuoraAPI::Oauth'
376
377
  if self.bearer_token.blank? || self.oauth_expired?
377
- self.new_session(auth_type: :bearer)
378
+ self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
378
379
  end
379
- self.get_z_session if self.status == 'Active'
380
+ self.get_z_session(zuora_track_id: zuora_track_id)
380
381
  when 'ZuoraAPI::Basic'
381
- self.new_session(auth_type: :basic)
382
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
382
383
  else
383
- raise "No Zuora Login Specified"
384
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
384
385
  end
385
386
  end
386
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
387
387
  return prefix ? "ZSession #{self.current_session}" : self.current_session.to_s
388
388
  when :bearer
389
389
  case self.class.to_s
390
390
  when 'ZuoraAPI::Oauth'
391
391
  if self.bearer_token.blank? || self.oauth_expired?
392
- self.new_session(auth_type: :bearer)
392
+ self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
393
393
  end
394
394
  when 'ZuoraAPI::Basic'
395
395
  raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Basic Login, does not support Authentication of Type: #{auth_type}")
396
+ else
397
+ raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Unknown Login, does not support Authentication of Type: #{auth_type}")
396
398
  end
397
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
398
399
  return prefix ? "Bearer #{self.bearer_token}" : self.bearer_token.to_s
399
400
  end
400
401
  end
401
402
 
402
- def soap_call(ns1: 'ns1', ns2: 'ns2', batch_size: nil, single_transaction: false, debug: false, errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS), z_session: true, timeout_retry: false, timeout: 120,**keyword_args)
403
+ def soap_call(
404
+ ns1: 'ns1',
405
+ ns2: 'ns2',
406
+ batch_size: nil,
407
+ headers: {},
408
+ single_transaction: false,
409
+ debug: false,
410
+ zuora_track_id: nil,
411
+ errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
412
+ z_session: true,
413
+ timeout_retry: false,
414
+ timeout: 130,
415
+ timeout_sleep_interval: self.timeout_sleep,
416
+ output_exception_messages: true,
417
+ skip_session: false,
418
+ **keyword_args)
403
419
  tries ||= 2
404
420
  xml = Nokogiri::XML::Builder.new do |xml|
405
421
  xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/",
@@ -408,8 +424,10 @@ module ZuoraAPI
408
424
  'xmlns:api' => "http://api.zuora.com/",
409
425
  "xmlns:#{ns1}" => "http://api.zuora.com/") do
410
426
  xml['SOAP-ENV'].Header do
411
- xml["#{ns1}"].SessionHeader do
412
- xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic)
427
+ if !skip_session
428
+ xml["#{ns1}"].SessionHeader do
429
+ xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: zuora_track_id)
430
+ end
413
431
  end
414
432
  if single_transaction
415
433
  xml["#{ns1}"].CallOptions do
@@ -427,88 +445,212 @@ module ZuoraAPI
427
445
  end
428
446
  end
429
447
  end
430
-
431
448
  input_xml = Nokogiri::XML(xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip)
432
449
  input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
433
450
  Rails.logger.debug("Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
434
451
 
435
- response = HTTParty.post(self.url,:body => xml.doc.to_xml(:save_with => XML_SAVE_OPTIONS).strip, :headers => {'Content-Type' => "text/xml; charset=utf-8"}, :timeout => timeout)
452
+ headers.merge!({ 'Content-Type' => "text/xml; charset=utf-8", 'Accept' => 'text/xml'})
453
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
454
+
455
+ request = HTTParty::Request.new(
456
+ Net::HTTP::Post,
457
+ self.url,
458
+ body: xml.doc.to_xml(:save_with => XML_SAVE_OPTIONS).strip,
459
+ headers: headers,
460
+ timeout: timeout,
461
+ )
462
+
463
+ response = request.perform
464
+
436
465
  output_xml = Nokogiri::XML(response.body)
437
466
  Rails.logger.debug("Response SOAP XML: #{output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
438
467
 
439
468
  raise_errors(type: :SOAP, body: output_xml, response: response)
469
+
470
+ return output_xml, input_xml, response
471
+
440
472
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
441
- if !(tries -= 1).zero? && z_session
473
+ raise if skip_session
474
+ if !tries.zero? && z_session
475
+ tries -= 1
442
476
  Rails.logger.debug("SOAP Call - Session Invalid")
443
- self.new_session(auth_type: :basic)
444
- retry
445
- else
446
- if errors.include?(ex.class)
447
- raise ex
448
- else
449
- return output_xml, input_xml, response
477
+
478
+ begin
479
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
480
+ rescue *ZUORA_API_ERRORS => ex
481
+ return output_xml, input_xml, ex.response
450
482
  end
483
+
484
+ retry
451
485
  end
486
+
487
+ raise ex if errors.include?(ex.class)
488
+
489
+ return output_xml, input_xml, response
490
+
452
491
  rescue *ZUORA_API_ERRORS => ex
453
- if errors.include?(ex.class)
454
- raise ex
455
- else
456
- return output_xml, input_xml, response
457
- end
492
+ raise ex if errors.include?(ex.class)
493
+
494
+ response = ex.response unless response
495
+ return output_xml, input_xml, response
496
+
458
497
  rescue *CONNECTION_EXCEPTIONS => ex
459
- if !(tries -= 1).zero?
460
- Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
461
- sleep(self.timeout_sleep)
498
+ if !tries.zero?
499
+ tries -= 1
500
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
501
+ sleep(timeout_sleep_interval)
462
502
  retry
463
- else
464
- raise ex
465
503
  end
504
+
505
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
506
+ raise ex
507
+
466
508
  rescue *CONNECTION_READ_EXCEPTIONS => ex
467
- if !(tries -= 1).zero? && timeout_retry
468
- Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
469
- sleep(self.timeout_sleep)
470
- retry
471
- else
472
- raise ex
509
+ if !tries.zero?
510
+ tries -= 1
511
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
512
+ if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
513
+ retry
514
+ elsif timeout_retry
515
+ sleep(timeout_sleep_interval)
516
+ retry
517
+ end
473
518
  end
474
- rescue Errno::ECONNRESET => ex
475
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
476
- retry
519
+
520
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
521
+ ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
522
+ raise ex
523
+
524
+ rescue => ex
525
+ raise ex
526
+ ensure
527
+ self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
528
+ end
529
+
530
+ def error_logger(ex)
531
+ exception_args = Rails.logger.with_fields.merge(self.exception_args(ex))
532
+ case ex
533
+ when ZuoraAPI::Exceptions::ZuoraAPIUnkownError, ZuoraAPI::Exceptions::ZuoraDataIntegrity
534
+ Rails.logger.error('Zuora Unknown/Integrity Error', ex, exception_args)
535
+ when ZuoraAPI::Exceptions::ZuoraAPIRequestLimit
536
+ Rails.logger.info('Zuora APILimit Reached', exception_args)
537
+ when *(ZuoraAPI::Login::ZUORA_API_ERRORS-ZuoraAPI::Login::ZUORA_SERVER_ERRORS)
538
+ #Rails.logger.debug('Zuora API Error', ex, self.exception_args(ex))
539
+ when *ZuoraAPI::Login::ZUORA_SERVER_ERRORS
540
+ Rails.logger.error('Zuora Server Error', ex, exception_args)
541
+ end
542
+ end
543
+
544
+ def log(location: "Rest Call", exception: nil, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :info )
545
+ level = :debug if ![:debug, :info, :warn, :error, :fatal].include?(level)
546
+ if Rails.logger.class.to_s == "Ougai::Logger"
547
+ Rails.logger.send(level.to_sym, "#{location} - #{message}", exception)
477
548
  else
478
- raise ex
549
+ Rails.logger.send(level.to_sym, "#{location} - #{exception.class} #{message}")
550
+ end
551
+ end
552
+
553
+ def exception_args(ex)
554
+ args = {}
555
+ if ex.class == ZuoraAPI::Exceptions::ZuoraAPIRequestLimit
556
+ args.merge!({
557
+ zuora_trace_id: ex.response.headers["zuora-request-id"],
558
+ zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"]
559
+ })
560
+ elsif defined?(ex.response) && ex.response.present?
561
+ args.merge!({
562
+ request: {
563
+ path: ex.response.request.path.to_s,
564
+ method: ex.response.request.http_method.to_s.split("Net::HTTP::").last.upcase,
565
+ params: ex.response.request.raw_body.to_s,
566
+ headers: ex.response.request.options[:headers].map{|k,v| [k.to_s, k.to_s.downcase.strip == "authorization" ? "VALUE FILTERED" : v]}.to_h.to_s,
567
+ },
568
+ response: {
569
+ status: ex.response.code,
570
+ params: ex.response.body.to_s,
571
+ headers: ex.response.headers.to_s,
572
+ },
573
+ zuora_trace_id: ex.response.headers["zuora-request-id"],
574
+ zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"],
575
+ })
576
+ elsif defined?(ex.request) && ex.request.present?
577
+ args.merge!({
578
+ request: {
579
+ path: ex.request.path.to_s,
580
+ method: ex.request.http_method.to_s.split("Net::HTTP::").last.upcase,
581
+ params: ex.request.options[:body],
582
+ headers: ex.request.options[:headers].map{|k,v| [k.to_s, k.to_s.downcase.strip == "authorization" ? "VALUE FILTERED" : v]}.to_h.to_s
583
+ }
584
+ })
585
+ args.merge!({
586
+ zuora_track_id: ex.request.options[:headers]["Zuora-Track-Id"]
587
+ }) if ex.request.options[:headers]["Zuora-Track-Id"].present?
479
588
  end
480
589
  rescue => ex
481
- raise ex
482
- else
483
- return output_xml, input_xml, response
590
+ Rails.logger.error("Failed to create exception arguments", ex, args)
591
+ ensure
592
+ return args
484
593
  end
485
594
 
486
595
  def raise_errors(type: :SOAP, body: nil, response: nil)
487
- case type
488
- when :SOAP
489
- error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
490
- message = body.xpath('//fns:FaultMessage', 'fns' =>'http://fault.api.zuora.com/').text
596
+ request_uri, request_path, match_string = "", "", ""
597
+ if response.class.to_s == "HTTP::Message"
598
+ request_uri = response.http_header.request_uri.to_s
599
+ request_path = response.http_header.request_uri.path
600
+ match_string = "#{response.http_header.request_method}::#{response.code}::#{request_uri}"
601
+ else
602
+ request = response.request
603
+ request_uri = response.request.uri
604
+ request_path = request.path.path
605
+ match_string = "#{request.http_method.to_s.split("Net::HTTP::").last.upcase}::#{response.code}::#{request_path}"
606
+ end
491
607
 
492
- if error.blank? || message.blank?
493
- error = body.xpath('//faultcode').text
494
- message = body.xpath('//faultstring').text
608
+ if [502,503].include?(response.code)
609
+ raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
610
+ end
611
+
612
+ # Check failure response code
613
+ case response.code
614
+ when 504
615
+ raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from 'https://#{rest_domain(endpoint: request_uri)}'", response)
616
+ when 429
617
+ raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("The total number of concurrent requests has exceeded the limit allowed by the system. Please resubmit your request later.", response)
618
+ when 401
619
+
620
+ else
621
+ if body.class == Hash
622
+ case request_path
623
+ when /^\/v1\/connections$/
624
+ response_headers = response.headers.to_h
625
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing cookies for authentication call", response) if response_headers['set-cookie'].blank?
626
+ z_session_cookie = response_headers.fetch('set-cookie', []).select{|x| x.match(/^ZSession=.*/) }.first
627
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing ZSession cookie for authentication call", response) if z_session_cookie.blank?
628
+ end
495
629
  end
630
+ end
631
+
632
+ case type
633
+ when :SOAP
634
+ error, success, message = get_soap_error_and_message(body)
496
635
 
497
- if error.blank? || message.blank?
498
- error = body.xpath('//ns1:Code', 'ns1' =>'http://api.zuora.com/').text
499
- message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
636
+ if body.xpath('//fns:LoginFault', 'fns' =>'http://fault.api.zuora.com/').present?
637
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response)
500
638
  end
501
639
 
502
- #Update/Create/Delete Calls with multiple requests and responses
503
- if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
504
- error = []
505
- success = []
506
- body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
507
- if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
508
- message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
509
- error.push(message)
640
+ if body.xpath('//ns1:queryResponse', 'ns1' => 'http://api.zuora.com/').present? &&
641
+ body.xpath(
642
+ '//ns1:records[@xsi:type="ns2:Export"]',
643
+ 'ns1' => 'http://api.zuora.com/', 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
644
+ ).present?
645
+ result = body.xpath('//ns2:Status', 'ns2' => 'http://object.api.zuora.com/').text
646
+ if result == 'Failed'
647
+ message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
648
+ error = 'FATAL_ERROR'
649
+ if message.present?
650
+ identifier, new_message = message.scan(/^([\w\d]{16})\: (.*)/).first
651
+ error, message = ['UNEXPECTED_ERROR', new_message] if new_message.present?
510
652
  else
511
- success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
653
+ message = 'Export failed due to unknown reason. Consult api logs.'
512
654
  end
513
655
  end
514
656
  end
@@ -516,44 +658,52 @@ module ZuoraAPI
516
658
  #By default response if not passed in for SOAP as all SOAP is 200
517
659
  if error.present?
518
660
  if error.class == String
519
- case error
520
- when "INVALID_SESSION"
521
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{error}::#{message}", response)
522
- when "REQUEST_EXCEEDED_LIMIT"
523
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{error}::#{message}", response)
524
- when "LOCK_COMPETITION"
525
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
526
- when "BATCH_FAIL_ERROR"
527
- if message.include?("optimistic locking failed")
528
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
529
- else
530
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
531
- end
532
- when "TEMPORARY_ERROR"
533
- raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{error}::#{message}", response)
534
- when "INVALID_VALUE"
535
- if message.include?("data integrity violation")
536
- raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
537
- else
538
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
539
- end
540
- else
541
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response) if error.present?
542
- end
661
+ raise_errors_helper(error: error, message: message, response: response)
543
662
  elsif error.class == Array
544
- if error[0].include?("LOCK_COMPETITION") && error.count == 1
545
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(error.group_by {|v| v}.map {|k,v| "(#{v.size}x) - #{k == "::" ? 'UNKNOWN::No error provided' : k}"}.join(', '), response)
663
+ if error.uniq.size == 1
664
+ err, msg = error[0].split('::')
665
+ raise_errors_helper(error: err, message: msg, response: response, errors: error, success: success)
546
666
  else
547
667
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(error.group_by {|v| v}.map {|k,v| "(#{v.size}x) - #{k == "::" ? 'UNKNOWN::No error provided' : k}"}.join(', '), response, error, success)
548
668
  end
549
669
  end
550
670
  end
671
+
672
+ self.errors_via_content_type(response: response, type: :xml)
551
673
 
552
- if response.code == 429
553
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("The total number of concurrent requests has exceeded the limit allowed by the system. Please resubmit your request later.", response)
674
+ when :JSON
675
+ case request_path
676
+ when /^\/query\/jobs.*/ #DataQuery Paths
677
+ return if body.class != Hash
678
+ case match_string
679
+ when /^GET::200::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job, Capture of the id is present if needed in future error responses.
680
+ if body.dig('data', "errorCode") == "LINK_10000005"
681
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body.dig('data', "errorMessage"), response)
682
+ elsif (body.dig('data', "errorMessage").present? || body.dig('data', "queryStatus") == "failed")
683
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('data', "errorMessage"), response)
684
+ end
685
+ when /^GET::404::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job not found, capture of the id is present if needed in future error responses.
686
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
687
+ when /^POST::400::\/query\/jobs$/ #Create DQ job
688
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
689
+ end
690
+ when /^\/api\/rest\/v1\/reports.*/ #Reporting Paths
691
+ reporting_message = response.parsed_response.dig("ZanResponse", "response", "message") || body.dig("message")
692
+ if reporting_message&.include?("com.zuora.rest.RestUsageException: The user does not have permissions for this API.")
693
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response)
694
+ elsif reporting_message&.include?("500 Internal Server Error")
695
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Internal Server Error. The Reporting API is down. Contact Support.", response)
696
+ end
697
+ case match_string
698
+ when /^GET::400::\/api\/rest\/v1\/reports\/(reportlabels\/)?([a-zA-Z0-9\-_]+)\/report-details$/ # Get report, capture of the id is present if needed in future error responses.
699
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response) if reporting_message.present?
700
+ end
701
+ when /\/objects\/batch\//
702
+ if body['code'].present? && /61$/.match(body['code'].to_s).present? # if last 2 digits of code are 61
703
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body['message'], nil, body['details'])
704
+ end
554
705
  end
555
706
 
556
- when :JSON
557
707
  body = body.dig("results").present? ? body["results"] : body if body.class == Hash
558
708
  if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
559
709
  messages_array = body.fetch("reasons", []).map {|error| error['message']}.compact
@@ -561,6 +711,14 @@ module ZuoraAPI
561
711
  codes_array = body.fetch("reasons", []).map {|error| error['code']}.compact
562
712
  codes_array = codes_array.push(body.dig("error", 'code')).compact if body.dig('error').class == Hash
563
713
 
714
+ if body['message'] == 'request exceeded limit'
715
+ raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("The total number of concurrent requests has exceeded the limit allowed by the system. Please resubmit your request later.", response)
716
+ end
717
+
718
+ if (body.dig('message') || '').downcase.include?('unexpected error') && response.code != 500
719
+ raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(body['message'], response)
720
+ end
721
+
564
722
  if body['message'] == "No bearer token" && response.code == 400
565
723
  raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Authentication type is not supported by this Login", response)
566
724
  end
@@ -574,16 +732,29 @@ module ZuoraAPI
574
732
  end
575
733
 
576
734
  #Oauth Tokens - User deactivated
577
- if body['message'] == 'Forbidden' && body['status'] == 403 && response.code == 403
578
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Forbidden", response)
735
+ if body['path'] == '/oauth/token'
736
+ if body['status'] == 403 && response.code == 403
737
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Forbidden", response)
738
+ elsif body['status'] == 400 && response.code == 400 && body['message'].include?("Invalid client id")
739
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Invalid Oauth Client Id", response)
740
+ end
579
741
  end
580
742
 
581
743
  if body['error'] == 'Unauthorized' && body['status'] == 401
582
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
744
+ if body['message'].present?
745
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
746
+ else
747
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
748
+ end
583
749
  end
584
750
  #Authentication failed
585
751
  if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
586
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
752
+ new_message = messages_array.join(', ')
753
+ if new_message.present?
754
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(new_message, response)
755
+ else
756
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
757
+ end
587
758
  end
588
759
 
589
760
  #Zuora REST Create Amendment error #Authentication failed
@@ -591,16 +762,30 @@ module ZuoraAPI
591
762
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body['faultstring']}", response)
592
763
  end
593
764
 
765
+ #Locking contention
766
+ if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
767
+ raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
768
+ end
769
+ #Internal Server Error
770
+ if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(60)
771
+ if messages_array.uniq.size == 1
772
+ if messages_array.first.match(/^Transaction declined.*|^There is an invoice pending tax.*|^The Zuora GetTax call to Avalara.*|^The tax calculation call to Zuora Connect returned the following error: Status Code: 4.*/)
773
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(messages_array.first, response)
774
+ end
775
+ end
776
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("#{messages_array.join(', ')}", response)
777
+ end
778
+
779
+ #Retryiable Service Error
780
+ if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(61)
781
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{messages_array.join(', ')}", response)
782
+ end
783
+
594
784
  #Request exceeded limit
595
785
  if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(70)
596
786
  raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{messages_array.join(', ')}", response)
597
787
  end
598
788
 
599
- #Locking contention
600
- if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
601
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
602
- end
603
-
604
789
  #All Errors catch
605
790
  if codes_array.size > 0
606
791
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages_array.join(', ')}", response)
@@ -608,26 +793,20 @@ module ZuoraAPI
608
793
 
609
794
  #Zuora REST Query Errors
610
795
  if body["faultcode"].present?
611
- case body["faultcode"]
612
- when "fns:MALFORMED_QUERY"
613
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
614
- when "fns:REQUEST_EXCEEDED_LIMIT"
615
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
616
- when "fns:LOCK_COMPETITION"
617
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
618
- when "INVALID_SESSION"
619
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
620
- else
621
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
622
- end
796
+ raise_errors_helper(error: body["faultcode"], message: body["faultstring"], response: response)
623
797
  end
624
798
 
625
799
  if body["Errors"].present? || body["errors"].present?
626
- errors = []
627
- (body["Errors"] || []).select { |obj| errors.push(obj["Message"]) }.compact
628
- (body["errors"] || []).select { |obj| errors.push(obj["Message"]) }.compact
629
- if errors.size > 0
630
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{errors.join(", ")}", response, errors)
800
+ codes, messages = [[],[]]
801
+ body.fetch("Errors", []).each { |obj| messages.push(obj["Message"]); messages.push(obj["title"]); codes.push(obj["Code"]); codes.push(obj["code"]) }
802
+ body.fetch("errors", []).each { |obj| messages.push(obj["Message"]); messages.push(obj["title"]); codes.push(obj["Code"]); codes.push(obj["code"]) }
803
+ codes, messages = [codes.uniq.compact, messages.uniq.compact]
804
+ if codes.size > 0
805
+ if codes.size == 1
806
+ raise_errors_helper(error: codes.first, message: messages.first, response: response, errors: messages)
807
+ else
808
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages.join(", ")}", response, messages)
809
+ end
631
810
  end
632
811
  end
633
812
  end
@@ -645,7 +824,7 @@ module ZuoraAPI
645
824
  raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("Retry Lock Competition", response)
646
825
  elsif error_messages.first.include?("data integrity violation")
647
826
  raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
648
- end
827
+ end
649
828
  end
650
829
  end
651
830
 
@@ -654,15 +833,130 @@ module ZuoraAPI
654
833
  end
655
834
  end
656
835
 
836
+ if body.class == Hash && body['message'].present?
837
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(body['message'], response) if response.code == 500
838
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['message'], response) if ![200,201].include?(response.code)
839
+ end
840
+
841
+ self.errors_via_content_type(response: response, type: :json)
842
+
657
843
  #All other errors
658
- if ![200,201].include?(response.code)
659
- if response.code == 429
660
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("The total number of concurrent requests has exceeded the limit allowed by the system. Please resubmit your request later.", response)
844
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(response.body, response) if ![200,201].include?(response.code)
845
+ end
846
+ end
847
+
848
+ def errors_via_content_type(response: nil, type: :xml)
849
+ response_content_types = response.headers.transform_keys(&:downcase).fetch('content-type', []).first || ""
850
+
851
+ if response_content_types.include?('application/json') && type != :json
852
+ output_json = JSON.parse(response.body)
853
+ self.raise_errors(type: :JSON, body: output_json, response: response)
854
+
855
+ elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml')) and type != :xml
856
+ output_xml = Nokogiri::XML(response.body)
857
+ self.raise_errors(type: :SOAP, body: output_xml, response: response)
858
+
859
+ elsif response_content_types.include?('text/html')
860
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Akamai Error", response) if response.headers.fetch('server', '') == 'AkamaiGHost'
861
+
862
+ parse_body = Nokogiri::HTML(response.body)
863
+ error_title = parse_body.xpath('//h2').text
864
+ error_title = parse_body.xpath('//h1').text if error_title.blank?
865
+ error_message = parse_body.xpath('//p').text
866
+
867
+ error_message = error_title if error_message.blank?
868
+
869
+ if error_title.present?
870
+ case error_title
871
+ when /Service Unavailable/
872
+ raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new(error_message, response)
873
+ when /Client sent a bad request./, /Bad Request/, /403 Forbidden/
874
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
875
+ when /414 Request-URI Too Large/
876
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Request URL is too long", response)
877
+ else
878
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
879
+ end
880
+ end
881
+
882
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Http response body is missing", response) if response.body.blank?
883
+ end
884
+
885
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(response.body, response) if response.code == 500
886
+ end
887
+
888
+
889
+ def get_soap_error_and_message(body)
890
+ error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
891
+ message = body.xpath('//fns:FaultMessage', 'fns' =>'http://fault.api.zuora.com/').text
892
+
893
+ if error.blank? || message.blank?
894
+ error = body.xpath('//faultcode').text
895
+ message = body.xpath('//faultstring').text
896
+ end
897
+
898
+ if error.blank? || message.blank?
899
+ error = body.xpath('//ns1:Code', 'ns1' =>'http://api.zuora.com/').text
900
+ message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
901
+ end
902
+
903
+ #Update/Create/Delete Calls with multiple requests and responses
904
+ if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
905
+ error = []
906
+ success = []
907
+ body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
908
+ if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
909
+ message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
910
+ error.push(message)
661
911
  else
662
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{response.message}", response)
912
+ success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
663
913
  end
664
914
  end
665
915
  end
916
+ return error, success, message
917
+ end
918
+
919
+ def raise_errors_helper(error: nil, message: nil, response: nil, errors: [], success: [])
920
+ case error
921
+ when /.*INVALID_SESSION/
922
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response, errors, success)
923
+ when /.*REQUEST_EXCEEDED_LIMIT/
924
+ raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new(message, response, errors, success)
925
+ when /.*LOCK_COMPETITION/
926
+ raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(message, response, errors, success)
927
+ when /.*BATCH_FAIL_ERROR/
928
+ if message.include?("optimistic locking failed") || message.include?("Operation failed due to a lock competition, please retry later.")
929
+ raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(message, response, errors, success)
930
+ elsif message.include?("org.hibernate.exception.ConstraintViolationException")
931
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
932
+ end
933
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
934
+ when /.*TEMPORARY_ERROR/, /.*TRANSACTION_TIMEOUT/
935
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(message, response, errors, success)
936
+ when /.*INVALID_VALUE/
937
+ if message.include?("data integrity violation")
938
+ raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response, errors), success
939
+ end
940
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
941
+ when /.*UNKNOWN_ERROR/
942
+ if /payment\/refund|Credit Balance Adjustment|Payment Gateway|ARSettlement permission/.match(message).nil?
943
+ raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
944
+ end
945
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
946
+ when /^INVALID_VERSION/, /invalid/, /^DUPLICATE_VALUE/, /^REQUEST_REJECTED/, /INVALID_ID/, /MAX_RECORDS_EXCEEDED/, /INVALID_FIELD/, /MALFORMED_QUERY/, /NO_PERMISSION/, /PDF_QUERY_ERROR/, /MISSING_REQUIRED_VALUE/, /INVALID_TYPE/, /TRANSACTION_FAILED/, /API_DISABLED/, /CANNOT_DELETE/, /ACCOUNTING_PERIOD_CLOSED/
947
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
948
+ when /.*UNEXPECTED_ERROR/
949
+ raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(message, response, errors, success)
950
+ when /.*soapenv:Server.*/
951
+ if /^Invalid value.*for type.*|^Id is invalid|^date string can not be less than 19 charactors$/.match(message).present?
952
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
953
+ elsif /^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
954
+ raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
955
+ end
956
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
957
+ else
958
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Z:#{error}::#{message}", response, errors, success)
959
+ end
666
960
  end
667
961
 
668
962
  def aqua_query(queryName: '', query: '', version: '1.2', jobName: 'Aqua',partner: '', project: '')
@@ -709,7 +1003,7 @@ module ZuoraAPI
709
1003
  base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
710
1004
  url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
711
1005
  headers = self.entity_id.present? ? {"Zuora-Entity-Ids" => self.entity_id, 'Content-Type' => "text/xml; charset=utf-8"} : {'Content-Type' => "text/xml; charset=utf-8"}
712
- response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 120)
1006
+ response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 130)
713
1007
 
714
1008
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401', response) if response.code == 401
715
1009
 
@@ -742,105 +1036,161 @@ module ZuoraAPI
742
1036
  end
743
1037
  des_hash[:related_objects] = output_xml.xpath(".//related-objects").xpath(".//object").map{ |x| [x.xpath(".//name").text.to_sym, [ [:url, x.attributes["href"].value], [:label, x.xpath(".//name").text ] ].to_h] }.to_h
744
1038
  end
745
- rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS) => ex
746
- if !(tries -= 1).zero?
747
- Rails.logger.info("Describe - #{ex.class} Timed out will retry after 5 seconds")
1039
+
1040
+ return des_hash
1041
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
1042
+ if !tries.zero?
1043
+ tries -= 1
1044
+ self.log(location: "Describe", exception: ex, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :debug)
748
1045
  sleep(self.timeout_sleep)
749
1046
  retry
750
- else
751
- raise ex
752
1047
  end
1048
+
1049
+ self.log(location: "Describe", exception: ex, message: "Timed out", level: :error) if log_errors
1050
+ raise ex
1051
+
753
1052
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
754
- if !(tries -= 1).zero? && self.status == 'Active'
1053
+ if !tries.zero? && self.status == 'Active'
1054
+ tries -= 1
755
1055
  Rails.logger.debug("Describe session expired. Starting new session.")
756
1056
  self.new_session
757
1057
  retry
758
- else
759
- Rails.logger.error("Describe session expired. Starting new session.") if log_errors
760
- raise ex
761
1058
  end
1059
+
1060
+ Rails.logger.error("Describe session expired. Starting new session.") if log_errors
1061
+ raise ex
762
1062
  rescue => ex
763
1063
  raise ex
764
- else
765
- return des_hash
766
1064
  end
767
1065
 
768
- def rest_call(method: :get, body: nil, headers: {}, url: rest_endpoint("catalog/products?pageSize=4"), debug: false, errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS), z_session: true, session_type: :basic, timeout_retry: false, timeout: 120, **keyword_args)
1066
+ def rest_call(
1067
+ method: :get,
1068
+ body: nil,
1069
+ headers: {},
1070
+ url: rest_endpoint("catalog/products?pageSize=4"),
1071
+ debug: false,
1072
+ errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
1073
+ z_session: true,
1074
+ session_type: :basic,
1075
+ timeout_retry: false,
1076
+ timeout: 130,
1077
+ timeout_sleep_interval: self.timeout_sleep,
1078
+ multipart: false,
1079
+ stream_body: false,
1080
+ output_exception_messages: true,
1081
+ zuora_track_id: nil,
1082
+ **keyword_args,
1083
+ &block
1084
+ )
769
1085
  tries ||= 2
770
1086
 
771
- if self.entity_id.present?
772
- headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
773
- headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
774
- end
775
-
776
1087
  raise "Method not supported, supported methods include: :get, :post, :put, :delete, :patch, :head, :options" if ![:get, :post, :put, :delete, :patch, :head, :options].include?(method)
777
1088
 
778
- authentication_headers = z_session ? {"Authorization" => self.get_session(prefix: true, auth_type: session_type) } : {}
1089
+ authentication_headers = {}
1090
+ if z_session
1091
+ authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type, zuora_track_id: zuora_track_id) }
1092
+ if self.entity_id.present?
1093
+ authentication_headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
1094
+ authentication_headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
1095
+ end
1096
+ end
1097
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
1098
+
779
1099
  modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
780
1100
 
781
- response = HTTParty::Request.new("Net::HTTP::#{method.to_s.capitalize}".constantize, url, body: body, headers: modified_headers, timeout: timeout).perform
782
- Rails.logger.debug("Response Code: #{response.code}") if debug
783
1101
  begin
784
- output_json = JSON.parse(response.body)
785
- rescue JSON::ParserError => ex
786
- output_json = {}
1102
+ request = HTTParty::Request.new(
1103
+ "Net::HTTP::#{method.to_s.capitalize}".constantize,
1104
+ url,
1105
+ body: body,
1106
+ headers: modified_headers,
1107
+ timeout: timeout,
1108
+ multipart: multipart,
1109
+ stream_body: stream_body
1110
+ )
1111
+
1112
+ response = request.perform(&block)
1113
+
1114
+ Rails.logger.debug("Response Code: #{response.code}") if debug
1115
+ begin
1116
+ output_json = JSON.parse(response.body)
1117
+ rescue JSON::ParserError => ex
1118
+ output_json = {}
1119
+ end
1120
+ Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
1121
+
1122
+ raise_errors(type: :JSON, body: output_json, response: response)
1123
+ rescue => ex
1124
+ reset_files(body) if multipart
1125
+ raise
787
1126
  end
788
- Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
789
1127
 
790
- raise_errors(type: :JSON, body: output_json, response: response)
1128
+ return [output_json, response]
791
1129
  rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
792
1130
  if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
793
- self.rest_call(method: method.to_sym, url: url, body: body, debug: debug, errors: errors, z_session: z_session, session_type: :bearer, timeout_retry: timeout_retry, timeout: timeout)
794
- else
795
- Rails.logger.debug("Rest Call - Session Bad Auth type")
796
- raise ex
1131
+ session_type = :bearer
1132
+ retry
797
1133
  end
1134
+ Rails.logger.debug("Rest Call - Session Bad Auth type")
1135
+ raise ex
1136
+
798
1137
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
799
- if !(tries -= 1).zero? && z_session
1138
+ if !tries.zero? && z_session
1139
+ tries -= 1
800
1140
  Rails.logger.debug("Rest Call - Session Invalid #{session_type}")
801
- self.new_session(auth_type: session_type)
802
- retry
803
- else
804
- if errors.include?(ex.class)
805
- raise ex
806
- else
807
- return [output_json, response]
1141
+
1142
+ begin
1143
+ self.new_session(auth_type: session_type)
1144
+ rescue *ZUORA_API_ERRORS => ex
1145
+ return [output_json, ex.response]
808
1146
  end
1147
+
1148
+ retry
809
1149
  end
1150
+
1151
+ raise ex if errors.include?(ex.class)
1152
+ return [output_json, response]
1153
+
810
1154
  rescue *ZUORA_API_ERRORS => ex
811
- if errors.include?(ex.class)
812
- raise ex
813
- else
814
- return [output_json, response]
815
- end
1155
+ raise ex if errors.include?(ex.class)
1156
+
1157
+ response = ex.response unless response
1158
+ return [output_json, response]
1159
+
816
1160
  rescue ZuoraAPI::Exceptions::BadEntityError => ex
817
1161
  raise ex
818
1162
  rescue *CONNECTION_EXCEPTIONS => ex
819
- if !(tries -= 1).zero?
820
- Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
821
- sleep(self.timeout_sleep)
1163
+ if !tries.zero?
1164
+ tries -= 1
1165
+ self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
1166
+ sleep(timeout_sleep_interval)
822
1167
  retry
823
- else
824
- raise ex
825
1168
  end
1169
+
1170
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1171
+ raise ex
1172
+
826
1173
  rescue *CONNECTION_READ_EXCEPTIONS => ex
827
- if !(tries -= 1).zero? && timeout_retry
828
- Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
829
- sleep(self.timeout_sleep)
830
- retry
831
- else
832
- raise ex
833
- end
834
- rescue Errno::ECONNRESET => ex
835
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
836
- retry
837
- else
838
- raise ex
1174
+
1175
+ if !tries.zero?
1176
+ tries -= 1
1177
+ self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
1178
+ if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
1179
+ retry
1180
+ elsif timeout_retry
1181
+ sleep(timeout_sleep_interval)
1182
+ retry
1183
+ end
839
1184
  end
1185
+
1186
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1187
+ ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
1188
+ raise ex
1189
+
840
1190
  rescue => ex
841
1191
  raise ex
842
- else
843
- return [output_json, response]
1192
+ ensure
1193
+ self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
844
1194
  end
845
1195
 
846
1196
  def update_create_tenant
@@ -862,8 +1212,9 @@ module ZuoraAPI
862
1212
  while !response["nextPage"].blank?
863
1213
  url = self.rest_endpoint(response["nextPage"].split('/v1/').last)
864
1214
  Rails.logger.debug("Fetch Catalog URL #{url}")
865
- output_json, response = self.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true )
866
- if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
1215
+ output_json, response = self.rest_call(debug: false, url: url, timeout_retry: true)
1216
+
1217
+ if !/(true|t|yes|y|1)$/.match(output_json['success'].to_s) || output_json['success'].class != TrueClass
867
1218
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}", response)
868
1219
  end
869
1220
  output_json["products"].each do |product|
@@ -889,9 +1240,11 @@ module ZuoraAPI
889
1240
  return products, catalog_map
890
1241
  end
891
1242
 
892
- def get_file(url: nil, headers: {}, z_session: true, tempfile: true, output_file_name: nil, add_timestamp: true, file_path: defined?(Rails.root.join('tmp')) ? Rails.root.join('tmp') : Pathname.new(Dir.pwd), timeout_retries: 3, timeout: 120, session_type: :basic, **execute_params)
1243
+ def get_file(url: nil, headers: {}, z_session: true, tempfile: true, output_file_name: nil, zuora_track_id: nil, add_timestamp: true, file_path: defined?(Rails.root.join('tmp')) ? Rails.root.join('tmp') : Pathname.new(Dir.pwd), timeout_retries: 3, timeout: 130, session_type: :basic, **execute_params)
893
1244
  raise "file_path must be of class Pathname" if file_path.class != Pathname
894
1245
 
1246
+ retry_count ||= timeout_retries
1247
+
895
1248
  #Make sure directory exists
896
1249
  require 'fileutils'
897
1250
  FileUtils.mkdir_p(file_path) unless File.exists?(file_path)
@@ -906,8 +1259,9 @@ module ZuoraAPI
906
1259
  headers = headers.merge({"Zuora-Entity-Ids" => self.entity_id}) if !self.entity_id.blank?
907
1260
  end
908
1261
 
1262
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
1263
+
909
1264
  response_save = nil
910
- retry_count ||= timeout_retries
911
1265
  http.request_get(uri.request_uri, headers) do |response|
912
1266
  response_save = response
913
1267
  status_code = response.code if response
@@ -991,36 +1345,42 @@ module ZuoraAPI
991
1345
  return file_handle
992
1346
  when Net::HTTPUnauthorized
993
1347
  if z_session
994
- if !(retry_count -= 1).zero?
1348
+ unless (retry_count -= 1).zero?
995
1349
  self.new_session
996
- raise response.class
997
- else
998
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1350
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError, 'Retrying'
999
1351
  end
1352
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1353
+ end
1354
+ raise
1355
+ when Net::HTTPNotFound
1356
+ if url.include?(self.fileURL)
1357
+ raise ZuoraAPI::Exceptions::FileDownloadError.new(
1358
+ "The current tenant does not have a file with id '#{url.split('/').last}'"
1359
+ )
1000
1360
  else
1001
- raise
1361
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1002
1362
  end
1003
1363
  else
1004
- raise StandardError.new("File Download Failed #{response.class}")
1364
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1005
1365
  end
1006
1366
  end
1007
-
1008
- rescue => ex
1367
+
1368
+ rescue => ex
1009
1369
  sleep(5)
1010
1370
  if (retry_count -= 1) >= 0
1011
1371
  retry
1012
- else
1013
- Rails.logger.error("File Download Failed")
1014
- raise
1015
1372
  end
1373
+ Rails.logger.error("File Download Failed")
1374
+ raise
1016
1375
  end
1017
1376
 
1018
- def getDataSourceExport(query, extract: true, encrypted: false, zip: true)
1377
+ def getDataSourceExport(query, extract: true, encrypted: false, zip: true, z_track_id: "")
1378
+ tries ||= 3
1019
1379
  request = Nokogiri::XML::Builder.new do |xml|
1020
1380
  xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/", 'xmlns:ns2' => "http://object.api.zuora.com/", 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:ns1' => "http://api.zuora.com/") do
1021
1381
  xml['SOAP-ENV'].Header do
1022
1382
  xml['ns1'].SessionHeader do
1023
- xml['ns1'].session self.get_session(prefix: false, auth_type: :basic)
1383
+ xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1024
1384
  end
1025
1385
  end
1026
1386
  xml['SOAP-ENV'].Body do
@@ -1037,17 +1397,20 @@ module ZuoraAPI
1037
1397
  end
1038
1398
  end
1039
1399
 
1040
- response_query = HTTParty.post(self.url, body: request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8"}, :timeout => 120)
1400
+ response_query = HTTParty.post(self.url, body: request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8", "Z-Track-Id" => z_track_id}, :timeout => 130)
1041
1401
 
1042
1402
  output_xml = Nokogiri::XML(response_query.body)
1043
- raise 'Export Creation Unsuccessful : ' + output_xml.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1403
+ raise_errors(type: :SOAP, body: output_xml, response: response_query) if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1404
+
1405
+ # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1406
+
1044
1407
  id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
1045
1408
 
1046
1409
  confirmRequest = Nokogiri::XML::Builder.new do |xml|
1047
1410
  xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/", 'xmlns:ns2' => "http://object.api.zuora.com/", 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:ns1' => "http://api.zuora.com/") do
1048
1411
  xml['SOAP-ENV'].Header do
1049
1412
  xml['ns1'].SessionHeader do
1050
- xml['ns1'].session self.get_session(prefix: false, auth_type: :basic)
1413
+ xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1051
1414
  end
1052
1415
  end
1053
1416
  xml['SOAP-ENV'].Body do
@@ -1061,12 +1424,14 @@ module ZuoraAPI
1061
1424
 
1062
1425
  while result != "Completed"
1063
1426
  sleep 3
1064
- response_query = HTTParty.post(self.url, body: confirmRequest.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8"}, :timeout => 120)
1427
+ response_query = HTTParty.post(self.url, body: confirmRequest.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8", "Z-Track-Id" => z_track_id}, :timeout => 130)
1065
1428
 
1066
1429
  output_xml = Nokogiri::XML(response_query.body)
1067
1430
  result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
1068
1431
  status_code = response_query.code if response_query
1069
- raise "Export Creation Unsuccessful : #{output_xml.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text}" if result.blank? || result == "Failed"
1432
+
1433
+ raise_errors(type: :SOAP, body: output_xml, response: response_query) if result.blank? || result == "Failed"
1434
+ # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if result.blank? || result == "Failed"
1070
1435
  end
1071
1436
 
1072
1437
  file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
@@ -1085,10 +1450,39 @@ module ZuoraAPI
1085
1450
  else
1086
1451
  return export_file_path
1087
1452
  end
1453
+ rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
1454
+ if !(tries -= 1).zero?
1455
+ Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
1456
+ self.new_session
1457
+ retry
1458
+ end
1459
+ raise ex
1460
+
1461
+ rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
1462
+ if !(tries -= 1).zero?
1463
+ Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
1464
+ sleep 10
1465
+ retry
1466
+ end
1467
+ raise ex
1468
+
1469
+ rescue *ZUORA_API_ERRORS => ex
1470
+ raise ex
1471
+
1472
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
1473
+ if !(tries -= 1).zero?
1474
+ Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
1475
+ sleep 5
1476
+ retry
1477
+ end
1478
+ raise ex
1479
+
1480
+ rescue ZuoraAPI::Exceptions::BadEntityError => ex
1481
+ raise ex
1088
1482
  end
1089
1483
 
1090
1484
  def query(query, parse = false)
1091
- output_xml, input_xml = self.soap_call({:debug => false, :timeout_retry => true}) do |xml|
1485
+ output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true) do |xml|
1092
1486
  xml['ns1'].query do
1093
1487
  xml['ns1'].queryString query
1094
1488
  end
@@ -1150,5 +1544,17 @@ module ZuoraAPI
1150
1544
  end
1151
1545
  return "failure"
1152
1546
  end
1547
+
1548
+ def reset_files(body)
1549
+ return unless body.is_a? Hash
1550
+
1551
+ body.transform_values! do |v|
1552
+ if v.is_a?(File)
1553
+ v.reopen(v.path)
1554
+ else
1555
+ v
1556
+ end
1557
+ end
1558
+ end
1153
1559
  end
1154
1560
  end