zuora_api 1.7.06 → 1.7.7a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f857a810633aad60e80293f6400b9fa8fab38bff15537589e4ffe0ef2aca808d
4
- data.tar.gz: d4512de5de2f7eb888dc32ca7d283fee4f366ad7a96c8dc619bd8ebd83b8f55d
3
+ metadata.gz: 2697794114e89b4d76001a4746c1148229aeada880a18ab2cd3a97673549d2c6
4
+ data.tar.gz: 7a66c8c6b0d604bc9d1229424ecca8a557c6c36a84304049970988cbf7e6c771
5
5
  SHA512:
6
- metadata.gz: 5d2a72796a2b442627768876ac6954b10aff3be6ea61b11375f4bd2d305b970c5887d26dbf0962b8876bb7575cfcf9a3c49e56d0a8ae0d6fe71ed4078fe04bd6
7
- data.tar.gz: c957560ae8e70276ff8c9fd6d2e9777af145d837bbb93ee1114f36ec7946ab59033820113b93872c4b42855be40c86e04a979255f839908facc9fb4ea4faa350
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,9 +210,9 @@ 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
127
- @message = message
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)
128
216
  @response = response
129
217
  @default_message = "Authentication type is not supported by this Login"
130
218
  end
@@ -138,8 +226,8 @@ module ZuoraAPI
138
226
  attr_reader :code, :response
139
227
  attr_writer :default_message
140
228
 
141
- def initialize(message = nil,response=nil, *args)
142
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
229
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
230
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
143
231
  @message = message
144
232
  @response = response
145
233
  @default_message = "Authentication type is not supported by this Login"
@@ -151,13 +239,14 @@ module ZuoraAPI
151
239
  end
152
240
 
153
241
  class ZuoraAPIReadTimeout < Net::ReadTimeout
154
- attr_reader :code, :response
242
+ attr_reader :code, :response, :request
155
243
  attr_writer :default_message
156
244
 
157
- def initialize(message = nil,response=nil, *args)
158
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
245
+ def initialize(message = nil, response = nil, request = nil, errors = [], successes = [], *args)
246
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
159
247
  @message = message
160
248
  @response = response
249
+ @request = request
161
250
  @default_message = "Authentication type is not supported by this Login"
162
251
  end
163
252
 
@@ -5,7 +5,7 @@ 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
@@ -16,7 +16,8 @@ module ZuoraAPI
16
16
  Errno::ECONNREFUSED,
17
17
  SocketError,
18
18
  Errno::EHOSTUNREACH,
19
- Errno::EADDRNOTAVAIL
19
+ Errno::EADDRNOTAVAIL,
20
+ Errno::ETIMEDOUT,
20
21
  ].freeze
21
22
 
22
23
  CONNECTION_READ_EXCEPTIONS = [
@@ -30,7 +31,17 @@ module ZuoraAPI
30
31
  ZuoraAPI::Exceptions::ZuoraAPIRequestLimit,
31
32
  ZuoraAPI::Exceptions::ZuoraAPILockCompetition,
32
33
  ZuoraAPI::Exceptions::ZuoraAPITemporaryError,
33
- ZuoraAPI::Exceptions::ZuoraDataIntegrity
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
34
45
  ].freeze
35
46
 
36
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
@@ -39,10 +50,10 @@ module ZuoraAPI
39
50
  raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
40
51
  # raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
41
52
  self.hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
42
- if !/apps\/services\/a\/\d{2}\.\d$/.match(url.strip)
53
+ if !/apps\/services\/a\/\d+\.\d$/.match(url.strip)
43
54
  self.url = "https://#{hostname}/apps/services/a/#{MIN_Endpoint}"
44
- elsif MIN_Endpoint.to_f > url.scan(/(\d{2}\.\d)$/).dig(0,0).to_f
45
- 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)
46
57
  else
47
58
  self.url = url
48
59
  end
@@ -61,27 +72,12 @@ module ZuoraAPI
61
72
 
62
73
  def get_identity(cookies)
63
74
  zsession = cookies["ZSession"]
64
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
65
75
  begin
66
76
  if !zsession.blank?
