zuora_api 1.8.1 → 1.9.0

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: b331dbd16a302e01b8cf4dc918343eb3c3bc62e809c15e4020eb2463b0b725ac
4
- data.tar.gz: c8f2f3f7c3153280e0624e38658b51f2cdd2e3ded90ad916701c2392c21223d1
3
+ metadata.gz: 2c377d99b1a4c4a64d1b46d45b19e42136da2e0faba049e7a45621b3daaa41ad
4
+ data.tar.gz: e00b3a6e9628aed4086190a3be983bd7998196812df9d53010c274f882a36749
5
5
  SHA512:
6
- metadata.gz: 29be5568ae328fe982f8c25f25d802f9ab941c2e0a4aa247d12ba1ef33afb25a77e7d738a1509e96a1e28ef8b07c68a205a9c3140dcc83f36a36536cc5d6c42e
7
- data.tar.gz: b450c458b5402843f9fdf57789cfaf28216c14d74505fe031c95bee79505f6273164f299c78dc73e4848c0ed9c620b41f3cb7ef219774e74d216d12c8a26f0c3
6
+ metadata.gz: 14c76c2a211f55a38d79940436241c3fa9203a2c1a960415a3a2287950adb6059461571703530d4754e803833f3ffc98e42182c50588498a51070a0a6984cadb
7
+ data.tar.gz: a2176a640fb0f77bbc0893f5161c30c822a9a8cc1147b33e6f088ce60240666a19853f82e5d85ecf676273c9a9ecc59c7aaee006f0aa63c8fd52b8fdc60ee2d1
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,12 @@ 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 GOVERNEDBY 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.
data/lib/zuora_api.rb CHANGED
@@ -4,6 +4,8 @@ require "zuora_api/logins/basic"
4
4
  require "zuora_api/logins/oauth"
5
5
  require 'zuora_api/exceptions'
6
6
  require "insights_api/login"
7
+ require "ougai"
8
+
7
9
  module ZuoraAPI
8
10
  # Your code goes here...
9
11
  end
@@ -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,21 +39,23 @@ 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
+
47
47
  attr_accessor :region, :url, :wsdl_number, :current_session, :bearer_token, :oauth_session_expires_at, :environment, :status, :errors, :current_error, :user_info, :tenant_id, :tenant_name, :entity_id, :timeout_sleep, :hostname, :zconnect_provider
48
48
 
49
49
  def initialize(url: nil, entity_id: nil, session: nil, status: nil, bearer_token: nil, oauth_session_expires_at: nil, **keyword_args)
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
@@ -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="")
@@ -298,7 +299,7 @@ module ZuoraAPI
298
299
  update_environment
299
300
  endpoint = url
300
301
  url_postfix = {"US" => ".", "EU" => ".eu.", "NA" => ".na."}[self.region]
301
-
302
+
302
303
  case self.environment
303
304
  when 'Test'
304
305
  endpoint = "https://rest.test#{url_postfix}zuora.com"
@@ -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,25 +397,25 @@ 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
374
403
 
