zuora_api 1.8.2 → 1.8.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 20af4f6a1a2d5f135800a45899d3ba43b2de122167125289566db95d2ba436fb
4
- data.tar.gz: 5eb24a5103ae70624155431cbf897f36c1ccb82c4b089220132ec16fe816da2b
3
+ metadata.gz: 44a650019155981d2075062e2d4de0d8feff5c5e89b7d3ba9e4540f81de803dc
4
+ data.tar.gz: 6db8be3cfaa2905300c7b3d91b7b7d4f8b5aa5eb8306d24116dac7f1035dd3b0
5
5
  SHA512:
6
- metadata.gz: 0742d058bdf5b53dae3273c4d5d6c9c45a5644b47bc71445cf50c59d0bd2bf33007536a7bdeac8ddd27b4a62119ef83e590f50efb157a982c0ce8d7567222443
7
- data.tar.gz: 6ced43d141d096b80cad77c641c171e6088af0f4ac2254021b2a61e086708a13bc77a6f2ce043cb9d94d72a72987edc7986ed7301ed6f4e7a8a33161714565cc
6
+ metadata.gz: 704fd52ba264db7e1937f6307d0c761d72f615495d15097b1e5ceb14bc31b3b07b8b71ef99b87d74c95706399a822008bf654e06e6e4f8e4e6637187ab993446
7
+ data.tar.gz: 0be344718465df6d0bbb69d7fca93263c9bcc299a20ad9a70498f9d56e09eb5b708d9d474984c42f183220eff62ba145b486fbae5be73202e7c7a210b740c9a5
data/.gitignore CHANGED
@@ -7,4 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
- .idea
10
+ .idea
11
+ .DS_Store
@@ -1,4 +1,4 @@
1
- image: ruby:2.6
1
+ image: ruby:2.7
2
2
  stages:
3
3
  - test
4
4
  - deploy
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
-
2
+ ruby "2.7.2"
3
3
  # Specify your gem's dependencies in zuora.gemspec
4
4
  gemspec