67
77
  response = HTTParty.get("https://#{self.hostname}/apps/v1/identity", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
68
78
  output_json = JSON.parse(response.body)
69
- elsif zconnect_accesstoken.present?
70
- begin
71
- code = zconnect_accesstoken.split("#!").last
72
- encrypted_token, tenant_id = Base64.decode64(code).split(":")
73
- body = {'token' => encrypted_token}.to_json
74
- rescue => ex
75
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Invalid ZConnect Cookie")
76
- end
77
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/identity", :body => body, :headers => { 'Content-Type' => 'application/json' })
78
- output_json = JSON.parse(response.body)
79
79
  else
80
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
81
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
82
- else
83
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
84
- end
80
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
85
81
  end
86
82
  rescue JSON::ParserError => ex
87
83
  output_json = {}
@@ -92,20 +88,12 @@ module ZuoraAPI
92
88
 
93
89
  def get_full_nav(cookies)
94
90
  zsession = cookies["ZSession"]
95
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
96
91
  begin
97
92
  if zsession.present?
98
93
  response = HTTParty.get("https://#{self.hostname}/apps/v1/navigation", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
99
94
  output_json = JSON.parse(response.body)
100
- elsif zconnect_accesstoken.present?
101
- response = HTTParty.get("https://#{self.hostname}/apps/zconnectsession/navigation", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}",'Content-Type' => 'application/json'})
102
- output_json = JSON.parse(response.body)
103
95
  else
104
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
105
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
106
- else
107
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
108
- end
96
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
109
97
  end
110
98
  rescue JSON::ParserError => ex
111
99
  output_json = {}
@@ -116,20 +104,12 @@ module ZuoraAPI
116
104
 
117
105
  def set_nav(state, cookies)
118
106
  zsession = cookies["ZSession"]
119
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
120
107
  begin
121
108
  if !zsession.blank?
122
109
  response = HTTParty.put("https://#{self.hostname}/apps/v1/preference/navigation", :body => state.to_json, :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
123
110
  output_json = JSON.parse(response.body)
124
- elsif !zconnect_accesstoken.blank?
125
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/navigationstate", :body => state.to_json, :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
126
- output_json = JSON.parse(response.body)
127
111
  else
128
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
129
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
130
- else
131
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
132
- end
112
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
133
113
  end
134
114
  rescue JSON::ParserError => ex
135
115
  output_json = {}
@@ -140,20 +120,12 @@ module ZuoraAPI
140
120
 
141
121
  def refresh_nav(cookies)
142
122
  zsession = cookies["ZSession"]
143
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
144
123
  begin
145
124
  if !zsession.blank?
146
125
  response = HTTParty.post("https://#{self.hostname}/apps/v1/navigation/fetch", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
147
126
  output_json = JSON.parse(response.body)
148
- elsif !zconnect_accesstoken.blank?
149
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/refresh-navbarcache", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
150
- output_json = JSON.parse(response.body)
151
127
  else
152
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
153
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
154
- else
155
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
156
- end
128
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
157
129
  end
158
130
  rescue JSON::ParserError => ex
159
131
  output_json = {}
@@ -162,27 +134,21 @@ module ZuoraAPI
162
134
  return output_json
163
135
  end
164
136
 
165
- def get_zconnect_accesstoken(cookies)
166
- accesstoken = nil
167
- self.update_zconnect_provider
168
- if !cookies[self.zconnect_provider].nil? && !cookies[self.zconnect_provider].empty?
169
- accesstoken = cookies[self.zconnect_provider]
170
- end
171
- return accesstoken
172
- end
173
-
174
137
  def reporting_url(path)
175
138
  map = {"US" => {"Sandbox" => "https://zconnectsandbox.zuora.com/api/rest/v1/",
176
139
  "Production" => "https://zconnect.zuora.com/api/rest/v1/",
177
- "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/"},
178
144
  "EU" => {"Sandbox" => "https://zconnect.sandbox.eu.zuora.com/api/rest/v1/",
179
145
  "Production" => "https://zconnect.eu.zuora.com/api/rest/v1/",
180
- "Services"=> ""},
146
+ "Services"=> "https://reporting-sbx0000.sbx.aec1.zuora.com/api/rest/v1/"},
181
147
  "NA" => {"Sandbox" => "https://zconnect.sandbox.na.zuora.com/api/rest/v1/",
182
148
  "Production" => "https://zconnect.na.zuora.com/api/rest/v1/",
183
149
  "Services"=> ""}
184
150
  }
185
- return map[zuora_client.region][zuora_client.environment].insert(-1, path)
151
+ return map[self.region][self.environment].insert(-1, path)
186
152
  end
187
153
 
188
154
  # There are two ways to call this method. The first way is best.
@@ -232,7 +198,7 @@ module ZuoraAPI
232
198
  end
233
199
 
234
200
  def self.environments
235
- %w(Sandbox Production Services Performance Staging)
201
+ %w(Sandbox Production Services Performance Staging Test)
236
202
  end
237
203
 
238
204
  def self.regions
@@ -244,11 +210,13 @@ module ZuoraAPI
244
210
  "Production" => "https://www.zuora.com/apps/services/a/",
245
211
  "Performance" => "https://pt1.zuora.com/apps/services/a/",
246
212
  "Services" => "https://services347.zuora.com/apps/services/a/",
247
- "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/"},
248
215
  "EU" => {"Sandbox" => "https://sandbox.eu.zuora.com/apps/services/a/",
249
216
  "Production" => "https://eu.zuora.com/apps/services/a/",
250
217
  "Performance" => "https://pt1.eu.zuora.com/apps/services/a/",
251
- "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/"},
252
220
  "NA" => {"Sandbox" => "https://sandbox.na.zuora.com/apps/services/a/",
253
221
  "Production" => "https://na.zuora.com/apps/services/a/",
254
222
  "Performance" => "https://pt1.na.zuora.com/apps/services/a/",
@@ -285,15 +253,18 @@ module ZuoraAPI
285
253
 
286
254
  def update_environment
287
255
  if !self.url.blank?
288
- if /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/ === self.hostname
256
+ case self.hostname
257
+ when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
289
258
  self.environment = 'Sandbox'
290
- elsif /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/ === self.hostname
259
+ when /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/
291
260
  self.environment = 'Services'
292
- elsif /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/ === self.hostname
261
+ when /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/
293
262
  self.environment = 'Performance'
294
- elsif /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/ === self.hostname
263
+ when /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/
295
264
  self.environment = 'Staging'
296
- elsif is_prod_env
265
+ when /(?<=\.|\/|^)(test)(?=\.|\/|$)/
266
+ self.environment = 'Test'
267
+ when /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/, /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/
297
268
  self.environment = 'Production'
298
269
  else
299
270
  self.environment = 'Unknown'
@@ -303,25 +274,14 @@ module ZuoraAPI
303
274
  end
304
275
  end
305
276
 
306
- def is_prod_env
307
- is_prod = false
308
- www_or_api = /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/ === self.hostname
309
- host_prefix_match = /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/ === self.hostname
310
- if www_or_api || host_prefix_match
311
- is_prod = true
312
- end
313
- return is_prod
314
- end
315
-
316
277
  def update_zconnect_provider
317
278
  region = update_region
318
279
  environment = update_environment
319
- mappings = {"US" => {"Sandbox" => "ZConnectSbx", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev", "Services" => "ZConnectQA", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Staging" => "ZConnectQA"},
320
- "NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectQANA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
321
- "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"},
322
283
  "Unknown" => {"Unknown" => "Unknown"}}
323
284
  self.zconnect_provider = mappings[region][environment]
324
- # raise "Can't find ZConnect Provider for #{region} region and #{environment} environment" if self.zconnect_provider.nil?
325
285
  end
326
286
 
327
287
  def aqua_endpoint(url="")
@@ -334,45 +294,35 @@ module ZuoraAPI
334
294
  return "#{url_slash_apps_slash}api/#{url}"
335
295
  end
336
296
 
337
- def rest_endpoint(url="")
297
+ def rest_endpoint(url="", domain=true, prefix='/v1/')
338
298
  update_environment
339
299
  endpoint = url
340
-
300
+ url_postfix = {"US" => ".", "EU" => ".eu.", "NA" => ".na."}[self.region]
301
+
341
302
  case self.environment
303
+ when 'Test'
304
+ endpoint = "https://rest.test#{url_postfix}zuora.com"
342
305
  when 'Sandbox'
343
- case self.region
344
- when 'US'
345
- endpoint = "https://rest.apisandbox.zuora.com/v1/".concat(url)
346
- when 'EU'
347
- endpoint = "https://rest.sandbox.eu.zuora.com/v1/".concat(url)
348
- when 'NA'
349
- endpoint = "https://rest.sandbox.na.zuora.com/v1/".concat(url)
350
- end
306
+ endpoint = "https://rest.sandbox#{url_postfix}zuora.com"
307
+ endpoint = "https://rest.apisandbox.zuora.com" if self.region == "US"
351
308
  when 'Production'
352
- case self.region
353
- when 'US'
354
- endpoint = "https://rest.zuora.com/v1/".concat(url)
355
- when 'EU'
356
- endpoint = "https://rest.eu.zuora.com/v1/".concat(url)
357
- when 'NA'
358
- endpoint = "https://rest.na.zuora.com/v1/".concat(url)
359
- end
309
+ endpoint = "https://rest#{url_postfix}zuora.com"
360
310
  when 'Performance'
361
- endpoint = "https://rest.pt1.zuora.com/v1/".concat(url)
311
+ endpoint = "https://rest.pt1.zuora.com"
362
312
  when 'Services'
363
313
  https = /https:\/\/|http:\/\//.match(self.url)[0]
364
314
  host = self.hostname
365
- endpoint = "#{https}rest#{host}/v1/#{url}"
315
+ endpoint = "#{https}rest#{host}"
366
316
  when 'Staging'
367
- endpoint = "https://rest-staging2.zuora.com/v1/".concat(url)
317
+ endpoint = "https://rest-staging2.zuora.com"
368
318
  when 'Unknown'
369
319
  raise "Environment unknown, returning passed in parameter unaltered"
370
320
  end
371
- return endpoint
321
+ return domain ? endpoint.concat(prefix).concat(url) : prefix.concat(url)
372
322
  end
373
323
 
374
- def rest_domain
375
- return URI(self.rest_endpoint).host
324
+ def rest_domain(endpoint: self.rest_endpoint)
325
+ return URI(endpoint).host
376
326
  end
377
327
 
378
328
  def fileURL(url="")
@@ -383,43 +333,89 @@ module ZuoraAPI
383
333
  return self.wsdl_number > 68 ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'
384
334
  end
385
335
 
386
- 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
+
387
369
  end
388
370
 
389
- def get_session(prefix: false, auth_type: :basic)
390
- 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)
391
372
  case auth_type
392
373
  when :basic
393
374
  if self.current_session.blank?
394
375
  case self.class.to_s
395
376
  when 'ZuoraAPI::Oauth'
396
377
  if self.bearer_token.blank? || self.oauth_expired?
397
- self.new_session(auth_type: :bearer)
378
+ self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
398
379
  end
399
- self.get_z_session if self.status == 'Active'
380
+ self.get_z_session(zuora_track_id: zuora_track_id)
400
381
  when 'ZuoraAPI::Basic'
401
- self.new_session(auth_type: :basic)
382
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
402
383
  else
403
- raise "No Zuora Login Specified"
384
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
404
385
  end
405
386
  end
406
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
407
387
  return prefix ? "ZSession #{self.current_session}" : self.current_session.to_s
408
388
  when :bearer
409
389
  case self.class.to_s
410
390
  when 'ZuoraAPI::Oauth'
411
391
  if self.bearer_token.blank? || self.oauth_expired?
412
- self.new_session(auth_type: :bearer)
392
+ self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
413
393
  end
414
394
  when 'ZuoraAPI::Basic'
415
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}")
416
398
  end