375
404
  def soap_call(
376
- ns1: 'ns1',
377
- ns2: 'ns2',
378
- batch_size: nil,
379
- single_transaction: false,
380
- debug: false,
381
- zuora_track_id: nil,
382
- errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
383
- z_session: true,
384
- timeout_retry: false,
385
- timeout: 120,
386
- timeout_sleep_interval: self.timeout_sleep,
405
+ ns1: 'ns1',
406
+ ns2: 'ns2',
407
+ batch_size: nil,
408
+ headers: {},
409
+ single_transaction: false,
410
+ debug: false,
411
+ zuora_track_id: nil,
412
+ errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
413
+ z_session: true,
414
+ timeout_retry: false,
415
+ timeout: 130,
416
+ timeout_sleep_interval: self.timeout_sleep,
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,76 @@ 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)
529
+ end
530
+
531
+ def error_logger(ex)
532
+ return unless Rails.logger.is_a? Ougai::Logger
533
+
534
+ exception_args = Rails.logger.with_fields.merge(self.exception_args(ex))
535
+ case ex
536
+ when ZuoraAPI::Exceptions::ZuoraAPIUnkownError, ZuoraAPI::Exceptions::ZuoraDataIntegrity
537
+ Rails.logger.error('Zuora Unknown/Integrity Error', ex, exception_args)
538
+ when ZuoraAPI::Exceptions::ZuoraAPIRequestLimit
539
+ Rails.logger.info('Zuora APILimit Reached', ex, exception_args)
540
+ when *(ZuoraAPI::Login::ZUORA_API_ERRORS-ZuoraAPI::Login::ZUORA_SERVER_ERRORS)
541
+ #Rails.logger.debug('Zuora API Error', ex, self.exception_args(ex))
542
+ when *ZuoraAPI::Login::ZUORA_SERVER_ERRORS
543
+ Rails.logger.error('Zuora Server Error', ex, exception_args)
544
+ end
545
+ end
546
+
547
+ def log(location: "Rest Call", exception: nil, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :info )
548
+ level = :debug if ![:debug, :info, :warn, :error, :fatal].include?(level)
549
+ if Rails.logger.is_a? Ougai::Logger
550
+ Rails.logger.send(level.to_sym, "#{location} - #{message}", exception)
551
+ else
552
+ Rails.logger.send(level.to_sym, "#{location} - #{exception.class} #{message}")
553
+ end
554
+ end
555
+
556
+ def exception_args(ex)
557
+ args = {}
558
+ if defined?(ex.response) && ex.response.present?
559
+ args.merge!({
560
+ request: {
561
+ path: ex.response.request.path.to_s,
562
+ method: ex.response.request.http_method.to_s.split("Net::HTTP::").last.upcase,
563
+ params: ex.response.request.raw_body.to_s,
564
+ 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,
565
+ },
566
+ response: {
567
+ status: ex.response.code,
568
+ params: ex.response.body.to_s,
569
+ headers: ex.response.headers.to_s,
570
+ },
571
+ zuora_trace_id: ex.response.headers["zuora-request-id"],
572
+ zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"],
573
+ })
574
+ elsif defined?(ex.request) && ex.request.present?
575
+ args.merge!({
576
+ request: {
577
+ path: ex.request.path.to_s,
578
+ method: ex.request.http_method.to_s.split("Net::HTTP::").last.upcase,
579
+ params: ex.request.options[:body],
580
+ headers: ex.request.options[:headers].map{|k,v| [k.to_s, k.to_s.downcase.strip == "authorization" ? "VALUE FILTERED" : v]}.to_h.to_s
581
+ }
582
+ })
583
+ args.merge!({
584
+ zuora_track_id: ex.request.options[:headers]["Zuora-Track-Id"]
585
+ }) if ex.request.options[:headers]["Zuora-Track-Id"].present?
586
+ end
587
+ rescue => ex
588
+ Rails.logger.error("Failed to create exception arguments", ex, args)
589
+ ensure
590
+ return args
504
591
  end
505
592
 
506
593
  def raise_errors(type: :SOAP, body: nil, response: nil)
@@ -517,24 +604,24 @@ module ZuoraAPI
517
604
  end
518
605
 
519
606
  if [502,503].include?(response.code)
520
- raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from #{request_uri}", response)
607
+ raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
521
608
  end
522
609
 
523
610
  # Check failure response code
524
611
  case response.code
525
612
  when 504
526
- raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from #{request_uri}", response)
613
+ raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from 'https://#{rest_domain(endpoint: request_uri)}'", response)
527
614
  when 429
528
615
  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
616
  when 401
530
-
617
+
531
618
  else
532
619
  if body.class == Hash
533
620
  case request_path
534
621
  when /^\/v1\/connections$/
