zuora_api 1.8.2 → 1.8.21

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: 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