417
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
418
399
  return prefix ? "Bearer #{self.bearer_token}" : self.bearer_token.to_s
419
400
  end
420
401
  end
421
402
 
422
- 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)
423
419
  tries ||= 2
424
420
  xml = Nokogiri::XML::Builder.new do |xml|
425
421
  xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/",
@@ -428,8 +424,10 @@ module ZuoraAPI
428
424
  'xmlns:api' => "http://api.zuora.com/",
429
425
  "xmlns:#{ns1}" => "http://api.zuora.com/") do
430
426
  xml['SOAP-ENV'].Header do
431
- xml["#{ns1}"].SessionHeader do
432
- 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
433
431
  end
434
432
  if single_transaction
435
433
  xml["#{ns1}"].CallOptions do
@@ -447,96 +445,212 @@ module ZuoraAPI
447
445
  end
448
446
  end
449
447
  end
450
-
451
448
  input_xml = Nokogiri::XML(xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip)
452
449
  input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
453
450
  Rails.logger.debug("Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
454
451
 
455
- 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
+
456
465
  output_xml = Nokogiri::XML(response.body)
457
466
  Rails.logger.debug("Response SOAP XML: #{output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
458
467
 
459
468
  raise_errors(type: :SOAP, body: output_xml, response: response)
469
+
470
+ return output_xml, input_xml, response
471
+
460
472
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
461
- if !(tries -= 1).zero? && z_session
473
+ raise if skip_session
474
+ if !tries.zero? && z_session
475
+ tries -= 1
462
476
  Rails.logger.debug("SOAP Call - Session Invalid")
463
- self.new_session(auth_type: :basic)
464
- retry
465
- else
466
- if errors.include?(ex.class)
467
- raise ex
468
- else
469
- 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
470
482
  end
483
+
484
+ retry
471
485
  end
486
+
487
+ raise ex if errors.include?(ex.class)
488
+
489
+ return output_xml, input_xml, response
490
+
472
491
  rescue *ZUORA_API_ERRORS => ex
473
- if errors.include?(ex.class)
474
- raise ex
475
- else
476
- return output_xml, input_xml, response
477
- end
492
+ raise ex if errors.include?(ex.class)
493
+
494
+ response = ex.response unless response
495
+ return output_xml, input_xml, response
496
+
478
497
  rescue *CONNECTION_EXCEPTIONS => ex
479
- if !(tries -= 1).zero?
480
- Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
481
- 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)
482
502
  retry
483
- else
484
- raise ex
485
503
  end
504
+
505
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
506
+ raise ex
507
+
486
508
  rescue *CONNECTION_READ_EXCEPTIONS => ex
487
- if !(tries -= 1).zero? && timeout_retry
488
- Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
489
- sleep(self.timeout_sleep)
490
- retry
491
- else
492
- 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
493
518
  end
494
- rescue Errno::ECONNRESET => ex
495
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
496
- 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)
497
548
  else
