zuora_api 1.8.00 → 1.8.23
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +20 -0
- data/README.md +10 -0
- data/lib/zuora_api/exceptions.rb +17 -3
- data/lib/zuora_api/login.rb +324 -257
- data/lib/zuora_api/logins/basic.rb +10 -101
- data/lib/zuora_api/logins/oauth.rb +24 -84
- data/lib/zuora_api/version.rb +1 -1
- metadata +25 -20
- data/.gitignore +0 -10
- data/.gitlab-ci.yml +0 -63
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/CHANGELOG.md +0 -105
- data/Gemfile +0 -4
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/zuora_api.gemspec +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0ab2609485e4d653738cdd84169feac4058531f4caac36e62b88fd8910008cc
|
4
|
+
data.tar.gz: d86b6df5e09c4eed423b362e0334f5605da724e6a7956bd6c896aeccbc9e3fa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42c8e4f181cb4c64275db13bb0104d47864cd80e3b53a930fd48373b4fa43aa4022bc21162f38fe53dcadeca06ea601e0d7dd25161a5a57678c3008b1239b046
|
7
|
+
data.tar.gz: 8f06a60bc4263a8529dbb5c6a862668cce4f1bda5b5f33f6df25f67bb49f79c7382cc2192d486a4c09802590e990041ad7a8fa627cd5e88975c9636c6e716eb1
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Zuora, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -145,3 +145,13 @@ Both do the same thing except one returns a url(data_export_insights) to downloa
|
|
145
145
|
objectype: "ACCOUNT/USER"
|
146
146
|
|
147
147
|
segmentuuid: A single or array of string or int of a segment uuid(s) that you get from the describe call. The csv holds a column with a bool that represents if that User or Account belongs to that segment.
|
148
|
+
|
149
|
+
### License Information
|
150
|
+
IN THE EVENT YOU ARE AN EXISTING ZUORA CUSTOMER, USE OF THIS SOFTWARE IS GOVERNED
BY THE MIT LICENSE SET FORTH BELOW AND NOT THE MASTER SUBSCRIPTION AGREEMENT OR OTHER COMMERCIAL AGREEMENT ENTERED INTO BETWEEN YOU AND ZUORA (“AGREEMENT”). FOR THE AVOIDANCE OF DOUBT, ZUORA’S OBLIGATIONS WITH RESPECT TO TECHNICAL SUPPORT, UPTIME, INDEMNIFICATION, AND SECURITY SET FORTH IN THE AGREEMENT DO NOT APPLY TO THE USE OF THIS SOFTWARE.
|
151
|
+
|
152
|
+
Copyright 2021 Zuora, Inc.
|
153
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
154
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
155
|
+
|
156
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
157
|
+
|
data/lib/zuora_api/exceptions.rb
CHANGED
@@ -40,6 +40,19 @@ module ZuoraAPI
|
|
40
40
|
def to_s
|
41
41
|
@message || @default_message
|
42
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
|
43
56
|
end
|
44
57
|
|
45
58
|
class BadEntityError < Error
|
@@ -177,14 +190,15 @@ module ZuoraAPI
|
|
177
190
|
end
|
178
191
|
|
179
192
|
class ZuoraAPITemporaryError < Error
|
180
|
-
attr_reader :code, :response
|
193
|
+
attr_reader :code, :response, :errors
|
181
194
|
attr_writer :default_message
|
182
195
|
|
183
|
-
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
196
|
+
def initialize(message = nil, response = nil, errors = [], successes = [], *args)
|
184
197
|
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
185
198
|
@message = parse_message(message)
|
186
199
|
@response = response
|
187
200
|
@default_message = "There is a temporary error with zuora system."
|
201
|
+
@errors = errors
|
188
202
|
end
|
189
203
|
|
190
204
|
def to_s
|
@@ -224,7 +238,7 @@ module ZuoraAPI
|
|
224
238
|
end
|
225
239
|
end
|
226
240
|
|
227
|
-
class ZuoraAPIReadTimeout <
|
241
|
+
class ZuoraAPIReadTimeout < Timeout::Error
|
228
242
|
attr_reader :code, :response, :request
|
229
243
|
attr_writer :default_message
|
230
244
|
|
data/lib/zuora_api/login.rb
CHANGED
@@ -7,7 +7,7 @@ module ZuoraAPI
|
|
7
7
|
class Login
|
8
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_Endpoints = {'Test': '107.0', 'Sandbox': '107.0', 'Production': '107.0', 'Performance': '107.0', 'Services': '96.0', 'Unknown': '96.0', 'Staging': '107.0'}.freeze
|
11
11
|
XML_SAVE_OPTIONS = Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
12
12
|
|
13
13
|
CONNECTION_EXCEPTIONS = [
|
@@ -21,7 +21,7 @@ module ZuoraAPI
|
|
21
21
|
].freeze
|
22
22
|
|
23
23
|
CONNECTION_READ_EXCEPTIONS = [
|
24
|
-
|
24
|
+
Timeout::Error,
|
25
25
|
Errno::ECONNRESET,
|
26
26
|
Errno::EPIPE
|
27
27
|
].freeze
|
@@ -39,8 +39,8 @@ module ZuoraAPI
|
|
39
39
|
|
40
40
|
ZUORA_SERVER_ERRORS = [
|
41
41
|
ZuoraAPI::Exceptions::ZuoraAPIInternalServerError,
|
42
|
-
|
43
|
-
|
42
|
+
ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout,
|
43
|
+
ZuoraAPI::Exceptions::ZuoraAPIReadTimeout,
|
44
44
|
ZuoraAPI::Exceptions::ZuoraUnexpectedError
|
45
45
|
].freeze
|
46
46
|
|
@@ -50,10 +50,12 @@ module ZuoraAPI
|
|
50
50
|
raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
|
51
51
|
# raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
|
52
52
|
self.hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
|
53
|
+
self.update_environment
|
54
|
+
min_endpoint = MIN_Endpoints[self.environment.to_sym]
|
53
55
|
if !/apps\/services\/a\/\d+\.\d$/.match(url.strip)
|
54
|
-
self.url = "https://#{hostname}/apps/services/a/#{
|
55
|
-
elsif
|
56
|
-
self.url = url.gsub(/(\d+\.\d)$/,
|
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)
|
57
59
|
else
|
58
60
|
self.url = url
|
59
61
|
end
|
@@ -65,7 +67,6 @@ module ZuoraAPI
|
|
65
67
|
self.status = status.blank? ? "Active" : status
|
66
68
|
self.user_info = Hash.new
|
67
69
|
self.update_region
|
68
|
-
self.update_environment
|
69
70
|
self.update_zconnect_provider
|
70
71
|
@timeout_sleep = 5
|
71
72
|
end
|
@@ -155,7 +156,7 @@ module ZuoraAPI
|
|
155
156
|
# 1. Pass in cookies and optionally custom_authorities, name, and description
|
156
157
|
# 2. Pass in user_id, entity_ids, client_id, client_secret, and optionally custom_authorities, name, and description
|
157
158
|
# https://intranet.zuora.com/confluence/display/Sunburst/Create+an+OAuth+Client+through+API+Gateway#CreateanOAuthClientthroughAPIGateway-ZSession
|
158
|
-
def get_oauth_client (custom_authorities = [], info_name: "No Name", info_desc: "This client was created without a description.", user_id: nil, entity_ids: nil, client_id: nil, client_secret: nil, new_client_id: nil, new_client_secret: nil, cookies: nil, chomp_v1_from_genesis_endpoint: false)
|
159
|
+
def get_oauth_client (custom_authorities = [], info_name: "No Name", info_desc: "This client was created without a description.", user_id: nil, entity_ids: nil, client_id: nil, client_secret: nil, new_client_id: nil, new_client_secret: nil, cookies: nil, chomp_v1_from_genesis_endpoint: false, use_api_generated_client_secret: false)
|
159
160
|
authorization = ""
|
160
161
|
new_client_id = SecureRandom.uuid if new_client_id.blank?
|
161
162
|
new_client_secret = SecureRandom.hex(10) if new_client_secret.blank?
|
@@ -185,7 +186,7 @@ module ZuoraAPI
|
|
185
186
|
oauth_response = HTTParty.post(endpoint, :headers => {'authorization' => authorization, 'Content-Type' => 'application/json'}, :body => {'clientId' => new_client_id, 'clientSecret' => new_client_secret, 'userId' => user_id, 'entityIds' => entity_ids, 'customAuthorities' => custom_authorities, 'additionalInformation' => {'description' => info_desc, 'name' => info_name}}.to_json)
|
186
187
|
output_json = JSON.parse(oauth_response.body)
|
187
188
|
if oauth_response.code == 201
|
188
|
-
output_json["clientSecret"] = new_client_secret
|
189
|
+
output_json["clientSecret"] = new_client_secret if !use_api_generated_client_secret
|
189
190
|
return output_json
|
190
191
|
elsif oauth_response.code == 401 && !oauth_response.message.blank?
|
191
192
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(output_json["message"], oauth_response)
|
@@ -252,7 +253,7 @@ module ZuoraAPI
|
|
252
253
|
end
|
253
254
|
|
254
255
|
def update_environment
|
255
|
-
if !self.
|
256
|
+
if !self.hostname.blank?
|
256
257
|
case self.hostname
|
257
258
|
when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
|
258
259
|
self.environment = 'Sandbox'
|
@@ -275,13 +276,13 @@ module ZuoraAPI
|
|
275
276
|
end
|
276
277
|
|
277
278
|
def update_zconnect_provider
|
278
|
-
|
279
|
-
|
279
|
+
update_region if self.region.blank?
|
280
|
+
update_environment if self.environment.blank?
|
280
281
|
mappings = {"US" => {"Sandbox" => "ZConnectSbx", "Services" => "ZConnectSvcUS", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Test" => "ZConnectTest", "Staging" => "ZConnectQA", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev"},
|
281
282
|
"NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectSvcNA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
|
282
283
|
"EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectSvcEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU", "Test" => "ZConnectTest"},
|
283
284
|
"Unknown" => {"Unknown" => "Unknown"}}
|
284
|
-
self.zconnect_provider = mappings[region][environment]
|
285
|
+
self.zconnect_provider = mappings[self.region][self.environment]
|
285
286
|
end
|
286
287
|
|
287
288
|
def aqua_endpoint(url="")
|
@@ -321,8 +322,8 @@ module ZuoraAPI
|
|
321
322
|
return domain ? endpoint.concat(prefix).concat(url) : prefix.concat(url)
|
322
323
|
end
|
323
324
|
|
324
|
-
def rest_domain
|
325
|
-
return URI(
|
325
|
+
def rest_domain(endpoint: self.rest_endpoint)
|
326
|
+
return URI(endpoint).host
|
326
327
|
end
|
327
328
|
|
328
329
|
def fileURL(url="")
|
@@ -334,10 +335,41 @@ module ZuoraAPI
|
|
334
335
|
end
|
335
336
|
|
336
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
|
+
|
337
370
|
end
|
338
371
|
|
339
372
|
def get_session(prefix: false, auth_type: :basic, zuora_track_id: nil)
|
340
|
-
Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}") if Rails.env.to_s == 'development'
|
341
373
|
case auth_type
|
342
374
|
when :basic
|
343
375
|
if self.current_session.blank?
|
@@ -346,14 +378,13 @@ module ZuoraAPI
|
|
346
378
|
if self.bearer_token.blank? || self.oauth_expired?
|
347
379
|
self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
|
348
380
|
end
|
349
|
-
self.get_z_session(zuora_track_id: zuora_track_id)
|
381
|
+
self.get_z_session(zuora_track_id: zuora_track_id)
|
350
382
|
when 'ZuoraAPI::Basic'
|
351
383
|
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
352
384
|
else
|
353
385
|
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
354
386
|
end
|
355
387
|
end
|
356
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
|
357
388
|
return prefix ? "ZSession #{self.current_session}" : self.current_session.to_s
|
358
389
|
when :bearer
|
359
390
|
case self.class.to_s
|
@@ -366,8 +397,6 @@ module ZuoraAPI
|
|
366
397
|
else
|
367
398
|
raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Unknown Login, does not support Authentication of Type: #{auth_type}")
|
368
399
|
end
|
369
|
-
|
370
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
|
371
400
|
return prefix ? "Bearer #{self.bearer_token}" : self.bearer_token.to_s
|
372
401
|
end
|
373
402
|
end
|
@@ -375,16 +404,18 @@ module ZuoraAPI
|
|
375
404
|
def soap_call(
|
376
405
|
ns1: 'ns1',
|
377
406
|
ns2: 'ns2',
|
378
|
-
batch_size: nil,
|
407
|
+
batch_size: nil,
|
408
|
+
headers: {},
|
379
409
|
single_transaction: false,
|
380
410
|
debug: false,
|
381
411
|
zuora_track_id: nil,
|
382
412
|
errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
|
383
413
|
z_session: true,
|
384
414
|
timeout_retry: false,
|
385
|
-
timeout:
|
415
|
+
timeout: 130,
|
386
416
|
timeout_sleep_interval: self.timeout_sleep,
|
387
417
|
output_exception_messages: true,
|
418
|
+
skip_session: false,
|
388
419
|
**keyword_args)
|
389
420
|
tries ||= 2
|
390
421
|
xml = Nokogiri::XML::Builder.new do |xml|
|
@@ -394,8 +425,10 @@ module ZuoraAPI
|
|
394
425
|
'xmlns:api' => "http://api.zuora.com/",
|
395
426
|
"xmlns:#{ns1}" => "http://api.zuora.com/") do
|
396
427
|
xml['SOAP-ENV'].Header do
|
397
|
-
|
398
|
-
xml["#{ns1}"].
|
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
|
399
432
|
end
|
400
433
|
if single_transaction
|
401
434
|
xml["#{ns1}"].CallOptions do
|
@@ -413,12 +446,11 @@ module ZuoraAPI
|
|
413
446
|
end
|
414
447
|
end
|
415
448
|
end
|
416
|
-
|
417
449
|
input_xml = Nokogiri::XML(xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip)
|
418
450
|
input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
|
419
451
|
Rails.logger.debug("Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
|
420
452
|
|
421
|
-
headers
|
453
|
+
headers.merge!({ 'Content-Type' => "text/xml; charset=utf-8", 'Accept' => 'text/xml'})
|
422
454
|
headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
|
423
455
|
|
424
456
|
request = HTTParty::Request.new(
|
@@ -435,7 +467,11 @@ module ZuoraAPI
|
|
435
467
|
Rails.logger.debug("Response SOAP XML: #{output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
|
436
468
|
|
437
469
|
raise_errors(type: :SOAP, body: output_xml, response: response)
|
470
|
+
|
471
|
+
return output_xml, input_xml, response
|
472
|
+
|
438
473
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
474
|
+
raise if skip_session
|
439
475
|
if !tries.zero? && z_session
|
440
476
|
tries -= 1
|
441
477
|
Rails.logger.debug("SOAP Call - Session Invalid")
|
@@ -447,39 +483,33 @@ module ZuoraAPI
|
|
447
483
|
end
|
448
484
|
|
449
485
|
retry
|
450
|
-
else
|
451
|
-
if errors.include?(ex.class)
|
452
|
-
raise ex
|
453
|
-
else
|
454
|
-
return output_xml, input_xml, response
|
455
|
-
end
|
456
486
|
end
|
487
|
+
|
488
|
+
raise ex if errors.include?(ex.class)
|
489
|
+
|
490
|
+
return output_xml, input_xml, response
|
491
|
+
|
457
492
|
rescue *ZUORA_API_ERRORS => ex
|
458
|
-
if errors.include?(ex.class)
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
end
|
493
|
+
raise ex if errors.include?(ex.class)
|
494
|
+
|
495
|
+
response = ex.response unless response
|
496
|
+
return output_xml, input_xml, response
|
497
|
+
|
464
498
|
rescue *CONNECTION_EXCEPTIONS => ex
|
465
|
-
if tries.zero?
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
Rails.logger.error("SOAP Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
471
|
-
end
|
472
|
-
end
|
473
|
-
raise ex
|
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)
|
503
|
+
retry
|
474
504
|
end
|
475
505
|
|
476
|
-
|
477
|
-
|
478
|
-
|
506
|
+
self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
507
|
+
raise ex
|
508
|
+
|
479
509
|
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
480
510
|
if !tries.zero?
|
481
511
|
tries -= 1
|
482
|
-
|
512
|
+
self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
483
513
|
if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
|
484
514
|
retry
|
485
515
|
elsif timeout_retry
|
@@ -488,19 +518,74 @@ module ZuoraAPI
|
|
488
518
|
end
|
489
519
|
end
|
490
520
|
|
491
|
-
if output_exception_messages
|
492
|
-
|
493
|
-
Rails.logger.error("SOAP Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
494
|
-
else
|
495
|
-
Rails.logger.error("SOAP Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
496
|
-
end
|
497
|
-
end
|
498
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from #{url}", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
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/write timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.is_a?(Timeout::Error) && !ex.instance_of?(ZuoraAPI::Exceptions::ZuoraAPIReadTimeout)
|
499
523
|
raise ex
|
524
|
+
|
500
525
|
rescue => ex
|
501
526
|
raise ex
|
502
|
-
|
503
|
-
|
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', ex, 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)
|
549
|
+
else
|
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 defined?(ex.response) && ex.response.present?
|
557
|
+
args.merge!({
|
558
|
+
request: {
|
559
|
+
path: ex.response.request.path.to_s,
|
560
|
+
method: ex.response.request.http_method.to_s.split("Net::HTTP::").last.upcase,
|
561
|
+
params: ex.response.request.raw_body.to_s,
|
562
|
+
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,
|
563
|
+
},
|
564
|
+
response: {
|
565
|
+
status: ex.response.code,
|
566
|
+
params: ex.response.body.to_s,
|
567
|
+
headers: ex.response.headers.to_s,
|
568
|
+
},
|
569
|
+
zuora_trace_id: ex.response.headers["zuora-request-id"],
|
570
|
+
zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"],
|
571
|
+
})
|
572
|
+
elsif defined?(ex.request) && ex.request.present?
|
573
|
+
args.merge!({
|
574
|
+
request: {
|
575
|
+
path: ex.request.path.to_s,
|
576
|
+
method: ex.request.http_method.to_s.split("Net::HTTP::").last.upcase,
|
577
|
+
params: ex.request.options[:body],
|
578
|
+
headers: ex.request.options[:headers].map{|k,v| [k.to_s, k.to_s.downcase.strip == "authorization" ? "VALUE FILTERED" : v]}.to_h.to_s
|
579
|
+
}
|
580
|
+
})
|
581
|
+
args.merge!({
|
582
|
+
zuora_track_id: ex.request.options[:headers]["Zuora-Track-Id"]
|
583
|
+
}) if ex.request.options[:headers]["Zuora-Track-Id"].present?
|
584
|
+
end
|
585
|
+
rescue => ex
|
586
|
+
Rails.logger.error("Failed to create exception arguments", ex, args)
|
587
|
+
ensure
|
588
|
+
return args
|
504
589
|
end
|
505
590
|
|
506
591
|
def raise_errors(type: :SOAP, body: nil, response: nil)
|
@@ -517,13 +602,13 @@ module ZuoraAPI
|
|
517
602
|
end
|
518
603
|
|
519
604
|
if [502,503].include?(response.code)
|
520
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from
|
605
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
|
521
606
|
end
|
522
607
|
|
523
608
|
# Check failure response code
|
524
609
|
case response.code
|
525
610
|
when 504
|
526
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from
|
611
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from 'https://#{rest_domain(endpoint: request_uri)}'", response)
|
527
612
|
when 429
|
528
613
|
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)
|
529
614
|
when 401
|
@@ -544,6 +629,10 @@ module ZuoraAPI
|
|
544
629
|
when :SOAP
|
545
630
|
error, success, message = get_soap_error_and_message(body)
|
546
631
|
|
632
|
+
if body.xpath('//fns:LoginFault', 'fns' =>'http://fault.api.zuora.com/').present?
|
633
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response)
|
634
|
+
end
|
635
|
+
|
547
636
|
if body.xpath('//ns1:queryResponse', 'ns1' => 'http://api.zuora.com/').present? &&
|
548
637
|
body.xpath(
|
549
638
|
'//ns1:records[@xsi:type="ns2:Export"]',
|
@@ -551,12 +640,12 @@ module ZuoraAPI
|
|
551
640
|
).present?
|
552
641
|
result = body.xpath('//ns2:Status', 'ns2' => 'http://object.api.zuora.com/').text
|
553
642
|
if result == 'Failed'
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
643
|
+
message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
|
644
|
+
error = 'FATAL_ERROR'
|
645
|
+
if message.present?
|
646
|
+
identifier, new_message = message.scan(/^([\w\d]{16})\: (.*)/).first
|
647
|
+
error, message = ['UNEXPECTED_ERROR', new_message] if new_message.present?
|
558
648
|
else
|
559
|
-
error = 'FATAL_ERROR'
|
560
649
|
message = 'Export failed due to unknown reason. Consult api logs.'
|
561
650
|
end
|
562
651
|
end
|
@@ -605,14 +694,31 @@ module ZuoraAPI
|
|
605
694
|
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.
|
606
695
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response) if reporting_message.present?
|
607
696
|
end
|
697
|
+
when /\/objects\/batch\//
|
698
|
+
if body['code'].present? && /61$/.match(body['code'].to_s).present? # if last 2 digits of code are 61
|
699
|
+
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body['message'], nil, body['details'])
|
700
|
+
end
|
701
|
+
when /^\/api\/v1\/payment_plans.*/
|
702
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['error'], response) if body['error']
|
608
703
|
end
|
609
704
|
|
610
705
|
body = body.dig("results").present? ? body["results"] : body if body.class == Hash
|
611
706
|
if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
|
612
|
-
|
613
|
-
|
614
|
-
codes_array =
|
615
|
-
|
707
|
+
reason_keys = %w(reasons errors)
|
708
|
+
message_keys = %w(message title)
|
709
|
+
messages_array, codes_array = [[],[]]
|
710
|
+
reason_keys.each do |rsn_key|
|
711
|
+
message_keys.each do |msg_key|
|
712
|
+
messages_array = body.fetch(rsn_key, []).map {|error| error[msg_key]}.compact
|
713
|
+
break if messages_array.present?
|
714
|
+
end
|
715
|
+
codes_array = body.fetch(rsn_key, []).map {|error| error['code']}.compact
|
716
|
+
break if messages_array.present? && codes_array.present?
|
717
|
+
end
|
718
|
+
if body.dig('error').class == Hash
|
719
|
+
messages_array = messages_array.push(body.dig("error", 'message')).compact
|
720
|
+
codes_array = codes_array.push(body.dig("error", 'code')).compact
|
721
|
+
end
|
616
722
|
|
617
723
|
if body['message'] == 'request exceeded limit'
|
618
724
|
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)
|
@@ -644,7 +750,11 @@ module ZuoraAPI
|
|
644
750
|
end
|
645
751
|
|
646
752
|
if body['error'] == 'Unauthorized' && body['status'] == 401
|
647
|
-
|
753
|
+
if body['message'].present?
|
754
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
|
755
|
+
else
|
756
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
|
757
|
+
end
|
648
758
|
end
|
649
759
|
#Authentication failed
|
650
760
|
if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
|
@@ -751,7 +861,7 @@ module ZuoraAPI
|
|
751
861
|
output_json = JSON.parse(response.body)
|
752
862
|
self.raise_errors(type: :JSON, body: output_json, response: response)
|
753
863
|
|
754
|
-
elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml')) and type != :xml
|
864
|
+
elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml') || response_content_types.include?('application/soap+xml')) and type != :xml
|
755
865
|
output_xml = Nokogiri::XML(response.body)
|
756
866
|
self.raise_errors(type: :SOAP, body: output_xml, response: response)
|
757
867
|
|
@@ -771,6 +881,8 @@ module ZuoraAPI
|
|
771
881
|
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new(error_message, response)
|
772
882
|
when /Client sent a bad request./, /Bad Request/, /403 Forbidden/
|
773
883
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
|
884
|
+
when /414 Request-URI Too Large/
|
885
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Request URL is too long", response)
|
774
886
|
else
|
775
887
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
|
776
888
|
end
|
@@ -797,6 +909,11 @@ module ZuoraAPI
|
|
797
909
|
message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
|
798
910
|
end
|
799
911
|
|
912
|
+
if error.blank? || message.blank?
|
913
|
+
error = body.xpath('//soapenv:Value', 'soapenv'=>'http://www.w3.org/2003/05/soap-envelope').text
|
914
|
+
message = body.xpath('//soapenv:Text', 'soapenv'=>'http://www.w3.org/2003/05/soap-envelope').text
|
915
|
+
end
|
916
|
+
|
800
917
|
#Update/Create/Delete Calls with multiple requests and responses
|
801
918
|
if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
|
802
919
|
error = []
|
@@ -840,17 +957,22 @@ module ZuoraAPI
|
|
840
957
|
raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
|
841
958
|
end
|
842
959
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
843
|
-
when /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/
|
960
|
+
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/
|
844
961
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
845
962
|
when /.*UNEXPECTED_ERROR/
|
846
963
|
raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(message, response, errors, success)
|
847
964
|
when /.*soapenv:Server.*/
|
848
965
|
if /^Invalid value.*for type.*|^Id is invalid|^date string can not be less than 19 charactors$/.match(message).present?
|
849
966
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
850
|
-
elsif /^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
|
967
|
+
elsif /^unknown$|^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
|
851
968
|
raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
|
852
969
|
end
|
853
970
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
|
971
|
+
when /soapenv:Receiver/
|
972
|
+
if /^com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character.*$/.match(message).present?
|
973
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
974
|
+
end
|
975
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
|
854
976
|
else
|
855
977
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Z:#{error}::#{message}", response, errors, success)
|
856
978
|
end
|
@@ -900,7 +1022,7 @@ module ZuoraAPI
|
|
900
1022
|
base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
|
901
1023
|
url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
|
902
1024
|
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"}
|
903
|
-
response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout =>
|
1025
|
+
response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 130)
|
904
1026
|
|
905
1027
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401', response) if response.code == 401
|
906
1028
|
|
@@ -933,35 +1055,31 @@ module ZuoraAPI
|
|
933
1055
|
end
|
934
1056
|
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
|
935
1057
|
end
|
1058
|
+
|
1059
|
+
return des_hash
|
936
1060
|
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
937
|
-
if tries.zero?
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
Rails.logger.error("Describe - #{ex.class} Timed out will retry after #{self.timeout_sleep} seconds")
|
943
|
-
end
|
944
|
-
end
|
945
|
-
raise ex
|
1061
|
+
if !tries.zero?
|
1062
|
+
tries -= 1
|
1063
|
+
self.log(location: "Describe", exception: ex, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :debug)
|
1064
|
+
sleep(self.timeout_sleep)
|
1065
|
+
retry
|
946
1066
|
end
|
947
1067
|
|
948
|
-
|
949
|
-
|
950
|
-
|
1068
|
+
self.log(location: "Describe", exception: ex, message: "Timed out", level: :error) if log_errors
|
1069
|
+
raise ex
|
1070
|
+
|
951
1071
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
952
1072
|
if !tries.zero? && self.status == 'Active'
|
953
1073
|
tries -= 1
|
954
1074
|
Rails.logger.debug("Describe session expired. Starting new session.")
|
955
1075
|
self.new_session
|
956
1076
|
retry
|
957
|
-
else
|
958
|
-
Rails.logger.error("Describe session expired. Starting new session.") if log_errors
|
959
|
-
raise ex
|
960
1077
|
end
|
1078
|
+
|
1079
|
+
Rails.logger.error("Describe session expired. Starting new session.") if log_errors
|
1080
|
+
raise ex
|
961
1081
|
rescue => ex
|
962
1082
|
raise ex
|
963
|
-
else
|
964
|
-
return des_hash
|
965
1083
|
end
|
966
1084
|
|
967
1085
|
def rest_call(
|
@@ -974,11 +1092,12 @@ module ZuoraAPI
|
|
974
1092
|
z_session: true,
|
975
1093
|
session_type: :basic,
|
976
1094
|
timeout_retry: false,
|
977
|
-
timeout:
|
1095
|
+
timeout: 130,
|
978
1096
|
timeout_sleep_interval: self.timeout_sleep,
|
979
1097
|
multipart: false,
|
980
1098
|
stream_body: false,
|
981
1099
|
output_exception_messages: true,
|
1100
|
+
zuora_track_id: nil,
|
982
1101
|
**keyword_args,
|
983
1102
|
&block
|
984
1103
|
)
|
@@ -988,12 +1107,13 @@ module ZuoraAPI
|
|
988
1107
|
|
989
1108
|
authentication_headers = {}
|
990
1109
|
if z_session
|
991
|
-
authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type) }
|
1110
|
+
authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type, zuora_track_id: zuora_track_id) }
|
992
1111
|
if self.entity_id.present?
|
993
1112
|
authentication_headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
|
994
1113
|
authentication_headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
|
995
1114
|
end
|
996
1115
|
end
|
1116
|
+
headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
|
997
1117
|
|
998
1118
|
modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
|
999
1119
|
|
@@ -1019,18 +1139,20 @@ module ZuoraAPI
|
|
1019
1139
|
Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
|
1020
1140
|
|
1021
1141
|
raise_errors(type: :JSON, body: output_json, response: response)
|
1022
|
-
rescue
|
1142
|
+
rescue => ex
|
1023
1143
|
reset_files(body) if multipart
|
1024
1144
|
raise
|
1025
1145
|
end
|
1146
|
+
|
1147
|
+
return [output_json, response]
|
1026
1148
|
rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
|
1027
1149
|
if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
|
1028
1150
|
session_type = :bearer
|
1029
1151
|
retry
|
1030
|
-
else
|
1031
|
-
Rails.logger.debug("Rest Call - Session Bad Auth type")
|
1032
|
-
raise ex
|
1033
1152
|
end
|
1153
|
+
Rails.logger.debug("Rest Call - Session Bad Auth type")
|
1154
|
+
raise ex
|
1155
|
+
|
1034
1156
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
1035
1157
|
if !tries.zero? && z_session
|
1036
1158
|
tries -= 1
|
@@ -1043,40 +1165,35 @@ module ZuoraAPI
|
|
1043
1165
|
end
|
1044
1166
|
|
1045
1167
|
retry
|
1046
|
-
else
|
1047
|
-
if errors.include?(ex.class)
|
1048
|
-
raise ex
|
1049
|
-
else
|
1050
|
-
return [output_json, response]
|
1051
|
-
end
|
1052
1168
|
end
|
1169
|
+
|
1170
|
+
raise ex if errors.include?(ex.class)
|
1171
|
+
return [output_json, response]
|
1172
|
+
|
1053
1173
|
rescue *ZUORA_API_ERRORS => ex
|
1054
|
-
if errors.include?(ex.class)
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
end
|
1174
|
+
raise ex if errors.include?(ex.class)
|
1175
|
+
|
1176
|
+
response = ex.response unless response
|
1177
|
+
return [output_json, response]
|
1178
|
+
|
1060
1179
|
rescue ZuoraAPI::Exceptions::BadEntityError => ex
|
1061
1180
|
raise ex
|
1062
1181
|
rescue *CONNECTION_EXCEPTIONS => ex
|
1063
|
-
if tries.zero?
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
Rails.logger.error("Rest Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
1069
|
-
end
|
1070
|
-
end
|
1071
|
-
raise ex
|
1182
|
+
if !tries.zero?
|
1183
|
+
tries -= 1
|
1184
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
1185
|
+
sleep(timeout_sleep_interval)
|
1186
|
+
retry
|
1072
1187
|
end
|
1073
1188
|
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1189
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
1190
|
+
raise ex
|
1191
|
+
|
1077
1192
|
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
1193
|
+
|
1078
1194
|
if !tries.zero?
|
1079
1195
|
tries -= 1
|
1196
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
1080
1197
|
if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
|
1081
1198
|
retry
|
1082
1199
|
elsif timeout_retry
|
@@ -1084,20 +1201,15 @@ module ZuoraAPI
|
|
1084
1201
|
retry
|
1085
1202
|
end
|
1086
1203
|
end
|
1087
|
-
|
1088
|
-
if output_exception_messages
|
1089
|
-
|
1090
|
-
Rails.logger.error("Rest Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
1091
|
-
else
|
1092
|
-
Rails.logger.error("Rest Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from #{url}", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
1204
|
+
|
1205
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
1206
|
+
ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read/write timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.is_a?(Timeout::Error) && !ex.instance_of?(ZuoraAPI::Exceptions::ZuoraAPIReadTimeout)
|
1096
1207
|
raise ex
|
1208
|
+
|
1097
1209
|
rescue => ex
|
1098
1210
|
raise ex
|
1099
|
-
|
1100
|
-
|
1211
|
+
ensure
|
1212
|
+
self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
|
1101
1213
|
end
|
1102
1214
|
|
1103
1215
|
def update_create_tenant
|
@@ -1119,8 +1231,9 @@ module ZuoraAPI
|
|
1119
1231
|
while !response["nextPage"].blank?
|
1120
1232
|
url = self.rest_endpoint(response["nextPage"].split('/v1/').last)
|
1121
1233
|
Rails.logger.debug("Fetch Catalog URL #{url}")
|
1122
|
-
output_json, response = self.rest_call(:
|
1123
|
-
|
1234
|
+
output_json, response = self.rest_call(debug: false, url: url, timeout_retry: true)
|
1235
|
+
|
1236
|
+
if !/(true|t|yes|y|1)$/.match(output_json['success'].to_s) || output_json['success'].class != TrueClass
|
1124
1237
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}", response)
|
1125
1238
|
end
|
1126
1239
|
output_json["products"].each do |product|
|
@@ -1146,7 +1259,7 @@ module ZuoraAPI
|
|
1146
1259
|
return products, catalog_map
|
1147
1260
|
end
|
1148
1261
|
|
1149
|
-
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:
|
1262
|
+
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)
|
1150
1263
|
raise "file_path must be of class Pathname" if file_path.class != Pathname
|
1151
1264
|
|
1152
1265
|
retry_count ||= timeout_retries
|
@@ -1251,14 +1364,20 @@ module ZuoraAPI
|
|
1251
1364
|
return file_handle
|
1252
1365
|
when Net::HTTPUnauthorized
|
1253
1366
|
if z_session
|
1254
|
-
|
1367
|
+
unless (retry_count -= 1).zero?
|
1255
1368
|
self.new_session
|
1256
|
-
raise
|
1257
|
-
else
|
1258
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
1369
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError, 'Retrying'
|
1259
1370
|
end
|
1371
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
1372
|
+
end
|
1373
|
+
raise
|
1374
|
+
when Net::HTTPNotFound
|
1375
|
+
if url.include?(self.fileURL)
|
1376
|
+
raise ZuoraAPI::Exceptions::FileDownloadError.new(
|
1377
|
+
"The current tenant does not have a file with id '#{url.split('/').last}'"
|
1378
|
+
)
|
1260
1379
|
else
|
1261
|
-
raise
|
1380
|
+
raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
|
1262
1381
|
end
|
1263
1382
|
else
|
1264
1383
|
raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
|
@@ -1269,133 +1388,81 @@ module ZuoraAPI
|
|
1269
1388
|
sleep(5)
|
1270
1389
|
if (retry_count -= 1) >= 0
|
1271
1390
|
retry
|
1272
|
-
else
|
1273
|
-
Rails.logger.error("File Download Failed")
|
1274
|
-
raise
|
1275
1391
|
end
|
1392
|
+
Rails.logger.error("File Download Failed")
|
1393
|
+
raise
|
1276
1394
|
end
|
1277
1395
|
|
1278
1396
|
def getDataSourceExport(query, extract: true, encrypted: false, zip: true, z_track_id: "")
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
xml['
|
1289
|
-
xml['ns1'].create do
|
1290
|
-
xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
|
1291
|
-
xml['ns2'].Format 'csv'
|
1292
|
-
xml['ns2'].Zip zip
|
1293
|
-
xml['ns2'].Name 'googman'
|
1294
|
-
xml['ns2'].Query query
|
1295
|
-
xml['ns2'].Encrypted encrypted
|
1296
|
-
end
|
1297
|
-
end
|
1298
|
-
end
|
1397
|
+
tries ||= 3
|
1398
|
+
|
1399
|
+
output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true, zuora_track_id: z_track_id) do |xml|
|
1400
|
+
xml['ns1'].create do
|
1401
|
+
xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
|
1402
|
+
xml['ns2'].Format 'csv'
|
1403
|
+
xml['ns2'].Zip zip
|
1404
|
+
xml['ns2'].Name 'googman'
|
1405
|
+
xml['ns2'].Query query
|
1406
|
+
xml['ns2'].Encrypted encrypted
|
1299
1407
|
end
|
1300
1408
|
end
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
confirmRequest = Nokogiri::XML::Builder.new do |xml|
|
1312
|
-
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
|
1313
|
-
xml['SOAP-ENV'].Header do
|
1314
|
-
xml['ns1'].SessionHeader do
|
1315
|
-
xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
|
1316
|
-
end
|
1317
|
-
end
|
1318
|
-
xml['SOAP-ENV'].Body do
|
1319
|
-
xml['ns1'].query do
|
1320
|
-
xml['ns1'].queryString "SELECT Id, CreatedById, CreatedDate, Encrypted, FileId, Format, Name, Query, Size, Status, StatusReason, UpdatedById, UpdatedDate, Zip From Export where Id = '#{id}'"
|
1321
|
-
end
|
1322
|
-
end
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
|
1412
|
+
|
1413
|
+
result = 'Waiting'
|
1414
|
+
while result != "Completed"
|
1415
|
+
sleep 3
|
1416
|
+
output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true, zuora_track_id: z_track_id) do |xml|
|
1417
|
+
xml['ns1'].query do
|
1418
|
+
xml['ns1'].queryString "SELECT Id, CreatedById, CreatedDate, Encrypted, FileId, Format, Name, Query, Size, Status, StatusReason, UpdatedById, UpdatedDate, Zip From Export where Id = '#{id}'"
|
1323
1419
|
end
|
1324
1420
|
end
|
1325
|
-
result = '
|
1326
|
-
|
1327
|
-
while result != "Completed"
|
1328
|
-
sleep 3
|
1329
|
-
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 => 120)
|
1330
|
-
|
1331
|
-
output_xml = Nokogiri::XML(response_query.body)
|
1332
|
-
result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
|
1333
|
-
status_code = response_query.code if response_query
|
1334
|
-
|
1335
|
-
raise_errors(type: :SOAP, body: output_xml, response: response_query) if result.blank? || result == "Failed"
|
1336
|
-
# raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if result.blank? || result == "Failed"
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
|
1340
|
-
export_file = get_file(:url => self.fileURL(file_id))
|
1341
|
-
export_file_path = export_file.path
|
1342
|
-
Rails.logger.debug("=====> Export path #{export_file.path}")
|
1343
|
-
|
1344
|
-
if extract && zip
|
1345
|
-
require "zip"
|
1346
|
-
new_path = export_file_path.partition('.zip').first
|
1347
|
-
zipped = Zip::File.open(export_file_path)
|
1348
|
-
file_handle = zipped.entries.first
|
1349
|
-
file_handle.extract(new_path)
|
1350
|
-
File.delete(export_file_path)
|
1351
|
-
return new_path
|
1352
|
-
else
|
1353
|
-
return export_file_path
|
1354
|
-
end
|
1355
|
-
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
1356
|
-
if !(tries -= 1).zero?
|
1357
|
-
Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
|
1358
|
-
self.new_session
|
1359
|
-
retry
|
1360
|
-
else
|
1361
|
-
raise ex
|
1362
|
-
end
|
1363
|
-
|
1364
|
-
rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
|
1365
|
-
if !(tries -= 1).zero?
|
1366
|
-
Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
|
1367
|
-
sleep 10
|
1368
|
-
retry
|
1369
|
-
else
|
1370
|
-
raise ex
|
1371
|
-
end
|
1372
|
-
|
1373
|
-
rescue *ZUORA_API_ERRORS => ex
|
1374
|
-
raise ex
|
1375
|
-
|
1376
|
-
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
1377
|
-
if !(tries -= 1).zero?
|
1378
|
-
Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
|
1379
|
-
sleep 5
|
1380
|
-
retry
|
1381
|
-
else
|
1382
|
-
raise ex
|
1383
|
-
end
|
1421
|
+
result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
|
1422
|
+
end
|
1384
1423
|
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1424
|
+
file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
|
1425
|
+
export_file = get_file(:url => self.fileURL(file_id))
|
1426
|
+
export_file_path = export_file.path
|
1427
|
+
|
1428
|
+
if extract && zip
|
1429
|
+
require "zip"
|
1430
|
+
new_path = export_file_path.partition('.zip').first
|
1431
|
+
zipped = Zip::File.open(export_file_path)
|
1432
|
+
file_handle = zipped.entries.first
|
1433
|
+
file_handle.extract(new_path)
|
1434
|
+
File.delete(export_file_path)
|
1435
|
+
return new_path
|
1436
|
+
else
|
1437
|
+
return export_file_path
|
1438
|
+
end
|
1439
|
+
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
1440
|
+
if !(tries -= 1).zero?
|
1441
|
+
Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
|
1442
|
+
self.new_session
|
1443
|
+
retry
|
1444
|
+
end
|
1445
|
+
raise ex
|
1391
1446
|
|
1392
|
-
|
1393
|
-
|
1447
|
+
rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
|
1448
|
+
if !(tries -= 1).zero?
|
1449
|
+
Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
|
1450
|
+
sleep(self.timeout_sleep)
|
1451
|
+
retry
|
1394
1452
|
end
|
1453
|
+
raise ex
|
1454
|
+
|
1455
|
+
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
1456
|
+
if !(tries -= 1).zero?
|
1457
|
+
Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
|
1458
|
+
sleep(self.timeout_sleep)
|
1459
|
+
retry
|
1460
|
+
end
|
1461
|
+
raise ex
|
1395
1462
|
end
|
1396
1463
|
|
1397
1464
|
def query(query, parse = false)
|
1398
|
-
output_xml, input_xml = self.soap_call(
|
1465
|
+
output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true) do |xml|
|
1399
1466
|
xml['ns1'].query do
|
1400
1467
|
xml['ns1'].queryString query
|
1401
1468
|
end
|