535
- response_headers = response.headers.to_h
622
+ response_headers = response.headers.to_h
536
623
  raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing cookies for authentication call", response) if response_headers['set-cookie'].blank?
537
- z_session_cookie = response_headers.fetch('set-cookie', []).select{|x| x.match(/^ZSession=.*/) }.first
624
+ z_session_cookie = response_headers.fetch('set-cookie', []).select{|x| x.match(/^ZSession=.*/) }.first
538
625
  raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing ZSession cookie for authentication call", response) if z_session_cookie.blank?
539
626
  end
540
627
  end
@@ -544,6 +631,10 @@ module ZuoraAPI
544
631
  when :SOAP
545
632
  error, success, message = get_soap_error_and_message(body)
546
633
 
634
+ if body.xpath('//fns:LoginFault', 'fns' =>'http://fault.api.zuora.com/').present?
635
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response)
636
+ end
637
+
547
638
  if body.xpath('//ns1:queryResponse', 'ns1' => 'http://api.zuora.com/').present? &&
548
639
  body.xpath(
549
640
  '//ns1:records[@xsi:type="ns2:Export"]',
@@ -551,12 +642,12 @@ module ZuoraAPI
551
642
  ).present?
552
643
  result = body.xpath('//ns2:Status', 'ns2' => 'http://object.api.zuora.com/').text
553
644
  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'
645
+ message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
646
+ error = 'FATAL_ERROR'
647
+ if message.present?
648
+ identifier, new_message = message.scan(/^([\w\d]{16})\: (.*)/).first
649
+ error, message = ['UNEXPECTED_ERROR', new_message] if new_message.present?
558
650
  else
559
- error = 'FATAL_ERROR'
560
651
  message = 'Export failed due to unknown reason. Consult api logs.'
561
652
  end
562
653
  end
@@ -577,19 +668,19 @@ module ZuoraAPI
577
668
  end
578
669
 
579
670
  self.errors_via_content_type(response: response, type: :xml)
580
-
671
+
581
672
  when :JSON
582
673
  case request_path
583
674
  when /^\/query\/jobs.*/ #DataQuery Paths
584
675
  return if body.class != Hash
585
676
  case match_string
586
- when /^GET::200::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job, Capture of the id is present if needed in future error responses.
677
+ when /^GET::200::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job, Capture of the id is present if needed in future error responses.
587
678
  if body.dig('data', "errorCode") == "LINK_10000005"
588
679
  raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body.dig('data', "errorMessage"), response)
589
- elsif (body.dig('data', "errorMessage").present? || body.dig('data', "queryStatus") == "failed")
680
+ elsif (body.dig('data', "errorMessage").present? || body.dig('data', "queryStatus") == "failed")
590
681
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('data', "errorMessage"), response)
591
682
  end
592
- when /^GET::404::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job not found, capture of the id is present if needed in future error responses.
683
+ when /^GET::404::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job not found, capture of the id is present if needed in future error responses.
593
684
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
594
685
  when /^POST::400::\/query\/jobs$/ #Create DQ job
595
686
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
@@ -605,14 +696,31 @@ module ZuoraAPI
605
696
  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
697
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response) if reporting_message.present?
607
698
  end
699
+ when /\/objects\/batch\//
700
+ if body['code'].present? && /61$/.match(body['code'].to_s).present? # if last 2 digits of code are 61
701
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body['message'], nil, body['details'])
702
+ end
703
+ when /^\/api\/v1\/payment_plans.*/
704
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['error'], response) if body['error']
608
705
  end
609
706
 
610
707
  body = body.dig("results").present? ? body["results"] : body if body.class == Hash
611
708
  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