498
- 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?
499
588
  end
500
589
  rescue => ex
501
- raise ex
502
- else
503
- return output_xml, input_xml, response
590
+ Rails.logger.error("Failed to create exception arguments", ex, args)
591
+ ensure
592
+ return args
504
593
  end
505
594
 
506
595
  def raise_errors(type: :SOAP, body: nil, response: nil)
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
607
+
507
608
  if [502,503].include?(response.code)
508
- raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from downstream host", response)
609
+ raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
509
610
  end
510
611
 
511
- if response.code == 504
512
- raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from downstream host", response)
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
629
+ end
513
630
  end
514
631
 
515
632
  case type
516
633
  when :SOAP
517
- error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
518
- message = body.xpath('//fns:FaultMessage', 'fns' =>'http://fault.api.zuora.com/').text
519
-
520
- if error.blank? || message.blank?
521
- error = body.xpath('//faultcode').text
522
- message = body.xpath('//faultstring').text
523
- end
634
+ error, success, message = get_soap_error_and_message(body)
524
635
 
525
- if error.blank? || message.blank?
526
- error = body.xpath('//ns1:Code', 'ns1' =>'http://api.zuora.com/').text
527
- 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)
528
638
  end
529
639
 
530
- #Update/Create/Delete Calls with multiple requests and responses
531
- if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
532
- error = []
533
- success = []
534
- body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
535
- if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
536
- message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
537
- 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?
538
652
  else
