zuora_api 1.7.03 → 1.7.7b

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