709
+ reason_keys = %w(reasons errors)
710
+ message_keys = %w(message title)
711
+ messages_array, codes_array = [[],[]]
712
+ reason_keys.each do |rsn_key|
713
+ message_keys.each do |msg_key|
714
+ messages_array = body.fetch(rsn_key, []).map {|error| error[msg_key]}.compact
715
+ break if messages_array.present?
716
+ end
717
+ codes_array = body.fetch(rsn_key, []).map {|error| error['code']}.compact
718
+ break if messages_array.present? && codes_array.present?
719
+ end
720
+ if body.dig('error').class == Hash
721
+ messages_array = messages_array.push(body.dig("error", 'message')).compact
722
+ codes_array = codes_array.push(body.dig("error", 'code')).compact
723
+ end
616
724
 
617
725
  if body['message'] == 'request exceeded limit'
618
726
  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)
@@ -635,7 +743,7 @@ module ZuoraAPI
635
743
  end
636
744
 
637
745
  #Oauth Tokens - User deactivated
638
- if body['path'] == '/oauth/token'
746
+ if body['path'] == '/oauth/token'
639
747
  if body['status'] == 403 && response.code == 403
640
748
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Forbidden", response)
641
749
  elsif body['status'] == 400 && response.code == 400 && body['message'].include?("Invalid client id")
@@ -644,7 +752,11 @@ module ZuoraAPI
644
752
  end
645
753
 
646
754
  if body['error'] == 'Unauthorized' && body['status'] == 401
647
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
755
+ if body['message'].present?
756
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
757
+ else
758
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
759
+ end
648
760
  end
649
761
  #Authentication failed
650
762
  if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
@@ -667,7 +779,7 @@ module ZuoraAPI
667
779
  end
668
780
  #Internal Server Error
669
781
  if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(60)
670
- if messages_array.uniq.size == 1
782
+ if messages_array.uniq.size == 1
671
783
  if messages_array.first.match(/^Transaction declined.*|^There is an invoice pending tax.*|^The Zuora GetTax call to Avalara.*|^The tax calculation call to Zuora Connect returned the following error: Status Code: 4.*/)
672
784
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(messages_array.first, response)
673
785
  end
@@ -740,18 +852,18 @@ module ZuoraAPI
740
852
  self.errors_via_content_type(response: response, type: :json)
741
853
 
742
854
  #All other errors
743
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new(response.body, response) if ![200,201].include?(response.code)
855
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(response.body, response) if ![200,201].include?(response.code)
744
856
  end
745
857
  end
746
858
 
747
859
  def errors_via_content_type(response: nil, type: :xml)
748
860
  response_content_types = response.headers.transform_keys(&:downcase).fetch('content-type', []).first || ""
749
-
861
+
750
862
  if response_content_types.include?('application/json') && type != :json
751
863
  output_json = JSON.parse(response.body)
752
864
  self.raise_errors(type: :JSON, body: output_json, response: response)
753
-
754
- elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml')) and type != :xml
865
+
866
+ elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml') || response_content_types.include?('application/soap+xml')) and type != :xml
755
867
  output_xml = Nokogiri::XML(response.body)
756
868
  self.raise_errors(type: :SOAP, body: output_xml, response: response)
757
869
 
@@ -770,8 +882,10 @@ module ZuoraAPI
770
882
  when /Service Unavailable/
771
883
  raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new(error_message, response)
772
884
  when /Client sent a bad request./, /Bad Request/, /403 Forbidden/
773
- raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
774
- else
885
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
886
+ when /414 Request-URI Too Large/
887
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Request URL is too long", response)
888
+ else
775
889
  raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
776
890
  end
777
891
  end
@@ -781,7 +895,7 @@ module ZuoraAPI
781
895
 
782
896
  raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(response.body, response) if response.code == 500
783
897
  end
784
-
898
+
785
899
 
786
900
  def get_soap_error_and_message(body)
787
901
  error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
@@ -797,6 +911,11 @@ module ZuoraAPI
797
911
  message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
798
912
  end
799
913
 