539
- success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
653
+ message = 'Export failed due to unknown reason. Consult api logs.'
540
654
  end
541
655
  end
542
656
  end
@@ -544,44 +658,52 @@ module ZuoraAPI
544
658
  #By default response if not passed in for SOAP as all SOAP is 200
545
659
  if error.present?
546
660
  if error.class == String
547
- case error
548
- when "INVALID_SESSION"
549
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{error}::#{message}", response)
550
- when "REQUEST_EXCEEDED_LIMIT"
551
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{error}::#{message}", response)
552
- when "LOCK_COMPETITION"
553
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
554
- when "BATCH_FAIL_ERROR"
555
- if message.include?("optimistic locking failed")
556
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
557
- else
558
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
559
- end
560
- when "TEMPORARY_ERROR"
561
- raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{error}::#{message}", response)
562
- when "INVALID_VALUE"
563
- if message.include?("data integrity violation")
564
- raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
565
- else
566
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
567
- end
568
- else
569
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response) if error.present?
570
- end
661
+ raise_errors_helper(error: error, message: message, response: response)
571
662
  elsif error.class == Array
572
- if error[0].include?("LOCK_COMPETITION") && error.count == 1
573
- 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)
574
666
  else
575
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)
576
668
  end
577
669
  end
578
670
  end
671
+
672
+ self.errors_via_content_type(response: response, type: :xml)
579
673
 
580
- if response.code == 429
581
- 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
582
705
  end
583
706
 
584
- when :JSON
585
707
  body = body.dig("results").present? ? body["results"] : body if body.class == Hash