@@ -0,0 +1,12 @@
1
+ ---
2
+ apiVersion: backstage.io/v1alpha1
3
+ kind: Component
4
+ metadata:
5
+ annotations:
6
+ backstage.io/techdocs-ref: "gitlab:https://gitlab.zeta.tools/extension-products/shared-libraries/zuora-gem.git"
7
+ description: "Zuora API Rails Gem"
8
+ name: Zuora-Gem
9
+ spec:
10
+ lifecycle: production
11
+ owner: connect@zuora.com
12
+ type: library
@@ -0,0 +1,147 @@
1
+ # Zuora Gem
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/zuora_api.svg)](https://badge.fury.io/rb/zuora_api) [![coverage report](https://gitlab.0.ecc.auw2.zuora/extension-products/shared-libraries/zuora-gem/badges/master/coverage.svg)](https://gitlab.0.ecc.auw2.zuora/extension-products/shared-libraries/zuora-gem/commits/master)
4
+
5
+ ## Installation
6
+ Add this line to your application's Gemfile:
7
+
8
+ ```ruby
9
+ gem 'zuora_api'
10
+ ```
11
+ Then execute `bundle install` in your terminal
12
+
13
+ ## Usage
14
+
15
+ ### Zuora Login Object
16
+ In order to make API calls a Zuora Login object must be created
17
+
18
+ ```ruby
19
+ zuora_client = ZuoraAPI::Login.new(username: "username", password: "password", url: "url")
20
+ ```
21
+
22
+ | Name | Type | Description | Example |
23
+ | ------------------- | ----------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | username | `Attribute` | Username to the Zuora environment | `zuora_client.username = "username"` |
25
+ | password | `Attribute` | password to the Zuora environment | `zuora_client.password = "Password"` |
26
+ | url | `Attribute` | Endpoint to the Zuora tenant | `zuora_client.url = "www.zuora.com"` |
27
+ | wsdl_number | `Attribute` | WSDL number of the zuora login | `wsdl = zuora_client.wsdl_number` |
28
+ | status | `Attribute` | Status of the login | `zuora_client.status` |
29
+ | current_session | `Attribute` | Current session for the login | `zuora_client.current_session` |
30
+ | environment | `Attribute` | environment of the login | `zuora_client.environment` |
31
+ | errors | `Attribute` | Any errors that the login has based on the login call | `zuora_client.errors` |
32
+ | current_error | `Attribute` | Current error from the new_session call | `zuora_client.current_error` |
33
+ | user_info | `Attribute` | Information related to the login | `zuora_client.user_info` |
34
+ | tenant_id | `Attribute` | Tenant ID the login is associated to | `zuora_client.tenant_id` |
35
+ | tenant_name | `Attribute` | Tenant Name of tenant the login is associated to | `zuora_client.tenant_name` |
36
+ | entity_id | `Attribute` | Current entity the login session is associated to | `zuora_client.entity_id` |
37
+ | rest_call | `Method` | Executes a REST call | `zuora_client.rest_call()` |
38
+ | soap_call | `Method` | Executes a SOAP call | `output_xml, input_xml = zuora_client.soap_call() do `|xml, args|` xml['ns1'].query do xml['ns1'].queryString "select id, name from account" end end` |
39
+ | query | `Method` | Executes a query call | `zuora_client.query("select id, name from account")` |
40
+ | getDataSourceExport | `Method` | Pulls a data source export with the given query and returns the file location | `zuora_client.getDataSourceExport("select id, name from account")` |
41
+ | describe_call | `Method` | Performs the describe call against the Zuora tenant for all objects or a specific object | `response = zuora_client.describe_call("Account")` |
42
+ | createJournalRun | `Method` | Creates a Journal Run | `zuora_client.createJournalRun(call)` |
43
+ | checkJRStatus | `Method` | Checks the status of a journal run | `zuora_client.checkJRStatus(journal_run_id)` |
44
+ | update_environment | `Method` | Sets the login's environment based on the url | `zuora_client.update_environment` |
45
+ | aqua_endpoint | `Method` | Returns the AQuA endpoint for the login based off the environment | `zuora_client.aqua_endpoint` |
46
+ | rest_endpoint | `Method` | Returns the REST endpoint for the login based off the environment | `zuora_client.rest_endpoint` |
47
+ | fileURL | `Method` | Returns the URL for files | `zuora_client.fileURL` |
48
+ | dateFormat | `Method` | Returns the data format syntax based on the wsdl_number | `zuora_client.dateFormat` |
49
+ | new_session | `Method` | Create a new session | `zuora_client.new_session` |
50
+ | get_session | `Method` | Returns the current session | `zuora_client.get_session`|
51
+
52
+ ## Rest Call
53
+ ```ruby
54
+ zuora_client.rest_call(method: :get, body: {}, url: zuora_client.rest_endpoint("catalog/products?pageSize=4"))
55
+ ```
56
+
57
+ ### Soap Call
58
+ Returns both output and input XML
59
+
60
+ ```ruby
61
+ zuora_client.soap_call(ns1: 'ns1', ns2: 'ns2', batch_size: nil, single_transaction: false)
62
+ ```
63
+
64
+ Example Call
65
+
66
+ ```ruby
67
+ output_xml, input_xml = zuora_client.soap_call() do |xml, args|
68
+ xml['ns1'].query do
69
+ xml['ns1'].queryString "select id, name from account"
70
+ end
71
+ end
72
+ ```
73
+ ### Query
74
+ ```ruby
75
+ zuora_client.query("select id from account")
76
+ ```
77
+ ### Data Export
78
+ Returns the file location of the data source export after downloading from Zuora
79
+
80
+ ```ruby
81
+ zuora_client.getDataSourceExport("select id from account")
82
+ ```
83
+
84
+ | Name | Description | Default | Example |
85
+ | --------- | ---------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------- |
86
+ | query | The query to execute | `N/A` | `zuora_client.getDataSourceExport("select id from account")` |
87
+ | zip | Indicates if the data source export should be a zip | `true` | `zuora_client.getDataSourceExport("select id from account", zip: false)` |
88
+ | extract | Indicates if the data source export should be extracted if it is a zip | `true` | `zuora_client.getDataSourceExport("select id from account", extract: false)` |
89
+ | encrypted | Indicates if the data source export should be encrypted | `false` | `zuora_client.getDataSourceExport("select id from account", encrypted: true)` |
90
+
91
+ ### Describe Call
92
+ This returns all available objects from the describe call as a hash. This response can be accessed by using response["Account"] to retrieve all related data about that object.
93
+
94
+ ```ruby
95
+ response = zuora_client.describe_call("Account")
96
+ ```
97
+ This returns all information and fields related to that object model as a hash.
98
+
99
+ ```ruby
100
+ response = zuora_client.describe_call()
101
+ ```
102
+
103
+ ### Journal Run
104
+ ```ruby
105
+ zuora_client.createJournalRun(call)
106
+ ```
107
+
108
+ ## Insights API
109
+
110
+ In order to make API calls a Zuora Login object must be created by running:
111
+
112
+ ```ruby
113
+ insightsapi = InsightsAPI::Login.new(api_token: "api token", url: "Nw1.api.insights.zuora.com/api/")
114
+ ```
115
+
116
+ Note that the login will default to the insights production url.
117
+
118
+ ```ruby
119
+ Date format: "YYYY-MM-DDT00:00:00Z"
120
+ ```
121
+
122
+ ### Uploading Data into Insights
123
+ ```ruby
124
+ insightsapi.upload_into_insights(dataSourceName, recordType, batchDate, filePath)
125
+ ```
126
+ dataSourceName: What system the data is coming from.
127
+ recordType: The type of records ie: "EVENTS, ATTRIBUTES, and METRICS"
128
+ batachDate: The date the data applies to.
129
+
130
+ ### Describing Insights Data
131
+ ```ruby
132
+ insightsapi.describe(type: "ACCOUNT/USER", object: "ATTRIBUTES/EVENTS/SEGMENTS/METRICS")
133
+ ```
134
+ Returns json payload describing attributes, events, metrics for each Account or User.
135
+
136
+ ### Downloading Data from Insights
137
+ ```ruby
138
+ insightsapi.data_export_insights(objecttype, segmentuuid, startDate: nil, endDate: nil, tries: 30)
139
+ ```
140
+ ```ruby
141
+ insightsapi.data_export_insights_file(objecttype, segmentuuid, startDate: nil, endDate: nil, tries: 30)
142
+ ```
143
+ Both do the same thing except one returns a url(data_export_insights) to download the file yourself and the other returns an actual Ruby temporary file(data_export_insights_file).
144
+
145
+ objectype: "ACCOUNT/USER"
146
+
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.
@@ -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
@@ -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 = [
@@ -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
@@ -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 timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
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 timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
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,120 @@ 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
1397
+ tries ||= 3
1398
+ request = Nokogiri::XML::Builder.new do |xml|
1399
+ 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
1400
+ xml['SOAP-ENV'].Header do
1401
+ xml['ns1'].SessionHeader do
1402
+ xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1287
1403
  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
1404
+ end
1405
+ xml['SOAP-ENV'].Body do
1406
+ xml['ns1'].create do
1407
+ xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
1408
+ xml['ns2'].Format 'csv'
1409
+ xml['ns2'].Zip zip
1410
+ xml['ns2'].Name 'googman'
1411
+ xml['ns2'].Query query
1412
+ xml['ns2'].Encrypted encrypted
1297
1413
  end
1298
1414
  end
1299
1415
  end
1300
1416
  end
1417
+ end
1301
1418
 
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)
1419
+ response_query = HTTParty.post(self.url, body: request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8", "Z-Track-Id" => z_track_id}, :timeout => 130)
1303
1420
 
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"
1421
+ output_xml = Nokogiri::XML(response_query.body)
1422
+ raise_errors(type: :SOAP, body: output_xml, response: response_query) if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1306
1423
 
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
1424
+ # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1425
+
1426
+ id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
1310
1427
 
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
1428
+ confirmRequest = Nokogiri::XML::Builder.new do |xml|
1429
+ 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
1430
+ xml['SOAP-ENV'].Header do
1431
+ xml['ns1'].SessionHeader do
1432
+ xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1317
1433
  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
1434
+ end
1435
+ xml['SOAP-ENV'].Body do
1436
+ xml['ns1'].query do
1437
+ xml['ns1'].queryString "SELECT Id, CreatedById, CreatedDate, Encrypted, FileId, Format, Name, Query, Size, Status, StatusReason, UpdatedById, UpdatedDate, Zip From Export where Id = '#{id}'"
1322
1438
  end
1323
1439
  end
1324
1440
  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)
1441
+ end
1442
+ result = 'Waiting'
1330
1443
 
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
1444
+ while result != "Completed"
1445
+ sleep 3
1446
+ response_query = HTTParty.post(self.url, body: confirmRequest.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8", "Z-Track-Id" => z_track_id}, :timeout => 130)
1334
1447
 
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
1448
+ output_xml = Nokogiri::XML(response_query.body)
1449
+ result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
1450
+ status_code = response_query.code if response_query
1363
1451
 
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
1452
+ raise_errors(type: :SOAP, body: output_xml, response: response_query) if result.blank? || result == "Failed"
1453
+ # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if result.blank? || result == "Failed"
1454
+ end
1372
1455
 
1373
- rescue *ZUORA_API_ERRORS => ex
1374
- raise ex
1456
+ file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
1457
+ export_file = get_file(:url => self.fileURL(file_id))
1458
+ export_file_path = export_file.path
1459
+ Rails.logger.debug("=====> Export path #{export_file.path}")
1460
+
1461
+ if extract && zip
1462
+ require "zip"
1463
+ new_path = export_file_path.partition('.zip').first
1464
+ zipped = Zip::File.open(export_file_path)
1465
+ file_handle = zipped.entries.first
1466
+ file_handle.extract(new_path)
1467
+ File.delete(export_file_path)
1468
+ return new_path
1469
+ else
1470
+ return export_file_path
1471
+ end
1472
+ rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
1473
+ if !(tries -= 1).zero?
1474
+ Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
1475
+ self.new_session
1476
+ retry
1477
+ end
1478
+ raise ex
1375
1479
 
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
1480
+ rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
1481
+ if !(tries -= 1).zero?
1482
+ Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
1483
+ sleep 10
1484
+ retry
1485
+ end
1486
+ raise ex
1384
1487
 
1385
- rescue Errno::ECONNRESET => ex
1386
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
1387
- retry
1388
- else
1389
- raise ex
1390
- end
1488
+ rescue *ZUORA_API_ERRORS => ex
1489
+ raise ex
1391
1490
 
1392
- rescue ZuoraAPI::Exceptions::BadEntityError => ex
1393
- raise ex
1491
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
1492
+ if !(tries -= 1).zero?
1493
+ Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
1494
+ sleep 5
1495
+ retry
1394
1496
  end
1497
+ raise ex
1498
+
1499
+ rescue ZuoraAPI::Exceptions::BadEntityError => ex
1500
+ raise ex
1395
1501
  end
1396
1502
 
1397
1503
  def query(query, parse = false)
1398
- output_xml, input_xml = self.soap_call({:debug => false, :timeout_retry => true}) do |xml|
1504
+ output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true) do |xml|
1399
1505
  xml['ns1'].query do
1400
1506
  xml['ns1'].queryString query
1401
1507
  end