914
+ if error.blank? || message.blank?
915
+ error = body.xpath('//soapenv:Value', 'soapenv'=>'http://www.w3.org/2003/05/soap-envelope').text
916
+ message = body.xpath('//soapenv:Text', 'soapenv'=>'http://www.w3.org/2003/05/soap-envelope').text
917
+ end
918
+
800
919
  #Update/Create/Delete Calls with multiple requests and responses
801
920
  if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
802
921
  error = []
@@ -840,18 +959,23 @@ module ZuoraAPI
840
959
  raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
841
960
  end
842
961
  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/
962
+ 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
963
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
845
964
  when /.*UNEXPECTED_ERROR/
846
965
  raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(message, response, errors, success)
847
966
  when /.*soapenv:Server.*/
848
967
  if /^Invalid value.*for type.*|^Id is invalid|^date string can not be less than 19 charactors$/.match(message).present?
849
968
  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?
969
+ elsif /^unknown$|^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
851
970
  raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
852
971
  end
853
972
  raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
854
- else
973
+ when /soapenv:Receiver/
974
+ if /^com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character.*$/.match(message).present?
975
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
976
+ end
977
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
978
+ else
855
979
  raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Z:#{error}::#{message}", response, errors, success)
856
980
  end
857
981
  end
@@ -900,7 +1024,7 @@ module ZuoraAPI
900
1024
  base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
901
1025
  url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
902
1026
  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)
1027
+ response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 130)
904
1028
 
905
1029
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401', response) if response.code == 401
906
1030
 
@@ -933,35 +1057,31 @@ module ZuoraAPI
933
1057
  end
934
1058
  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
1059
  end
1060
+
1061
+ return des_hash
936
1062
  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
1063
+ if !tries.zero?
1064
+ tries -= 1
1065
+ self.log(location: "Describe", exception: ex, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :debug)
1066
+ sleep(self.timeout_sleep)
1067
+ retry
946
1068
  end
947
1069
 
948
- tries -= 1
949
- sleep(self.timeout_sleep)
950
- retry
1070
+ self.log(location: "Describe", exception: ex, message: "Timed out", level: :error) if log_errors
1071
+ raise ex
1072
+
951
1073
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
952
1074
  if !tries.zero? && self.status == 'Active'
953
1075
  tries -= 1
954
1076
  Rails.logger.debug("Describe session expired. Starting new session.")
955
1077
  self.new_session
956
1078
  retry
957
- else
958
- Rails.logger.error("Describe session expired. Starting new session.") if log_errors
959
- raise ex
960
1079
  end
1080
+
1081
+ Rails.logger.error("Describe session expired. Starting new session.") if log_errors
1082
+ raise ex
961
1083
  rescue => ex
962
1084
  raise ex
963
- else
964
- return des_hash
965
1085
  end
966
1086
 
967
1087
  def rest_call(
@@ -974,11 +1094,12 @@ module ZuoraAPI
974
1094
  z_session: true,
975
1095
  session_type: :basic,
976
1096
  timeout_retry: false,
977
- timeout: 120,
978
- timeout_sleep_interval: self.timeout_sleep,
1097
+ timeout: 130,
1098
+ timeout_sleep_interval: self.timeout_sleep,
979
1099
  multipart: false,
980
1100
  stream_body: false,
981
1101
  output_exception_messages: true,
1102
+ zuora_track_id: nil,
982
1103
  **keyword_args,
983
1104
  &block
984
1105
  )
@@ -988,12 +1109,13 @@ module ZuoraAPI
988
1109
 
989
1110
  authentication_headers = {}
990
1111
  if z_session
991
- authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type) }
1112
+ authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type, zuora_track_id: zuora_track_id) }
992
1113
  if self.entity_id.present?
993
1114
  authentication_headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
994
1115
  authentication_headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
995
1116
  end
996
1117
  end
1118
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
997
1119
 
998
1120
  modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
999
1121
 
@@ -1019,18 +1141,20 @@ module ZuoraAPI
1019
1141
  Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