586
708
  if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
587
709
  messages_array = body.fetch("reasons", []).map {|error| error['message']}.compact
@@ -589,6 +711,14 @@ module ZuoraAPI
589
711
  codes_array = body.fetch("reasons", []).map {|error| error['code']}.compact
590
712
  codes_array = codes_array.push(body.dig("error", 'code')).compact if body.dig('error').class == Hash
591
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
+
592
722
  if body['message'] == "No bearer token" && response.code == 400
593
723
  raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Authentication type is not supported by this Login", response)
594
724
  end
@@ -602,16 +732,29 @@ module ZuoraAPI
602
732
  end
603
733
 
604
734
  #Oauth Tokens - User deactivated
605
- if body['reason'] == 'Forbidden' && body['status'] == 403 && response.code == 403
606
- 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
607
741
  end
608
742
 
609
743
  if body['error'] == 'Unauthorized' && body['status'] == 401
610
- 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
611
749
  end
612
750
  #Authentication failed
613
751
  if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
614
- 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
615
758
  end
616
759
 
617
760
  #Zuora REST Create Amendment error #Authentication failed
@@ -619,16 +762,30 @@ module ZuoraAPI
619
762
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body['faultstring']}", response)
620
763
  end
621
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
+
622
784
  #Request exceeded limit
623
785
  if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(70)
624
786
  raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{messages_array.join(', ')}", response)
625
787
  end
626
788
 
627
- #Locking contention
628
- if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
629
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
630
- end
631
-
632
789
  #All Errors catch
633
790
  if codes_array.size > 0
634
791
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages_array.join(', ')}", response)
@@ -636,26 +793,20 @@ module ZuoraAPI
636
793
 
637
794
  #Zuora REST Query Errors
638
795
  if body["faultcode"].present?
639
- case body["faultcode"]
640
- when "fns:MALFORMED_QUERY"
641
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
642
- when "fns:REQUEST_EXCEEDED_LIMIT"
643
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
644
- when "fns:LOCK_COMPETITION"
645
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
646
- when "INVALID_SESSION"
647
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
648
- else
649
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
650
- end
796
+ raise_errors_helper(error: body["faultcode"], message: body["faultstring"], response: response)
651
797
  end
652
798
 
653
799
  if body["Errors"].present? || body["errors"].present?
654
- errors = []
655
- (body["Errors"] || []).select { |obj| errors.push(obj["Message"]) }.compact
656
- (body["errors"] || []).select { |obj| errors.push(obj["Message"]) }.compact
657
- if errors.size > 0
658
- 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
659
810
  end
660
811
  end
661
812
  end
@@ -673,7 +824,7 @@ module ZuoraAPI
673
824
  raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("Retry Lock Competition", response)
674
825
  elsif error_messages.first.include?("data integrity violation")
675
826
  raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
676
- end
827
+ end
677
828
  end
678
829
  end
679
830
 
@@ -682,15 +833,130 @@ module ZuoraAPI
682
833
  end
683
834
  end
684
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
+
685
843
  #All other errors
686
- if ![200,201].include?(response.code)
687
- if response.code == 429
688
- 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)
689
911
  else
690
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{response.message}", response)
912
+ success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
691
913
  end
692
914
  end
693
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
694
960
  end
695
961
 
696
962
  def aqua_query(queryName: '', query: '', version: '1.2', jobName: 'Aqua',partner: '', project: '')
@@ -737,7 +1003,7 @@ module ZuoraAPI
737
1003
  base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
738
1004
  url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
739
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"}
740
- 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)
741
1007
 
742
1008
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401', response) if response.code == 401
743
1009
 
@@ -770,27 +1036,31 @@ module ZuoraAPI
770
1036
  end
771
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
772
1038
  end
1039
+
1040
+ return des_hash
773
1041
  rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
774
- if !(tries -= 1).zero?
775
- Rails.logger.info("Describe - #{ex.class} Timed out will retry after 5 seconds")
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)
776
1045
  sleep(self.timeout_sleep)
777
1046
  retry
778
- else
779
- raise ex
780
1047
  end
1048
+
1049
+ self.log(location: "Describe", exception: ex, message: "Timed out", level: :error) if log_errors
1050
+ raise ex
1051
+
781
1052
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
782
- if !(tries -= 1).zero? && self.status == 'Active'
1053
+ if !tries.zero? && self.status == 'Active'
1054
+ tries -= 1
783
1055
  Rails.logger.debug("Describe session expired. Starting new session.")
