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