1020
1142
 
1021
1143
  raise_errors(type: :JSON, body: output_json, response: response)
1022
- rescue
1144
+ rescue => ex
1023
1145
  reset_files(body) if multipart
1024
1146
  raise
1025
1147
  end
1148
+
1149
+ return [output_json, response]
1026
1150
  rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
1027
1151
  if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
1028
1152
  session_type = :bearer
1029
1153
  retry
1030
- else
1031
- Rails.logger.debug("Rest Call - Session Bad Auth type")
1032
- raise ex
1033
1154
  end
1155
+ Rails.logger.debug("Rest Call - Session Bad Auth type")
1156
+ raise ex
1157
+
1034
1158
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
1035
1159
  if !tries.zero? && z_session
1036
1160
  tries -= 1
@@ -1043,40 +1167,35 @@ module ZuoraAPI
1043
1167
  end
1044
1168
 
1045
1169
  retry
1046
- else
1047
- if errors.include?(ex.class)
1048
- raise ex
1049
- else
1050
- return [output_json, response]
1051
- end
1052
1170
  end
1171
+
1172
+ raise ex if errors.include?(ex.class)
1173
+ return [output_json, response]
1174
+
1053
1175
  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
1176
+ raise ex if errors.include?(ex.class)
1177
+
1178
+ response = ex.response unless response
1179
+ return [output_json, response]
1180
+
1060
1181
  rescue ZuoraAPI::Exceptions::BadEntityError => ex
1061
1182
  raise ex
1062
1183
  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
1184
+ if !tries.zero?
1185
+ tries -= 1
1186
+ self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
1187
+ sleep(timeout_sleep_interval)
1188
+ retry
1072
1189
  end
1073
1190
 
1074
- tries -= 1
1075
- sleep(timeout_sleep_interval)
1076
- retry
1191
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1192
+ raise ex
1193
+
1077
1194
  rescue *CONNECTION_READ_EXCEPTIONS => ex
1195
+
1078
1196
  if !tries.zero?
1079
1197
  tries -= 1
1198
+ self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
1080
1199
  if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
1081
1200
  retry
1082
1201
  elsif timeout_retry
@@ -1084,20 +1203,15 @@ module ZuoraAPI
1084
1203
  retry
1085
1204
  end
1086
1205
  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)
1206
+
1207
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1208
+ 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
1209
  raise ex
1210
+
1097
1211
  rescue => ex
1098
1212
  raise ex
1099
- else
1100
- return [output_json, response]
1213
+ ensure
1214
+ self.error_logger(ex) if defined?(ex)
1101
1215
  end
1102
1216
 
1103
1217
  def update_create_tenant
@@ -1119,8 +1233,9 @@ module ZuoraAPI
1119
1233
  while !response["nextPage"].blank?
1120
1234
  url = self.rest_endpoint(response["nextPage"].split('/v1/').last)
1121
1235
  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
1236
+ output_json, response = self.rest_call(debug: false, url: url, timeout_retry: true)
1237
+
1238
+ if !/(true|t|yes|y|1)$/.match(output_json['success'].to_s) || output_json['success'].class != TrueClass
1124
1239
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}", response)
1125
1240
  end
1126
1241
  output_json["products"].each do |product|
@@ -1146,7 +1261,7 @@ module ZuoraAPI
1146
1261
  return products, catalog_map
1147
1262
  end
1148
1263
 
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)
1264
+ 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
1265
  raise "file_path must be of class Pathname" if file_path.class != Pathname
1151
1266
 
1152
1267
  retry_count ||= timeout_retries
@@ -1251,14 +1366,20 @@ module ZuoraAPI
1251
1366
  return file_handle
1252
1367
  when Net::HTTPUnauthorized
1253
1368
  if z_session
1254
- if !(retry_count -= 1).zero?
1369
+ unless (retry_count -= 1).zero?
1255
1370
  self.new_session