784
1056
  self.new_session
785
1057
  retry
786
- else
787
- Rails.logger.error("Describe session expired. Starting new session.") if log_errors
788
- raise ex
789
1058
  end
1059
+
1060
+ Rails.logger.error("Describe session expired. Starting new session.") if log_errors
1061
+ raise ex
790
1062
  rescue => ex
791
1063
  raise ex
792
- else
793
- return des_hash
794
1064
  end
795
1065
 
796
1066
  def rest_call(
@@ -803,93 +1073,124 @@ module ZuoraAPI
803
1073
  z_session: true,
804
1074
  session_type: :basic,
805
1075
  timeout_retry: false,
806
- timeout: 120,
1076
+ timeout: 130,
1077
+ timeout_sleep_interval: self.timeout_sleep,
807
1078
  multipart: false,
808
- **keyword_args
1079
+ stream_body: false,
1080
+ output_exception_messages: true,
1081
+ zuora_track_id: nil,
1082
+ **keyword_args,
1083
+ &block
809
1084
  )
810
1085
  tries ||= 2
811
1086
 
812
- if self.entity_id.present?
813
- headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
814
- headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
815
- end
816
-
817
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)
818
1088
 
819
- authentication_headers = z_session ? {"Authorization" => self.get_session(prefix: true, auth_type: session_type) } : {}
820
- modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
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?
821
1098
 
822
- response = HTTParty::Request.new(
823
- "Net::HTTP::#{method.to_s.capitalize}".constantize,
824
- url,
825
- body: body,
826
- headers: modified_headers,
827
- timeout: timeout,
828
- multipart: multipart,
829
- ).perform
1099
+ modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
830
1100
 
831
- Rails.logger.debug("Response Code: #{response.code}") if debug
832
1101
  begin
833
- output_json = JSON.parse(response.body)
834
- rescue JSON::ParserError => ex
835
- 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
836
1126
  end
837
- Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
838
1127
 
839
- raise_errors(type: :JSON, body: output_json, response: response)
1128
+ return [output_json, response]
840
1129
  rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
841
1130
  if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
842
- 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)
843
- else
844
- Rails.logger.debug("Rest Call - Session Bad Auth type")
845
- raise ex
1131
+ session_type = :bearer
1132
+ retry
846
1133
  end
1134
+ Rails.logger.debug("Rest Call - Session Bad Auth type")
1135
+ raise ex
1136
+
847
1137
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
848
- if !(tries -= 1).zero? && z_session
1138
+ if !tries.zero? && z_session
1139
+ tries -= 1
849
1140
  Rails.logger.debug("Rest Call - Session Invalid #{session_type}")
850
- self.new_session(auth_type: session_type)
851
- retry
852
- else
853
- if errors.include?(ex.class)
854
- raise ex
855
- else
856
- 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]
857
1146
  end
1147
+
1148
+ retry
858
1149
  end
1150
+
1151
+ raise ex if errors.include?(ex.class)
1152
+ return [output_json, response]
1153
+
859
1154
  rescue *ZUORA_API_ERRORS => ex
860
- if errors.include?(ex.class)
861
- raise ex
862
- else
863
- return [output_json, response]
864
- end
1155
+ raise ex if errors.include?(ex.class)
1156
+
1157
+ response = ex.response unless response
1158
+ return [output_json, response]
1159
+
865
1160
  rescue ZuoraAPI::Exceptions::BadEntityError => ex
866
1161
  raise ex
867
1162
  rescue *CONNECTION_EXCEPTIONS => ex
868
- if !(tries -= 1).zero?
869
- Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
870
- 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)
871
1167
  retry
872
- else
873
- raise ex
874
1168
  end
1169
+
1170
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1171
+ raise ex
1172
+
875
1173
  rescue *CONNECTION_READ_EXCEPTIONS => ex
876
- if !(tries -= 1).zero? && timeout_retry
877
- Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
878
- sleep(self.timeout_sleep)
879
- retry
880
- else
881
- raise ex
882
- end
883
- rescue Errno::ECONNRESET => ex
884
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
885
- retry
886
- else
887
- 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
888
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
+
889
1190
  rescue => ex
890
1191
  raise ex
891
- else
892
- return [output_json, response]
1192
+ ensure
1193
+ self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
893
1194
  end
894
1195
 
895
1196
  def update_create_tenant
