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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e79d63055902d66670dad6abed776b5c02ccb422b886bad2e632624e43eb16ba
4
- data.tar.gz: 8abaee55dad2d4c3636bcbd08159a26dc403a8041aecb7d2ce2f6d84bd9f3917
3
+ metadata.gz: e0ab2609485e4d653738cdd84169feac4058531f4caac36e62b88fd8910008cc
4
+ data.tar.gz: d86b6df5e09c4eed423b362e0334f5605da724e6a7956bd6c896aeccbc9e3fa5
5
5
  SHA512:
6
- metadata.gz: 72165d5892a404b6e25c6f32e83e52065a23837eeb7f4d72a40f13e7c642c460362a3c4ed83b51760e5efac650c5eaffc4d4baa1b97b6c980818273af3fa9af1
7
- data.tar.gz: 271b546b700d026e9ac12a22418eb90fc45a2a2b1bd66f872586a5552a4dfe703cb95f9f7dd78cb38960d4885a65e20acab0095ab0a2b5c5b3339295b129b995
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
+
@@ -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 < Net::ReadTimeout
241
+ class ZuoraAPIReadTimeout < Timeout::Error
228
242
  attr_reader :code, :response, :request
229
243
  attr_writer :default_message
230
244
 
@@ -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
- MIN_Endpoint = '96.0'
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
- Net::ReadTimeout,
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
- ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout,
43
- ZuoraAPI::Exceptions::ZuoraAPIReadTimeout,
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/#{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)
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.url.blank?
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
- region = update_region
279
- environment = update_environment
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(self.rest_endpoint).host
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) if self.status == 'Active'
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: 120,
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
- xml["#{ns1}"].SessionHeader do
398
- xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: zuora_track_id)
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 = { 'Content-Type' => "text/xml; charset=utf-8", 'Accept' => 'text/xml'}
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
- raise ex
460
- else
461
- response = ex.response unless response
462
- return output_xml, input_xml, response
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
- if output_exception_messages
467
- if Rails.logger.class.to_s == "Ougai::Logger"
468
- Rails.logger.error("SOAP Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
469
- else
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
- tries -= 1
477
- sleep(timeout_sleep_interval)
478
- retry
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
- if Rails.logger.class.to_s == "Ougai::Logger"
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
- else
503
- return output_xml, input_xml, response
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 #{request_uri}", response)
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 #{request_uri}", response)
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
- reason = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
555
- if reason.present?
556
- message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
557
- error = message.match(/^[\w\d]{16}\: (Unexpected error.|No HTTP Response|Socket Timeout|There is an internal error, please try again later)/).present? ? 'UNEXPECTED_ERROR' : 'FATAL_ERROR'
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
- messages_array = body.fetch("reasons", []).map {|error| error['message']}.compact
613
- messages_array = messages_array.push(body.dig("error", 'message')).compact if body.dig('error').class == Hash
614
- codes_array = body.fetch("reasons", []).map {|error| error['code']}.compact
615
- codes_array = codes_array.push(body.dig("error", 'code')).compact if body.dig('error').class == Hash
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
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
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 => 120)
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
- if log_errors
939
- if Rails.logger.class.to_s == "Ougai::Logger"
940
- Rails.logger.error("Describe - Timed out will retry after #{self.timeout_sleep} seconds", ex)
941
- else
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
- tries -= 1
949
- sleep(self.timeout_sleep)
950
- retry
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: 120,
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
- raise ex
1056
- else
1057
- response = ex.response unless response
1058
- return [output_json, response]
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
- if output_exception_messages
1065
- if Rails.logger.class.to_s == "Ougai::Logger"
1066
- Rails.logger.error("Rest Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
1067
- else
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
- tries -= 1
1075
- sleep(timeout_sleep_interval)
1076
- retry
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
- if Rails.logger.class.to_s == "Ougai::Logger"
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
- else
1100
- return [output_json, response]
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(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true )
1123
- if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
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: 120, session_type: :basic, **execute_params)
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
- if !(retry_count -= 1).zero?
1367
+ unless (retry_count -= 1).zero?
1255
1368
  self.new_session
1256
- raise response.class
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
- begin
1280
- tries ||= 3
1281
- request = Nokogiri::XML::Builder.new do |xml|
1282
- 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
1283
- xml['SOAP-ENV'].Header do
1284
- xml['ns1'].SessionHeader do
1285
- xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1286
- end
1287
- end
1288
- xml['SOAP-ENV'].Body do
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
- 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 => 120)
1303
-
1304
- output_xml = Nokogiri::XML(response_query.body)
1305
- raise_errors(type: :SOAP, body: output_xml, response: response_query) if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1306
-
1307
- # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1308
-
1309
- id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
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 = 'Waiting'
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
- rescue Errno::ECONNRESET => ex
1386
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
1387
- retry
1388
- else
1389
- raise ex
1390
- end
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
- rescue ZuoraAPI::Exceptions::BadEntityError => ex
1393
- raise ex
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({:debug => false, :timeout_retry => true}) do |xml|
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