1256
- raise response.class
1257
- else
1258
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1371
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError, 'Retrying'
1259
1372
  end
1373
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1374
+ end
1375
+ raise
1376
+ when Net::HTTPNotFound
1377
+ if url.include?(self.fileURL)
1378
+ raise ZuoraAPI::Exceptions::FileDownloadError.new(
1379
+ "The current tenant does not have a file with id '#{url.split('/').last}'"
1380
+ )
1260
1381
  else
1261
- raise
1382
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1262
1383
  end
1263
1384
  else
1264
1385
  raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
@@ -1269,133 +1390,81 @@ module ZuoraAPI
1269
1390
  sleep(5)
1270
1391
  if (retry_count -= 1) >= 0
1271
1392
  retry
1272
- else
1273
- Rails.logger.error("File Download Failed")
1274
- raise
1275
1393
  end
1394
+ Rails.logger.error("File Download Failed")
1395
+ raise
1276
1396
  end
1277
1397
 
1278
1398
  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
1399
+ tries ||= 3
1400
+
1401
+ output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true, zuora_track_id: z_track_id) do |xml|
1402
+ xml['ns1'].create do
1403
+ xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
1404
+ xml['ns2'].Format 'csv'
1405
+ xml['ns2'].Zip zip
1406
+ xml['ns2'].Name 'googman'
1407
+ xml['ns2'].Query query
1408
+ xml['ns2'].Encrypted encrypted
1299
1409
  end
1300
1410
  end
1411
+ end
1301
1412
 
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
1413
+ id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
1310
1414
 
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
1415
+ result = 'Waiting'
1416
+ while result != "Completed"
1417
+ sleep 3
1418
+ output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true, zuora_track_id: z_track_id) do |xml|
1419
+ xml['ns1'].query do
1420
+ xml['ns1'].queryString "SELECT Id, CreatedById, CreatedDate, Encrypted, FileId, Format, Name, Query, Size, Status, StatusReason, UpdatedById, UpdatedDate, Zip From Export where Id = '#{id}'"
1323
1421
  end
1324
1422
  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
1423
+ result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
1424
+ end
1375
1425
 
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
1426
+ file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
1427
+ export_file = get_file(:url => self.fileURL(file_id))
1428
+ export_file_path = export_file.path
1429
+
1430
+ if extract && zip
1431
+ require "zip"
1432
+ new_path = export_file_path.partition('.zip').first
1433
+ zipped = Zip::File.open(export_file_path)
1434
+ file_handle = zipped.entries.first
1435
+ file_handle.extract(new_path)
1436
+ File.delete(export_file_path)
1437
+ return new_path
1438
+ else
1439
+ return export_file_path
1440
+ end
1441
+ rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
1442
+ if !(tries -= 1).zero?
1443
+ Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
1444
+ self.new_session
1445
+ retry
1446
+ end
1447
+ raise ex
1384
1448
 
1385
- rescue Errno::ECONNRESET => ex
1386
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
1387
- retry
1388
- else
1389
- raise ex
1390
- end
1449
+ rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
1450
+ if !(tries -= 1).zero?
1451
+ Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
1452
+ sleep(self.timeout_sleep)
1453
+ retry
1454
+ end
1455
+ raise ex
1391
1456
 
1392
- rescue ZuoraAPI::Exceptions::BadEntityError => ex
1393
- raise ex
1457
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
1458
+ if !(tries -= 1).zero?
1459
+ Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
1460
+ sleep(self.timeout_sleep)
1461
+ retry
1394
1462
  end
1463
+ raise ex
1395
1464
  end
1396
1465
 
1397
1466
  def query(query, parse = false)
1398
- output_xml, input_xml = self.soap_call({:debug => false, :timeout_retry => true}) do |xml|
1467
+ output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true) do |xml|
1399
1468
  xml['ns1'].query do
1400
1469
  xml['ns1'].queryString query
1401
1470
  end