@@ -911,8 +1212,9 @@ module ZuoraAPI
911
1212
  while !response["nextPage"].blank?
912
1213
  url = self.rest_endpoint(response["nextPage"].split('/v1/').last)
913
1214
  Rails.logger.debug("Fetch Catalog URL #{url}")
914
- output_json, response = self.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true )
915
- 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
916
1218
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}", response)
917
1219
  end
918
1220
  output_json["products"].each do |product|
@@ -938,9 +1240,11 @@ module ZuoraAPI
938
1240
  return products, catalog_map
939
1241
  end
940
1242
 
941
- 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)
942
1244
  raise "file_path must be of class Pathname" if file_path.class != Pathname
943
1245
 
1246
+ retry_count ||= timeout_retries
1247
+
944
1248
  #Make sure directory exists
945
1249
  require 'fileutils'
946
1250
  FileUtils.mkdir_p(file_path) unless File.exists?(file_path)
@@ -955,8 +1259,9 @@ module ZuoraAPI
955
1259
  headers = headers.merge({"Zuora-Entity-Ids" => self.entity_id}) if !self.entity_id.blank?
956
1260
  end
957
1261
 
1262
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
1263
+
958
1264
  response_save = nil
959
- retry_count ||= timeout_retries
960
1265
  http.request_get(uri.request_uri, headers) do |response|
961
1266
  response_save = response
962
1267
  status_code = response.code if response
@@ -1040,36 +1345,42 @@ module ZuoraAPI
1040
1345
  return file_handle
1041
1346
  when Net::HTTPUnauthorized
1042
1347
  if z_session
1043
- if !(retry_count -= 1).zero?
1348
+ unless (retry_count -= 1).zero?
1044
1349
  self.new_session
1045
- raise response.class
1046
- else
1047
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1350
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError, 'Retrying'
1048
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
+ )
1049
1360
  else
1050
- raise
1361
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1051
1362
  end
1052
1363
  else
1053
- raise StandardError.new("File Download Failed #{response.class}")
1364
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1054
1365
  end
1055
1366
  end
1056
-
1057
- rescue => ex
1367
+
1368
+ rescue => ex
1058
1369
  sleep(5)
1059
1370
  if (retry_count -= 1) >= 0
1060
1371
  retry
1061
- else
1062
- Rails.logger.error("File Download Failed")
1063
- raise
1064
1372
  end
1373
+ Rails.logger.error("File Download Failed")
1374
+ raise
1065
1375
  end
1066
1376
 
1067
- 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
1068
1379
  request = Nokogiri::XML::Builder.new do |xml|
1069
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
1070
1381
  xml['SOAP-ENV'].Header do
1071
1382
  xml['ns1'].SessionHeader do
1072
- 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)
1073
1384
  end
1074
1385
  end
1075
1386
  xml['SOAP-ENV'].Body do
@@ -1086,17 +1397,20 @@ module ZuoraAPI
1086
1397
  end
1087
1398
  end
1088
1399
 
1089
- 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)
1090
1401
 
1091
1402
  output_xml = Nokogiri::XML(response_query.body)
1092
- 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
+
1093
1407
  id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
1094
1408
 
1095
1409
  confirmRequest = Nokogiri::XML::Builder.new do |xml|
1096
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
1097
1411
  xml['SOAP-ENV'].Header do
1098
1412
  xml['ns1'].SessionHeader do
1099
- 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)
1100
1414
  end
1101
1415
  end
1102
1416
  xml['SOAP-ENV'].Body do
@@ -1110,12 +1424,14 @@ module ZuoraAPI
1110
1424
 
1111
1425
  while result != "Completed"
1112
1426
  sleep 3
1113
- 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)
1114
1428
 
1115
1429
  output_xml = Nokogiri::XML(response_query.body)
1116
1430
  result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
1117
1431
  status_code = response_query.code if response_query
1118
- 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"
1119
1435
  end
1120
1436
 
1121
1437
  file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
@@ -1134,10 +1450,39 @@ module ZuoraAPI
1134
1450
  else
1135
1451
  return export_file_path
1136
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
1137
1482
  end
1138
1483
 
1139
1484
  def query(query, parse = false)
1140
- 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|
1141
1486
  xml['ns1'].query do
1142
1487
  xml['ns1'].queryString query
1143
1488
  end
@@ -1199,5 +1544,17 @@ module ZuoraAPI
1199
1544
  end
1200
1545
  return "failure"
1201
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
1202
1559
  end
1203
1560
  end