zuora_api 1.7.06 → 1.7.7a

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: f857a810633aad60e80293f6400b9fa8fab38bff15537589e4ffe0ef2aca808d
4
- data.tar.gz: d4512de5de2f7eb888dc32ca7d283fee4f366ad7a96c8dc619bd8ebd83b8f55d
3
+ metadata.gz: 2697794114e89b4d76001a4746c1148229aeada880a18ab2cd3a97673549d2c6
4
+ data.tar.gz: 7a66c8c6b0d604bc9d1229424ecca8a557c6c36a84304049970988cbf7e6c771
5
5
  SHA512:
6
- metadata.gz: 5d2a72796a2b442627768876ac6954b10aff3be6ea61b11375f4bd2d305b970c5887d26dbf0962b8876bb7575cfcf9a3c49e56d0a8ae0d6fe71ed4078fe04bd6
7
- data.tar.gz: c957560ae8e70276ff8c9fd6d2e9777af145d837bbb93ee1114f36ec7946ab59033820113b93872c4b42855be40c86e04a979255f839908facc9fb4ea4faa350
6
+ metadata.gz: d7a8ae407cd8d86a1249b404ef27be7ee13e13e8f620ec7cda03d84db3c17c077ead6cbf8f90d6bb8be66fb5594d90adf7184e707a7ac1f1639b84434868fe4f
7
+ data.tar.gz: b9732ae7b0397cc983e87e3459f80fa013c81170f2fd1fec15256a51e79e6722eeac58dcda457eb7d4ec323cbe5efd869094f3abbc29124e737d95689d4150a1
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .idea
@@ -1,4 +1,4 @@
1
- image: ruby:2.6
1
+ image: ruby:2.7
2
2
  stages:
3
3
  - setup
4
4
  - test
@@ -35,6 +35,7 @@ rspec-testing:
35
35
  script:
36
36
  - bundle install
37
37
  - rspec
38
+ coverage: '/\(\d+.\d+\%\) covered/'
38
39
 
39
40
  rubygems-deploy:
40
41
  stage: deploy
@@ -48,8 +49,8 @@ rubygems-deploy:
48
49
  - if [[ "staging" == $CI_BUILD_REF_SLUG ]];then export VERSION=`git describe --match "[0-9]*\.[0-9]*\.[0-9]*[a-z]" --abbrev=0 --tags HEAD`; fi
49
50
  - if [[ "master" == $CI_BUILD_REF_SLUG ]];then export VERSION=`git describe --exclude "[0-9]*\.[0-9]*\.[0-9]*[a-z]" --abbrev=0 --tags HEAD`; fi
50
51
  - echo $VERSION
51
- - sed -i "s/0.0.1/$VERSION/" /Connect/zuora-gem/lib/zuora_api/version.rb
52
- - git add /Connect/zuora-gem/lib/zuora_api/version.rb
52
+ - sed -i "s/0.0.1/$VERSION/" lib/zuora_api/version.rb
53
+ - git add lib/zuora_api/version.rb
53
54
  - git config --global user.email "connect@zuora.com"
54
55
  - git config --global user.name "Connect Automation"
55
56
  - git commit -m "Automated Version Update $VERSION"
@@ -1,6 +1,23 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [1.7.07] - 2018-9-10
5
+ ### Changed
6
+ - Cookie name for service endpoint integration
7
+
8
+ ## [1.7.06] - 2018-8-25
9
+ ### Added
10
+ - Retry for 502/503
11
+ - Standard exception for 504
12
+
13
+ ## [1.7.05] - 2018-8-25
14
+ ### Added
15
+ - Added support for oauth token forbidden
16
+
17
+ ## [1.7.02] - 2018-8-23
18
+ ### Added
19
+ - Added mulit part support for rest call
20
+
4
21
  ## [1.7.01] - 2018-8-06
5
22
  ### Changed
6
23
  - Changed library used to determine host
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
-
2
+ ruby "2.7.1"
3
3
  # Specify your gem's dependencies in zuora.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Zuora Gem
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/zuora_api.svg)](https://badge.fury.io/rb/zuora_api) [![coverage report](https://gitlab.zuora.com/Connect/zuora-gem/badges/master/coverage.svg)](https://gitlab.zuora.com/Connect/zuora-gem/commits/master)
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
4
 
5
5
  ## Installation
6
6
  Add this line to your application's Gemfile:
@@ -190,12 +190,11 @@ module InsightsAPI
190
190
  end
191
191
 
192
192
  rescue => ex
193
- if !(tries -= 1).zero?
194
- sleep 3
195
- retry
196
- else
197
- raise ex
198
- end
193
+ raise ex if tries.zero?
194
+
195
+ tries -= 1
196
+ sleep 3
197
+ retry
199
198
  else
200
199
  return temp_file
201
200
  end
@@ -1,14 +1,38 @@
1
1
  module ZuoraAPI
2
2
  module Exceptions
3
- class Error < StandardError; end
3
+ class Error < StandardError;
4
+ def parse_message(message)
5
+ case message
6
+ when /^Payment status should be Processed. Invalid payment is P-\d*./
7
+ @message = "Payment status should be Processed."
8
+ when /^Adjustment cannot be created for invoice(.*) with a zero balance./
9
+ @message = "Adjustment cannot be created for invoice with a zero balance."
10
+ when /^The balance of all the invoice items and tax items is 0. No write-off is needed for the invoice .*./
11
+ @message = "The balance of all the invoice items and tax items is 0. No write-off is needed for the invoice."
12
+ when /^Json input does not match schema. Error(s): string ".*" is too long .*/
13
+ @message = "Json input does not match schema. Error(s): String is too long."
14
+ when /^Query failed \(#[\d\w_]*\): line [0-9]+:[0-9]+: (.*)$/
15
+ @message = "Query failed: #{$1}"
16
+ when /^Query failed \(#[\d\w_]*\): (.*)$/
17
+ @message = "Query failed: #{$1}"
18
+ when /^Could not find [\w\d]{32}.$/
19
+ @message = "Could not find object."
20
+ when /^Subscription [\w\d]{32} is in expired status. It is not supported to generate billing documents for expired subscriptions./
21
+ @message = "Subscription is in expired status. It is not supported to generate billing documents for expired subscriptions."
22
+ else
23
+ @message = message
24
+ end
25
+ end
26
+ end
27
+ class FileDownloadError < StandardError; end
4
28
  class AuthorizationNotPerformed < Error; end
5
29
  class ZuoraAPISessionError < Error
6
30
  attr_reader :code, :response
7
31
  attr_writer :default_message
8
32
 
9
- def initialize(message = nil,response=nil)
10
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
11
- @message = message
33
+ def initialize(message = nil,response=nil, errors = [], successes = [])
34
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
35
+ @message = parse_message(message)
12
36
  @response = response
13
37
  @default_message = "Error with Zuora Session."
14
38
  end
@@ -16,6 +40,19 @@ module ZuoraAPI
16
40
  def to_s
17
41
  @message || @default_message
18
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
19
56
  end
20
57
 
21
58
  class BadEntityError < Error
@@ -23,8 +60,8 @@ module ZuoraAPI
23
60
  attr_writer :default_message
24
61
 
25
62
  def initialize(message = nil,response=nil, errors = [], successes = [], *args)
26
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
27
- @message = message
63
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
64
+ @message = parse_message(message)
28
65
  @response = response
29
66
  @default_message = "Error with Zuora Entity"
30
67
  @errors = errors
@@ -41,8 +78,8 @@ module ZuoraAPI
41
78
  attr_writer :default_message
42
79
 
43
80
  def initialize(message = nil,response=nil, errors = [], successes = [], *args)
44
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
45
- @message = message
81
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
82
+ @message = parse_message(message)
46
83
  @response = response
47
84
  @default_message = "Error communicating with Zuora."
48
85
  @errors = errors
@@ -54,13 +91,31 @@ module ZuoraAPI
54
91
  end
55
92
  end
56
93
 
94
+ class ZuoraAPIInternalServerError < Error
95
+ attr_reader :code, :response, :errors, :successes
96
+ attr_writer :default_message
97
+
98
+ def initialize(message = nil,response = nil, errors = [], successes = [], *args)
99
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
100
+ @message = parse_message(message)
101
+ @response = response
102
+ @default_message = "Zuora Internal Server Error."
103
+ @errors = errors
104
+ @successes = successes
105
+ end
106
+
107
+ def to_s
108
+ @message || @default_message
109
+ end
110
+ end
111
+
57
112
  class ZuoraAPIRequestLimit < Error
58
113
  attr_reader :code, :response
59
114
  attr_writer :default_message
60
115
 
61
- def initialize(message = nil,response=nil, *args)
62
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
63
- @message = message
116
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
117
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
118
+ @message = parse_message(message)
64
119
  @response = response
65
120
  @default_message = "Your request limit has been exceeded for zuora."
66
121
  end
@@ -70,13 +125,29 @@ module ZuoraAPI
70
125
  end
71
126
  end
72
127
 
128
+ class ZuoraAPIUnkownError < Error
129
+ attr_reader :code, :response
130
+ attr_writer :default_message
131
+
132
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
133
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
134
+ @message = parse_message(message)
135
+ @response = response
136
+ @default_message = "An unkown error occured. Workflow is not responsible. Please contact Support."
137
+ end
138
+
139
+ def to_s
140
+ @message || @default_message
141
+ end
142
+ end
143
+
73
144
  class ZuoraAPILockCompetition < Error
74
145
  attr_reader :code, :response
75
146
  attr_writer :default_message
76
147
 
77
- def initialize(message = nil,response=nil, *args)
78
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
79
- @message = message
148
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
149
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
150
+ @message = parse_message(message)
80
151
  @response = response
81
152
  @default_message = "Operation failed due to lock competition. Please retry"
82
153
  end
@@ -90,9 +161,9 @@ module ZuoraAPI
90
161
  attr_reader :code, :response
91
162
  attr_writer :default_message
92
163
 
93
- def initialize(message = nil,response=nil, *args)
94
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
95
- @message = message
164
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
165
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
166
+ @message = parse_message(message)
96
167
  @response = response
97
168
  @default_message = "Operation failed due to lock competition. Please retry"
98
169
  end
@@ -102,15 +173,32 @@ module ZuoraAPI
102
173
  end
103
174
  end
104
175
 
105
- class ZuoraAPITemporaryError < Error
176
+ class ZuoraUnexpectedError < Error
106
177
  attr_reader :code, :response
107
178
  attr_writer :default_message
108
179
 
109
- def initialize(message = nil,response=nil, *args)
110
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
111
- @message = message
180
+ def initialize(message = nil, response=nil, errors = [], successes = [], *args)
181
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
182
+ @message = parse_message(message)
183
+ @response = response
184
+ @default_message = "An unexpected error occurred"
185
+ end
186
+
187
+ def to_s
188
+ @message || @default_message
189
+ end
190
+ end
191
+
192
+ class ZuoraAPITemporaryError < Error
193
+ attr_reader :code, :response, :errors
194
+ attr_writer :default_message
195
+
196
+ def initialize(message = nil, response = nil, errors = [], successes = [], *args)
197
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
198
+ @message = parse_message(message)
112
199
  @response = response
113
200
  @default_message = "There is a temporary error with zuora system."
201
+ @errors = errors
114
202
  end
115
203
 
116
204
  def to_s
@@ -122,9 +210,9 @@ module ZuoraAPI
122
210
  attr_reader :code, :response
123
211
  attr_writer :default_message
124
212
 
125
- def initialize(message = nil,response=nil, *args)
126
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
127
- @message = message
213
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
214
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
215
+ @message = parse_message(message)
128
216
  @response = response
129
217
  @default_message = "Authentication type is not supported by this Login"
130
218
  end
@@ -138,8 +226,8 @@ module ZuoraAPI
138
226
  attr_reader :code, :response
139
227
  attr_writer :default_message
140
228
 
141
- def initialize(message = nil,response=nil, *args)
142
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
229
+ def initialize(message = nil,response=nil, errors = [], successes = [], *args)
230
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
143
231
  @message = message
144
232
  @response = response
145
233
  @default_message = "Authentication type is not supported by this Login"
@@ -151,13 +239,14 @@ module ZuoraAPI
151
239
  end
152
240
 
153
241
  class ZuoraAPIReadTimeout < Net::ReadTimeout
154
- attr_reader :code, :response
242
+ attr_reader :code, :response, :request
155
243
  attr_writer :default_message
156
244
 
157
- def initialize(message = nil,response=nil, *args)
158
- @code = response.present? && response.class.to_s == "HTTParty::Response" ? response.code : nil
245
+ def initialize(message = nil, response = nil, request = nil, errors = [], successes = [], *args)
246
+ @code = response.class.to_s == "HTTParty::Response" ? response.code : nil
159
247
  @message = message
160
248
  @response = response
249
+ @request = request
161
250
  @default_message = "Authentication type is not supported by this Login"
162
251
  end
163
252
 
@@ -5,7 +5,7 @@ require 'zuora_api/exceptions'
5
5
 
6
6
  module ZuoraAPI
7
7
  class Login
8
- ENVIRONMENTS = [SANDBOX = 'Sandbox', PRODUCTION = 'Production', PREFORMANCE = 'Preformance', SERVICES = 'Services', UNKNOWN = 'Unknown', STAGING = 'Staging' ]
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_Endpoint = '96.0'
11
11
  XML_SAVE_OPTIONS = Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
@@ -16,7 +16,8 @@ module ZuoraAPI
16
16
  Errno::ECONNREFUSED,
17
17
  SocketError,
18
18
  Errno::EHOSTUNREACH,
19
- Errno::EADDRNOTAVAIL
19
+ Errno::EADDRNOTAVAIL,
20
+ Errno::ETIMEDOUT,
20
21
  ].freeze
21
22
 
22
23
  CONNECTION_READ_EXCEPTIONS = [
@@ -30,7 +31,17 @@ module ZuoraAPI
30
31
  ZuoraAPI::Exceptions::ZuoraAPIRequestLimit,
31
32
  ZuoraAPI::Exceptions::ZuoraAPILockCompetition,
32
33
  ZuoraAPI::Exceptions::ZuoraAPITemporaryError,
33
- ZuoraAPI::Exceptions::ZuoraDataIntegrity
34
+ ZuoraAPI::Exceptions::ZuoraDataIntegrity,
35
+ ZuoraAPI::Exceptions::ZuoraAPIInternalServerError,
36
+ ZuoraAPI::Exceptions::ZuoraUnexpectedError,
37
+ ZuoraAPI::Exceptions::ZuoraAPIUnkownError
38
+ ].freeze
39
+
40
+ ZUORA_SERVER_ERRORS = [
41
+ ZuoraAPI::Exceptions::ZuoraAPIInternalServerError,
42
+ ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout,
43
+ ZuoraAPI::Exceptions::ZuoraAPIReadTimeout,
44
+ ZuoraAPI::Exceptions::ZuoraUnexpectedError
34
45
  ].freeze
35
46
 
36
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
@@ -39,10 +50,10 @@ module ZuoraAPI
39
50
  raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
40
51
  # raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
41
52
  self.hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
42
- if !/apps\/services\/a\/\d{2}\.\d$/.match(url.strip)
53
+ if !/apps\/services\/a\/\d+\.\d$/.match(url.strip)
43
54
  self.url = "https://#{hostname}/apps/services/a/#{MIN_Endpoint}"
44
- elsif MIN_Endpoint.to_f > url.scan(/(\d{2}\.\d)$/).dig(0,0).to_f
45
- self.url = url.gsub(/(\d{2}\.\d)$/, 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)
46
57
  else
47
58
  self.url = url
48
59
  end
@@ -61,27 +72,12 @@ module ZuoraAPI
61
72
 
62
73
  def get_identity(cookies)
63
74
  zsession = cookies["ZSession"]
64
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
65
75
  begin
66
76
  if !zsession.blank?
67
77
  response = HTTParty.get("https://#{self.hostname}/apps/v1/identity", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
68
78
  output_json = JSON.parse(response.body)
69
- elsif zconnect_accesstoken.present?
70
- begin
71
- code = zconnect_accesstoken.split("#!").last
72
- encrypted_token, tenant_id = Base64.decode64(code).split(":")
73
- body = {'token' => encrypted_token}.to_json
74
- rescue => ex
75
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Invalid ZConnect Cookie")
76
- end
77
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/identity", :body => body, :headers => { 'Content-Type' => 'application/json' })
78
- output_json = JSON.parse(response.body)
79
79
  else
80
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
81
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
82
- else
83
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
84
- end
80
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
85
81
  end
86
82
  rescue JSON::ParserError => ex
87
83
  output_json = {}
@@ -92,20 +88,12 @@ module ZuoraAPI
92
88
 
93
89
  def get_full_nav(cookies)
94
90
  zsession = cookies["ZSession"]
95
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
96
91
  begin
97
92
  if zsession.present?
98
93
  response = HTTParty.get("https://#{self.hostname}/apps/v1/navigation", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
99
94
  output_json = JSON.parse(response.body)
100
- elsif zconnect_accesstoken.present?
101
- response = HTTParty.get("https://#{self.hostname}/apps/zconnectsession/navigation", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}",'Content-Type' => 'application/json'})
102
- output_json = JSON.parse(response.body)
103
95
  else
104
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
105
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
106
- else
107
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
108
- end
96
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
109
97
  end
110
98
  rescue JSON::ParserError => ex
111
99
  output_json = {}
@@ -116,20 +104,12 @@ module ZuoraAPI
116
104
 
117
105
  def set_nav(state, cookies)
118
106
  zsession = cookies["ZSession"]
119
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
120
107
  begin
121
108
  if !zsession.blank?
122
109
  response = HTTParty.put("https://#{self.hostname}/apps/v1/preference/navigation", :body => state.to_json, :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
123
110
  output_json = JSON.parse(response.body)
124
- elsif !zconnect_accesstoken.blank?
125
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/navigationstate", :body => state.to_json, :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
126
- output_json = JSON.parse(response.body)
127
111
  else
128
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
129
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
130
- else
131
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
132
- end
112
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
133
113
  end
134
114
  rescue JSON::ParserError => ex
135
115
  output_json = {}
@@ -140,20 +120,12 @@ module ZuoraAPI
140
120
 
141
121
  def refresh_nav(cookies)
142
122
  zsession = cookies["ZSession"]
143
- zconnect_accesstoken = get_zconnect_accesstoken(cookies)
144
123
  begin
145
124
  if !zsession.blank?
146
125
  response = HTTParty.post("https://#{self.hostname}/apps/v1/navigation/fetch", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
147
126
  output_json = JSON.parse(response.body)
148
- elsif !zconnect_accesstoken.blank?
149
- response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/refresh-navbarcache", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
150
- output_json = JSON.parse(response.body)
151
127
  else
152
- if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
153
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
154
- else
155
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
156
- end
128
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
157
129
  end
158
130
  rescue JSON::ParserError => ex
159
131
  output_json = {}
@@ -162,27 +134,21 @@ module ZuoraAPI
162
134
  return output_json
163
135
  end
164
136
 
165
- def get_zconnect_accesstoken(cookies)
166
- accesstoken = nil
167
- self.update_zconnect_provider
168
- if !cookies[self.zconnect_provider].nil? && !cookies[self.zconnect_provider].empty?
169
- accesstoken = cookies[self.zconnect_provider]
170
- end
171
- return accesstoken
172
- end
173
-
174
137
  def reporting_url(path)
175
138
  map = {"US" => {"Sandbox" => "https://zconnectsandbox.zuora.com/api/rest/v1/",
176
139
  "Production" => "https://zconnect.zuora.com/api/rest/v1/",
177
- "Services"=> ""},
140
+ "Test" => "https://reporting-sbx.zan.0001.sbx.auw2.zuora.com/api/rest/v1/",
141
+ "Staging" => "https://reporting-stg11.zan.svc.auw2.zuora.com/api/rest/v1/",
142
+ "Performance" => "https://zconnectpt1.zuora.com/api/rest/v1/",
143
+ "Services" => "https://reporting-svc08.svc.auw2.zuora.com/api/rest/v1/"},
178
144
  "EU" => {"Sandbox" => "https://zconnect.sandbox.eu.zuora.com/api/rest/v1/",
179
145
  "Production" => "https://zconnect.eu.zuora.com/api/rest/v1/",
180
- "Services"=> ""},
146
+ "Services"=> "https://reporting-sbx0000.sbx.aec1.zuora.com/api/rest/v1/"},
181
147
  "NA" => {"Sandbox" => "https://zconnect.sandbox.na.zuora.com/api/rest/v1/",
182
148
  "Production" => "https://zconnect.na.zuora.com/api/rest/v1/",
183
149
  "Services"=> ""}
184
150
  }
185
- return map[zuora_client.region][zuora_client.environment].insert(-1, path)
151
+ return map[self.region][self.environment].insert(-1, path)
186
152
  end
187
153
 
188
154
  # There are two ways to call this method. The first way is best.
@@ -232,7 +198,7 @@ module ZuoraAPI
232
198
  end
233
199
 
234
200
  def self.environments
235
- %w(Sandbox Production Services Performance Staging)
201
+ %w(Sandbox Production Services Performance Staging Test)
236
202
  end
237
203
 
238
204
  def self.regions
@@ -244,11 +210,13 @@ module ZuoraAPI
244
210
  "Production" => "https://www.zuora.com/apps/services/a/",
245
211
  "Performance" => "https://pt1.zuora.com/apps/services/a/",
246
212
  "Services" => "https://services347.zuora.com/apps/services/a/",
247
- "Staging" => "https://staging2.zuora.com/apps/services/a/"},
213
+ "Staging" => "https://staging2.zuora.com/apps/services/a/",
214
+ "Test" => "https://test.zuora.com/apps/services/a/"},
248
215
  "EU" => {"Sandbox" => "https://sandbox.eu.zuora.com/apps/services/a/",
249
216
  "Production" => "https://eu.zuora.com/apps/services/a/",
250
217
  "Performance" => "https://pt1.eu.zuora.com/apps/services/a/",
251
- "Services" => "https://services347.eu.zuora.com/apps/services/a/"},
218
+ "Services" => "https://services347.eu.zuora.com/apps/services/a/",
219
+ "Test" => "https://test.eu.zuora.com/apps/services/a/"},
252
220
  "NA" => {"Sandbox" => "https://sandbox.na.zuora.com/apps/services/a/",
253
221
  "Production" => "https://na.zuora.com/apps/services/a/",
254
222
  "Performance" => "https://pt1.na.zuora.com/apps/services/a/",
@@ -285,15 +253,18 @@ module ZuoraAPI
285
253
 
286
254
  def update_environment
287
255
  if !self.url.blank?
288
- if /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/ === self.hostname
256
+ case self.hostname
257
+ when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
289
258
  self.environment = 'Sandbox'
290
- elsif /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/ === self.hostname
259
+ when /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/
291
260
  self.environment = 'Services'
292
- elsif /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/ === self.hostname
261
+ when /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/
293
262
  self.environment = 'Performance'
294
- elsif /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/ === self.hostname
263
+ when /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/
295
264
  self.environment = 'Staging'
296
- elsif is_prod_env
265
+ when /(?<=\.|\/|^)(test)(?=\.|\/|$)/
266
+ self.environment = 'Test'
267
+ when /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/, /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/
297
268
  self.environment = 'Production'
298
269
  else
299
270
  self.environment = 'Unknown'
@@ -303,25 +274,14 @@ module ZuoraAPI
303
274
  end
304
275
  end
305
276
 
306
- def is_prod_env
307
- is_prod = false
308
- www_or_api = /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/ === self.hostname
309
- host_prefix_match = /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/ === self.hostname
310
- if www_or_api || host_prefix_match
311
- is_prod = true
312
- end
313
- return is_prod
314
- end
315
-
316
277
  def update_zconnect_provider
317
278
  region = update_region
318
279
  environment = update_environment
319
- mappings = {"US" => {"Sandbox" => "ZConnectSbx", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev", "Services" => "ZConnectQA", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Staging" => "ZConnectQA"},
320
- "NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectQANA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
321
- "EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectQAEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU"},
280
+ mappings = {"US" => {"Sandbox" => "ZConnectSbx", "Services" => "ZConnectSvcUS", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Test" => "ZConnectTest", "Staging" => "ZConnectQA", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev"},
281
+ "NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectSvcNA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
282
+ "EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectSvcEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU", "Test" => "ZConnectTest"},
322
283
  "Unknown" => {"Unknown" => "Unknown"}}
323
284
  self.zconnect_provider = mappings[region][environment]
324
- # raise "Can't find ZConnect Provider for #{region} region and #{environment} environment" if self.zconnect_provider.nil?
325
285
  end
326
286
 
327
287
  def aqua_endpoint(url="")
@@ -334,45 +294,35 @@ module ZuoraAPI
334
294
  return "#{url_slash_apps_slash}api/#{url}"
335
295
  end
336
296
 
337
- def rest_endpoint(url="")
297
+ def rest_endpoint(url="", domain=true, prefix='/v1/')
338
298
  update_environment
339
299
  endpoint = url
340
-
300
+ url_postfix = {"US" => ".", "EU" => ".eu.", "NA" => ".na."}[self.region]
301
+
341
302
  case self.environment
303
+ when 'Test'
304
+ endpoint = "https://rest.test#{url_postfix}zuora.com"
342
305
  when 'Sandbox'
343
- case self.region
344
- when 'US'
345
- endpoint = "https://rest.apisandbox.zuora.com/v1/".concat(url)
346
- when 'EU'
347
- endpoint = "https://rest.sandbox.eu.zuora.com/v1/".concat(url)
348
- when 'NA'
349
- endpoint = "https://rest.sandbox.na.zuora.com/v1/".concat(url)
350
- end
306
+ endpoint = "https://rest.sandbox#{url_postfix}zuora.com"
307
+ endpoint = "https://rest.apisandbox.zuora.com" if self.region == "US"
351
308
  when 'Production'
352
- case self.region
353
- when 'US'
354
- endpoint = "https://rest.zuora.com/v1/".concat(url)
355
- when 'EU'
356
- endpoint = "https://rest.eu.zuora.com/v1/".concat(url)
357
- when 'NA'
358
- endpoint = "https://rest.na.zuora.com/v1/".concat(url)
359
- end
309
+ endpoint = "https://rest#{url_postfix}zuora.com"
360
310
  when 'Performance'
361
- endpoint = "https://rest.pt1.zuora.com/v1/".concat(url)
311
+ endpoint = "https://rest.pt1.zuora.com"
362
312
  when 'Services'
363
313
  https = /https:\/\/|http:\/\//.match(self.url)[0]
364
314
  host = self.hostname
365
- endpoint = "#{https}rest#{host}/v1/#{url}"
315
+ endpoint = "#{https}rest#{host}"
366
316
  when 'Staging'
367
- endpoint = "https://rest-staging2.zuora.com/v1/".concat(url)
317
+ endpoint = "https://rest-staging2.zuora.com"
368
318
  when 'Unknown'
369
319
  raise "Environment unknown, returning passed in parameter unaltered"
370
320
  end
371
- return endpoint
321
+ return domain ? endpoint.concat(prefix).concat(url) : prefix.concat(url)
372
322
  end
373
323
 
374
- def rest_domain
375
- return URI(self.rest_endpoint).host
324
+ def rest_domain(endpoint: self.rest_endpoint)
325
+ return URI(endpoint).host
376
326
  end
377
327
 
378
328
  def fileURL(url="")
@@ -383,43 +333,89 @@ module ZuoraAPI
383
333
  return self.wsdl_number > 68 ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'
384
334
  end
385
335
 
386
- def new_session(auth_type: :basic, debug: false)
336
+ def new_session(auth_type: :basic, debug: false, zuora_track_id: nil)
337
+ retries ||= 2
338
+ yield
339
+
340
+ rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
341
+ self.status = 'Invalid'
342
+ self.current_error = ex.message
343
+ raise
344
+ rescue ZuoraAPI::Exceptions::ZuoraAPIError => ex
345
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(ex.message, ex.response)
346
+
347
+ rescue ZuoraAPI::Exceptions::ZuoraAPIInternalServerError => ex
348
+ raise ex if retries.zero?
349
+
350
+ retries -= 1
351
+ sleep(self.timeout_sleep)
352
+ retry
353
+
354
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
355
+ self.log(location: "BasicLogin", exception: ex, message: "Timed out", level: :error)
356
+
357
+ self.current_error = "Request timed out. Try again"
358
+ self.status = 'Timeout'
359
+
360
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
361
+
362
+ rescue EOFError
363
+ if self.url.match?(/.*services\d{1,}.zuora.com*/)
364
+ self.current_error = "Services tenant '#{self.url.scan(/.*\/\/(services\d{1,}).zuora.com*/).last.first}' is no longer available."
365
+ self.status = 'Not Available'
366
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
367
+ end
368
+
387
369
  end
388
370
 
389
- def get_session(prefix: false, auth_type: :basic)
390
- Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}") if Rails.env.to_s == 'development'
371
+ def get_session(prefix: false, auth_type: :basic, zuora_track_id: nil)
391
372
  case auth_type
392
373
  when :basic
393
374
  if self.current_session.blank?
394
375
  case self.class.to_s
395
376
  when 'ZuoraAPI::Oauth'
396
377
  if self.bearer_token.blank? || self.oauth_expired?
397
- self.new_session(auth_type: :bearer)
378
+ self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
398
379
  end
399
- self.get_z_session if self.status == 'Active'
380
+ self.get_z_session(zuora_track_id: zuora_track_id)
400
381
  when 'ZuoraAPI::Basic'
401
- self.new_session(auth_type: :basic)
382
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
402
383
  else
403
- raise "No Zuora Login Specified"
384
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
404
385
  end
405
386
  end
406
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
407
387
  return prefix ? "ZSession #{self.current_session}" : self.current_session.to_s
408
388
  when :bearer
409
389
  case self.class.to_s
410
390
  when 'ZuoraAPI::Oauth'
411
391
  if self.bearer_token.blank? || self.oauth_expired?
412
- self.new_session(auth_type: :bearer)
392
+ self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
413
393
  end
414
394
  when 'ZuoraAPI::Basic'
415
395
  raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Basic Login, does not support Authentication of Type: #{auth_type}")
396
+ else
397
+ raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Unknown Login, does not support Authentication of Type: #{auth_type}")
416
398
  end
417
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
418
399
  return prefix ? "Bearer #{self.bearer_token}" : self.bearer_token.to_s
419
400
  end
420
401
  end
421
402
 
422
- def soap_call(ns1: 'ns1', ns2: 'ns2', batch_size: nil, single_transaction: false, debug: false, errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS), z_session: true, timeout_retry: false, timeout: 120,**keyword_args)
403
+ def soap_call(
404
+ ns1: 'ns1',
405
+ ns2: 'ns2',
406
+ batch_size: nil,
407
+ headers: {},
408
+ single_transaction: false,
409
+ debug: false,
410
+ zuora_track_id: nil,
411
+ errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
412
+ z_session: true,
413
+ timeout_retry: false,
414
+ timeout: 130,
415
+ timeout_sleep_interval: self.timeout_sleep,
416
+ output_exception_messages: true,
417
+ skip_session: false,
418
+ **keyword_args)
423
419
  tries ||= 2
424
420
  xml = Nokogiri::XML::Builder.new do |xml|
425
421
  xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/",
@@ -428,8 +424,10 @@ module ZuoraAPI
428
424
  'xmlns:api' => "http://api.zuora.com/",
429
425
  "xmlns:#{ns1}" => "http://api.zuora.com/") do
430
426
  xml['SOAP-ENV'].Header do
431
- xml["#{ns1}"].SessionHeader do
432
- xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic)
427
+ if !skip_session
428
+ xml["#{ns1}"].SessionHeader do
429
+ xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: zuora_track_id)
430
+ end
433
431
  end
434
432
  if single_transaction
435
433
  xml["#{ns1}"].CallOptions do
@@ -447,96 +445,212 @@ module ZuoraAPI
447
445
  end
448
446
  end
449
447
  end
450
-
451
448
  input_xml = Nokogiri::XML(xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip)
452
449
  input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
453
450
  Rails.logger.debug("Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
454
451
 
455
- response = HTTParty.post(self.url,:body => xml.doc.to_xml(:save_with => XML_SAVE_OPTIONS).strip, :headers => {'Content-Type' => "text/xml; charset=utf-8"}, :timeout => timeout)
452
+ headers.merge!({ 'Content-Type' => "text/xml; charset=utf-8", 'Accept' => 'text/xml'})
453
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
454
+
455
+ request = HTTParty::Request.new(
456
+ Net::HTTP::Post,
457
+ self.url,
458
+ body: xml.doc.to_xml(:save_with => XML_SAVE_OPTIONS).strip,
459
+ headers: headers,
460
+ timeout: timeout,
461
+ )
462
+
463
+ response = request.perform
464
+
456
465
  output_xml = Nokogiri::XML(response.body)
457
466
  Rails.logger.debug("Response SOAP XML: #{output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
458
467
 
459
468
  raise_errors(type: :SOAP, body: output_xml, response: response)
469
+
470
+ return output_xml, input_xml, response
471
+
460
472
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
461
- if !(tries -= 1).zero? && z_session
473
+ raise if skip_session
474
+ if !tries.zero? && z_session
475
+ tries -= 1
462
476
  Rails.logger.debug("SOAP Call - Session Invalid")
463
- self.new_session(auth_type: :basic)
464
- retry
465
- else
466
- if errors.include?(ex.class)
467
- raise ex
468
- else
469
- return output_xml, input_xml, response
477
+
478
+ begin
479
+ self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
480
+ rescue *ZUORA_API_ERRORS => ex
481
+ return output_xml, input_xml, ex.response
470
482
  end
483
+
484
+ retry
471
485
  end
486
+
487
+ raise ex if errors.include?(ex.class)
488
+
489
+ return output_xml, input_xml, response
490
+
472
491
  rescue *ZUORA_API_ERRORS => ex
473
- if errors.include?(ex.class)
474
- raise ex
475
- else
476
- return output_xml, input_xml, response
477
- end
492
+ raise ex if errors.include?(ex.class)
493
+
494
+ response = ex.response unless response
495
+ return output_xml, input_xml, response
496
+
478
497
  rescue *CONNECTION_EXCEPTIONS => ex
479
- if !(tries -= 1).zero?
480
- Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
481
- sleep(self.timeout_sleep)
498
+ if !tries.zero?
499
+ tries -= 1
500
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
501
+ sleep(timeout_sleep_interval)
482
502
  retry
483
- else
484
- raise ex
485
503
  end
504
+
505
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
506
+ raise ex
507
+
486
508
  rescue *CONNECTION_READ_EXCEPTIONS => ex
487
- if !(tries -= 1).zero? && timeout_retry
488
- Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
489
- sleep(self.timeout_sleep)
490
- retry
491
- else
492
- raise ex
509
+ if !tries.zero?
510
+ tries -= 1
511
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
512
+ if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
513
+ retry
514
+ elsif timeout_retry
515
+ sleep(timeout_sleep_interval)
516
+ retry
517
+ end
493
518
  end
494
- rescue Errno::ECONNRESET => ex
495
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
496
- retry
519
+
520
+ self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
521
+ ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
522
+ raise ex
523
+
524
+ rescue => ex
525
+ raise ex
526
+ ensure
527
+ self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
528
+ end
529
+
530
+ def error_logger(ex)
531
+ exception_args = Rails.logger.with_fields.merge(self.exception_args(ex))
532
+ case ex
533
+ when ZuoraAPI::Exceptions::ZuoraAPIUnkownError, ZuoraAPI::Exceptions::ZuoraDataIntegrity
534
+ Rails.logger.error('Zuora Unknown/Integrity Error', ex, exception_args)
535
+ when ZuoraAPI::Exceptions::ZuoraAPIRequestLimit
536
+ Rails.logger.info('Zuora APILimit Reached', exception_args)
537
+ when *(ZuoraAPI::Login::ZUORA_API_ERRORS-ZuoraAPI::Login::ZUORA_SERVER_ERRORS)
538
+ #Rails.logger.debug('Zuora API Error', ex, self.exception_args(ex))
539
+ when *ZuoraAPI::Login::ZUORA_SERVER_ERRORS
540
+ Rails.logger.error('Zuora Server Error', ex, exception_args)
541
+ end
542
+ end
543
+
544
+ def log(location: "Rest Call", exception: nil, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :info )
545
+ level = :debug if ![:debug, :info, :warn, :error, :fatal].include?(level)
546
+ if Rails.logger.class.to_s == "Ougai::Logger"
547
+ Rails.logger.send(level.to_sym, "#{location} - #{message}", exception)
497
548
  else
498
- raise ex
549
+ Rails.logger.send(level.to_sym, "#{location} - #{exception.class} #{message}")
550
+ end
551
+ end
552
+
553
+ def exception_args(ex)
554
+ args = {}
555
+ if ex.class == ZuoraAPI::Exceptions::ZuoraAPIRequestLimit
556
+ args.merge!({
557
+ zuora_trace_id: ex.response.headers["zuora-request-id"],
558
+ zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"]
559
+ })
560
+ elsif defined?(ex.response) && ex.response.present?
561
+ args.merge!({
562
+ request: {
563
+ path: ex.response.request.path.to_s,
564
+ method: ex.response.request.http_method.to_s.split("Net::HTTP::").last.upcase,
565
+ params: ex.response.request.raw_body.to_s,
566
+ 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,
567
+ },
568
+ response: {
569
+ status: ex.response.code,
570
+ params: ex.response.body.to_s,
571
+ headers: ex.response.headers.to_s,
572
+ },
573
+ zuora_trace_id: ex.response.headers["zuora-request-id"],
574
+ zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"],
575
+ })
576
+ elsif defined?(ex.request) && ex.request.present?
577
+ args.merge!({
578
+ request: {
579
+ path: ex.request.path.to_s,
580
+ method: ex.request.http_method.to_s.split("Net::HTTP::").last.upcase,
581
+ params: ex.request.options[:body],
582
+ headers: ex.request.options[:headers].map{|k,v| [k.to_s, k.to_s.downcase.strip == "authorization" ? "VALUE FILTERED" : v]}.to_h.to_s
583
+ }
584
+ })
585
+ args.merge!({
586
+ zuora_track_id: ex.request.options[:headers]["Zuora-Track-Id"]
587
+ }) if ex.request.options[:headers]["Zuora-Track-Id"].present?
499
588
  end
500
589
  rescue => ex
501
- raise ex
502
- else
503
- return output_xml, input_xml, response
590
+ Rails.logger.error("Failed to create exception arguments", ex, args)
591
+ ensure
592
+ return args
504
593
  end
505
594
 
506
595
  def raise_errors(type: :SOAP, body: nil, response: nil)
596
+ request_uri, request_path, match_string = "", "", ""
597
+ if response.class.to_s == "HTTP::Message"
598
+ request_uri = response.http_header.request_uri.to_s
599
+ request_path = response.http_header.request_uri.path
600
+ match_string = "#{response.http_header.request_method}::#{response.code}::#{request_uri}"
601
+ else
602
+ request = response.request
603
+ request_uri = response.request.uri
604
+ request_path = request.path.path
605
+ match_string = "#{request.http_method.to_s.split("Net::HTTP::").last.upcase}::#{response.code}::#{request_path}"
606
+ end
607
+
507
608
  if [502,503].include?(response.code)
508
- raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from downstream host", response)
609
+ raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
509
610
  end
510
611
 
511
- if response.code == 504
512
- raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from downstream host", response)
612
+ # Check failure response code
613
+ case response.code
614
+ when 504
615
+ raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from 'https://#{rest_domain(endpoint: request_uri)}'", response)
616
+ when 429
617
+ 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)
618
+ when 401
619
+
620
+ else
621
+ if body.class == Hash
622
+ case request_path
623
+ when /^\/v1\/connections$/
624
+ response_headers = response.headers.to_h
625
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing cookies for authentication call", response) if response_headers['set-cookie'].blank?
626
+ z_session_cookie = response_headers.fetch('set-cookie', []).select{|x| x.match(/^ZSession=.*/) }.first
627
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing ZSession cookie for authentication call", response) if z_session_cookie.blank?
628
+ end
629
+ end
513
630
  end
514
631
 
515
632
  case type
516
633
  when :SOAP
517
- error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
518
- message = body.xpath('//fns:FaultMessage', 'fns' =>'http://fault.api.zuora.com/').text
519
-
520
- if error.blank? || message.blank?
521
- error = body.xpath('//faultcode').text
522
- message = body.xpath('//faultstring').text
523
- end
634
+ error, success, message = get_soap_error_and_message(body)
524
635
 
525
- if error.blank? || message.blank?
526
- error = body.xpath('//ns1:Code', 'ns1' =>'http://api.zuora.com/').text
527
- message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
636
+ if body.xpath('//fns:LoginFault', 'fns' =>'http://fault.api.zuora.com/').present?
637
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response)
528
638
  end
529
639
 
530
- #Update/Create/Delete Calls with multiple requests and responses
531
- if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
532
- error = []
533
- success = []
534
- body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
535
- if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
536
- message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
537
- error.push(message)
640
+ if body.xpath('//ns1:queryResponse', 'ns1' => 'http://api.zuora.com/').present? &&
641
+ body.xpath(
642
+ '//ns1:records[@xsi:type="ns2:Export"]',
643
+ 'ns1' => 'http://api.zuora.com/', 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
644
+ ).present?
645
+ result = body.xpath('//ns2:Status', 'ns2' => 'http://object.api.zuora.com/').text
646
+ if result == 'Failed'
647
+ message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
648
+ error = 'FATAL_ERROR'
649
+ if message.present?
650
+ identifier, new_message = message.scan(/^([\w\d]{16})\: (.*)/).first
651
+ error, message = ['UNEXPECTED_ERROR', new_message] if new_message.present?
538
652
  else
539
- success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
653
+ message = 'Export failed due to unknown reason. Consult api logs.'
540
654
  end
541
655
  end
542
656
  end
@@ -544,44 +658,52 @@ module ZuoraAPI
544
658
  #By default response if not passed in for SOAP as all SOAP is 200
545
659
  if error.present?
546
660
  if error.class == String
547
- case error
548
- when "INVALID_SESSION"
549
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{error}::#{message}", response)
550
- when "REQUEST_EXCEEDED_LIMIT"
551
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{error}::#{message}", response)
552
- when "LOCK_COMPETITION"
553
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
554
- when "BATCH_FAIL_ERROR"
555
- if message.include?("optimistic locking failed")
556
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
557
- else
558
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
559
- end
560
- when "TEMPORARY_ERROR"
561
- raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{error}::#{message}", response)
562
- when "INVALID_VALUE"
563
- if message.include?("data integrity violation")
564
- raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
565
- else
566
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
567
- end
568
- else
569
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response) if error.present?
570
- end
661
+ raise_errors_helper(error: error, message: message, response: response)
571
662
  elsif error.class == Array
572
- if error[0].include?("LOCK_COMPETITION") && error.count == 1
573
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(error.group_by {|v| v}.map {|k,v| "(#{v.size}x) - #{k == "::" ? 'UNKNOWN::No error provided' : k}"}.join(', '), response)
663
+ if error.uniq.size == 1
664
+ err, msg = error[0].split('::')
665
+ raise_errors_helper(error: err, message: msg, response: response, errors: error, success: success)
574
666
  else
575
667
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new(error.group_by {|v| v}.map {|k,v| "(#{v.size}x) - #{k == "::" ? 'UNKNOWN::No error provided' : k}"}.join(', '), response, error, success)
576
668
  end
577
669
  end
578
670
  end
671
+
672
+ self.errors_via_content_type(response: response, type: :xml)
579
673
 
580
- if response.code == 429
581
- 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)
674
+ when :JSON
675
+ case request_path
676
+ when /^\/query\/jobs.*/ #DataQuery Paths
677
+ return if body.class != Hash
678
+ case match_string
679
+ when /^GET::200::\/query\/jobs\/([a-zA-Z0-9\-_]+)$/ #Get DQ job, Capture of the id is present if needed in future error responses.
680
+ if body.dig('data', "errorCode") == "LINK_10000005"
681
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body.dig('data', "errorMessage"), response)
682
+ elsif (body.dig('data', "errorMessage").present? || body.dig('data', "queryStatus") == "failed")
683
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('data', "errorMessage"), response)
684
+ end
685
+ 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.
686
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
687
+ when /^POST::400::\/query\/jobs$/ #Create DQ job
688
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
689
+ end
690
+ when /^\/api\/rest\/v1\/reports.*/ #Reporting Paths
691
+ reporting_message = response.parsed_response.dig("ZanResponse", "response", "message") || body.dig("message")
692
+ if reporting_message&.include?("com.zuora.rest.RestUsageException: The user does not have permissions for this API.")
693
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response)
694
+ elsif reporting_message&.include?("500 Internal Server Error")
695
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Internal Server Error. The Reporting API is down. Contact Support.", response)
696
+ end
697
+ case match_string
698
+ 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.
699
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response) if reporting_message.present?
700
+ end
701
+ when /\/objects\/batch\//
702
+ if body['code'].present? && /61$/.match(body['code'].to_s).present? # if last 2 digits of code are 61
703
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body['message'], nil, body['details'])
704
+ end
582
705
  end
583
706
 
584
- when :JSON
585
707
  body = body.dig("results").present? ? body["results"] : body if body.class == Hash
586
708
  if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
587
709
  messages_array = body.fetch("reasons", []).map {|error| error['message']}.compact
@@ -589,6 +711,14 @@ module ZuoraAPI
589
711
  codes_array = body.fetch("reasons", []).map {|error| error['code']}.compact
590
712
  codes_array = codes_array.push(body.dig("error", 'code')).compact if body.dig('error').class == Hash
591
713
 
714
+ if body['message'] == 'request exceeded limit'
715
+ 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)
716
+ end
717
+
718
+ if (body.dig('message') || '').downcase.include?('unexpected error') && response.code != 500
719
+ raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(body['message'], response)
720
+ end
721
+
592
722
  if body['message'] == "No bearer token" && response.code == 400
593
723
  raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Authentication type is not supported by this Login", response)
594
724
  end
@@ -602,16 +732,29 @@ module ZuoraAPI
602
732
  end
603
733
 
604
734
  #Oauth Tokens - User deactivated
605
- if body['reason'] == 'Forbidden' && body['status'] == 403 && response.code == 403
606
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Forbidden", response)
735
+ if body['path'] == '/oauth/token'
736
+ if body['status'] == 403 && response.code == 403
737
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Forbidden", response)
738
+ elsif body['status'] == 400 && response.code == 400 && body['message'].include?("Invalid client id")
739
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Invalid Oauth Client Id", response)
740
+ end
607
741
  end
608
742
 
609
743
  if body['error'] == 'Unauthorized' && body['status'] == 401
610
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
744
+ if body['message'].present?
745
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
746
+ else
747
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
748
+ end
611
749
  end
612
750
  #Authentication failed
613
751
  if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
614
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
752
+ new_message = messages_array.join(', ')
753
+ if new_message.present?
754
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(new_message, response)
755
+ else
756
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
757
+ end
615
758
  end
616
759
 
617
760
  #Zuora REST Create Amendment error #Authentication failed
@@ -619,16 +762,30 @@ module ZuoraAPI
619
762
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body['faultstring']}", response)
620
763
  end
621
764
 
765
+ #Locking contention
766
+ if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
767
+ raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
768
+ end
769
+ #Internal Server Error
770
+ if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(60)
771
+ if messages_array.uniq.size == 1
772
+ 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.*/)
773
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(messages_array.first, response)
774
+ end
775
+ end
776
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("#{messages_array.join(', ')}", response)
777
+ end
778
+
779
+ #Retryiable Service Error
780
+ if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(61)
781
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{messages_array.join(', ')}", response)
782
+ end
783
+
622
784
  #Request exceeded limit
623
785
  if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(70)
624
786
  raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{messages_array.join(', ')}", response)
625
787
  end
626
788
 
627
- #Locking contention
628
- if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
629
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
630
- end
631
-
632
789
  #All Errors catch
633
790
  if codes_array.size > 0
634
791
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages_array.join(', ')}", response)
@@ -636,26 +793,20 @@ module ZuoraAPI
636
793
 
637
794
  #Zuora REST Query Errors
638
795
  if body["faultcode"].present?
639
- case body["faultcode"]
640
- when "fns:MALFORMED_QUERY"
641
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
642
- when "fns:REQUEST_EXCEEDED_LIMIT"
643
- raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
644
- when "fns:LOCK_COMPETITION"
645
- raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
646
- when "INVALID_SESSION"
647
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
648
- else
649
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
650
- end
796
+ raise_errors_helper(error: body["faultcode"], message: body["faultstring"], response: response)
651
797
  end
652
798
 
653
799
  if body["Errors"].present? || body["errors"].present?
654
- errors = []
655
- (body["Errors"] || []).select { |obj| errors.push(obj["Message"]) }.compact
656
- (body["errors"] || []).select { |obj| errors.push(obj["Message"]) }.compact
657
- if errors.size > 0
658
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{errors.join(", ")}", response, errors)
800
+ codes, messages = [[],[]]
801
+ body.fetch("Errors", []).each { |obj| messages.push(obj["Message"]); messages.push(obj["title"]); codes.push(obj["Code"]); codes.push(obj["code"]) }
802
+ body.fetch("errors", []).each { |obj| messages.push(obj["Message"]); messages.push(obj["title"]); codes.push(obj["Code"]); codes.push(obj["code"]) }
803
+ codes, messages = [codes.uniq.compact, messages.uniq.compact]
804
+ if codes.size > 0
805
+ if codes.size == 1
806
+ raise_errors_helper(error: codes.first, message: messages.first, response: response, errors: messages)
807
+ else
808
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages.join(", ")}", response, messages)
809
+ end
659
810
  end
660
811
  end
661
812
  end
@@ -673,7 +824,7 @@ module ZuoraAPI
673
824
  raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("Retry Lock Competition", response)
674
825
  elsif error_messages.first.include?("data integrity violation")
675
826
  raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
676
- end
827
+ end
677
828
  end
678
829
  end
679
830
 
@@ -682,15 +833,130 @@ module ZuoraAPI
682
833
  end
683
834
  end
684
835
 
836
+ if body.class == Hash && body['message'].present?
837
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(body['message'], response) if response.code == 500
838
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['message'], response) if ![200,201].include?(response.code)
839
+ end
840
+
841
+ self.errors_via_content_type(response: response, type: :json)
842
+
685
843
  #All other errors
686
- if ![200,201].include?(response.code)
687
- if response.code == 429
688
- 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)
844
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(response.body, response) if ![200,201].include?(response.code)
845
+ end
846
+ end
847
+
848
+ def errors_via_content_type(response: nil, type: :xml)
849
+ response_content_types = response.headers.transform_keys(&:downcase).fetch('content-type', []).first || ""
850
+
851
+ if response_content_types.include?('application/json') && type != :json
852
+ output_json = JSON.parse(response.body)
853
+ self.raise_errors(type: :JSON, body: output_json, response: response)
854
+
855
+ elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml')) and type != :xml
856
+ output_xml = Nokogiri::XML(response.body)
857
+ self.raise_errors(type: :SOAP, body: output_xml, response: response)
858
+
859
+ elsif response_content_types.include?('text/html')
860
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Akamai Error", response) if response.headers.fetch('server', '') == 'AkamaiGHost'
861
+
862
+ parse_body = Nokogiri::HTML(response.body)
863
+ error_title = parse_body.xpath('//h2').text
864
+ error_title = parse_body.xpath('//h1').text if error_title.blank?
865
+ error_message = parse_body.xpath('//p').text
866
+
867
+ error_message = error_title if error_message.blank?
868
+
869
+ if error_title.present?
870
+ case error_title
871
+ when /Service Unavailable/
872
+ raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new(error_message, response)
873
+ when /Client sent a bad request./, /Bad Request/, /403 Forbidden/
874
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
875
+ when /414 Request-URI Too Large/
876
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Request URL is too long", response)
877
+ else
878
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
879
+ end
880
+ end
881
+
882
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Http response body is missing", response) if response.body.blank?
883
+ end
884
+
885
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(response.body, response) if response.code == 500
886
+ end
887
+
888
+
889
+ def get_soap_error_and_message(body)
890
+ error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
891
+ message = body.xpath('//fns:FaultMessage', 'fns' =>'http://fault.api.zuora.com/').text
892
+
893
+ if error.blank? || message.blank?
894
+ error = body.xpath('//faultcode').text
895
+ message = body.xpath('//faultstring').text
896
+ end
897
+
898
+ if error.blank? || message.blank?
899
+ error = body.xpath('//ns1:Code', 'ns1' =>'http://api.zuora.com/').text
900
+ message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
901
+ end
902
+
903
+ #Update/Create/Delete Calls with multiple requests and responses
904
+ if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
905
+ error = []
906
+ success = []
907
+ body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
908
+ if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
909
+ message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
910
+ error.push(message)
689
911
  else
690
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{response.message}", response)
912
+ success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
691
913
  end
692
914
  end
693
915
  end
916
+ return error, success, message
917
+ end
918
+
919
+ def raise_errors_helper(error: nil, message: nil, response: nil, errors: [], success: [])
920
+ case error
921
+ when /.*INVALID_SESSION/
922
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response, errors, success)
923
+ when /.*REQUEST_EXCEEDED_LIMIT/
924
+ raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new(message, response, errors, success)
925
+ when /.*LOCK_COMPETITION/
926
+ raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(message, response, errors, success)
927
+ when /.*BATCH_FAIL_ERROR/
928
+ if message.include?("optimistic locking failed") || message.include?("Operation failed due to a lock competition, please retry later.")
929
+ raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(message, response, errors, success)
930
+ elsif message.include?("org.hibernate.exception.ConstraintViolationException")
931
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
932
+ end
933
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
934
+ when /.*TEMPORARY_ERROR/, /.*TRANSACTION_TIMEOUT/
935
+ raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(message, response, errors, success)
936
+ when /.*INVALID_VALUE/
937
+ if message.include?("data integrity violation")
938
+ raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response, errors), success
939
+ end
940
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
941
+ when /.*UNKNOWN_ERROR/
942
+ if /payment\/refund|Credit Balance Adjustment|Payment Gateway|ARSettlement permission/.match(message).nil?
943
+ raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
944
+ end
945
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
946
+ 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/
947
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
948
+ when /.*UNEXPECTED_ERROR/
949
+ raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(message, response, errors, success)
950
+ when /.*soapenv:Server.*/
951
+ if /^Invalid value.*for type.*|^Id is invalid|^date string can not be less than 19 charactors$/.match(message).present?
952
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
953
+ elsif /^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
954
+ raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
955
+ end
956
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
957
+ else
958
+ raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Z:#{error}::#{message}", response, errors, success)
959
+ end
694
960
  end
695
961
 
696
962
  def aqua_query(queryName: '', query: '', version: '1.2', jobName: 'Aqua',partner: '', project: '')
@@ -737,7 +1003,7 @@ module ZuoraAPI
737
1003
  base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
738
1004
  url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
739
1005
  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"}
740
- response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 120)
1006
+ response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 130)
741
1007
 
742
1008
  raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401', response) if response.code == 401
743
1009
 
@@ -770,27 +1036,31 @@ module ZuoraAPI
770
1036
  end
771
1037
  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
772
1038
  end
1039
+
1040
+ return des_hash
773
1041
  rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
774
- if !(tries -= 1).zero?
775
- Rails.logger.info("Describe - #{ex.class} Timed out will retry after 5 seconds")
1042
+ if !tries.zero?
1043
+ tries -= 1
1044
+ self.log(location: "Describe", exception: ex, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :debug)
776
1045
  sleep(self.timeout_sleep)
777
1046
  retry
778
- else
779
- raise ex
780
1047
  end
1048
+
1049
+ self.log(location: "Describe", exception: ex, message: "Timed out", level: :error) if log_errors
1050
+ raise ex
1051
+
781
1052
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
782
- if !(tries -= 1).zero? && self.status == 'Active'
1053
+ if !tries.zero? && self.status == 'Active'
1054
+ tries -= 1
783
1055
  Rails.logger.debug("Describe session expired. Starting new session.")
784
1056
  self.new_session
785
1057
  retry
786
- else
787
- Rails.logger.error("Describe session expired. Starting new session.") if log_errors
788
- raise ex
789
1058
  end
1059
+
1060
+ Rails.logger.error("Describe session expired. Starting new session.") if log_errors
1061
+ raise ex
790
1062
  rescue => ex
791
1063
  raise ex
792
- else
793
- return des_hash
794
1064
  end
795
1065
 
796
1066
  def rest_call(
@@ -803,93 +1073,124 @@ module ZuoraAPI
803
1073
  z_session: true,
804
1074
  session_type: :basic,
805
1075
  timeout_retry: false,
806
- timeout: 120,
1076
+ timeout: 130,
1077
+ timeout_sleep_interval: self.timeout_sleep,
807
1078
  multipart: false,
808
- **keyword_args
1079
+ stream_body: false,
1080
+ output_exception_messages: true,
1081
+ zuora_track_id: nil,
1082
+ **keyword_args,
1083
+ &block
809
1084
  )
810
1085
  tries ||= 2
811
1086
 
812
- if self.entity_id.present?
813
- headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
814
- headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
815
- end
816
-
817
1087
  raise "Method not supported, supported methods include: :get, :post, :put, :delete, :patch, :head, :options" if ![:get, :post, :put, :delete, :patch, :head, :options].include?(method)
818
1088
 
819
- authentication_headers = z_session ? {"Authorization" => self.get_session(prefix: true, auth_type: session_type) } : {}
820
- modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
1089
+ authentication_headers = {}
1090
+ if z_session
1091
+ authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type, zuora_track_id: zuora_track_id) }
1092
+ if self.entity_id.present?
1093
+ authentication_headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
1094
+ authentication_headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
1095
+ end
1096
+ end
1097
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
821
1098
 
822
- response = HTTParty::Request.new(
823
- "Net::HTTP::#{method.to_s.capitalize}".constantize,
824
- url,
825
- body: body,
826
- headers: modified_headers,
827
- timeout: timeout,
828
- multipart: multipart,
829
- ).perform
1099
+ modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
830
1100
 
831
- Rails.logger.debug("Response Code: #{response.code}") if debug
832
1101
  begin
833
- output_json = JSON.parse(response.body)
834
- rescue JSON::ParserError => ex
835
- output_json = {}
1102
+ request = HTTParty::Request.new(
1103
+ "Net::HTTP::#{method.to_s.capitalize}".constantize,
1104
+ url,
1105
+ body: body,
1106
+ headers: modified_headers,
1107
+ timeout: timeout,
1108
+ multipart: multipart,
1109
+ stream_body: stream_body
1110
+ )
1111
+
1112
+ response = request.perform(&block)
1113
+
1114
+ Rails.logger.debug("Response Code: #{response.code}") if debug
1115
+ begin
1116
+ output_json = JSON.parse(response.body)
1117
+ rescue JSON::ParserError => ex
1118
+ output_json = {}
1119
+ end
1120
+ Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
1121
+
1122
+ raise_errors(type: :JSON, body: output_json, response: response)
1123
+ rescue => ex
1124
+ reset_files(body) if multipart
1125
+ raise
836
1126
  end
837
- Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
838
1127
 
839
- raise_errors(type: :JSON, body: output_json, response: response)
1128
+ return [output_json, response]
840
1129
  rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
841
1130
  if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
842
- self.rest_call(method: method.to_sym, url: url, body: body, debug: debug, errors: errors, z_session: z_session, session_type: :bearer, timeout_retry: timeout_retry, timeout: timeout)
843
- else
844
- Rails.logger.debug("Rest Call - Session Bad Auth type")
845
- raise ex
1131
+ session_type = :bearer
1132
+ retry
846
1133
  end
1134
+ Rails.logger.debug("Rest Call - Session Bad Auth type")
1135
+ raise ex
1136
+
847
1137
  rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
848
- if !(tries -= 1).zero? && z_session
1138
+ if !tries.zero? && z_session
1139
+ tries -= 1
849
1140
  Rails.logger.debug("Rest Call - Session Invalid #{session_type}")
850
- self.new_session(auth_type: session_type)
851
- retry
852
- else
853
- if errors.include?(ex.class)
854
- raise ex
855
- else
856
- return [output_json, response]
1141
+
1142
+ begin
1143
+ self.new_session(auth_type: session_type)
1144
+ rescue *ZUORA_API_ERRORS => ex
1145
+ return [output_json, ex.response]
857
1146
  end
1147
+
1148
+ retry
858
1149
  end
1150
+
1151
+ raise ex if errors.include?(ex.class)
1152
+ return [output_json, response]
1153
+
859
1154
  rescue *ZUORA_API_ERRORS => ex
860
- if errors.include?(ex.class)
861
- raise ex
862
- else
863
- return [output_json, response]
864
- end
1155
+ raise ex if errors.include?(ex.class)
1156
+
1157
+ response = ex.response unless response
1158
+ return [output_json, response]
1159
+
865
1160
  rescue ZuoraAPI::Exceptions::BadEntityError => ex
866
1161
  raise ex
867
1162
  rescue *CONNECTION_EXCEPTIONS => ex
868
- if !(tries -= 1).zero?
869
- Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
870
- sleep(self.timeout_sleep)
1163
+ if !tries.zero?
1164
+ tries -= 1
1165
+ self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
1166
+ sleep(timeout_sleep_interval)
871
1167
  retry
872
- else
873
- raise ex
874
1168
  end
1169
+
1170
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1171
+ raise ex
1172
+
875
1173
  rescue *CONNECTION_READ_EXCEPTIONS => ex
876
- if !(tries -= 1).zero? && timeout_retry
877
- Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
878
- sleep(self.timeout_sleep)
879
- retry
880
- else
881
- raise ex
882
- end
883
- rescue Errno::ECONNRESET => ex
884
- if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
885
- retry
886
- else
887
- raise ex
1174
+
1175
+ if !tries.zero?
1176
+ tries -= 1
1177
+ self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
1178
+ if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
1179
+ retry
1180
+ elsif timeout_retry
1181
+ sleep(timeout_sleep_interval)
1182
+ retry
1183
+ end
888
1184
  end
1185
+
1186
+ self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
1187
+ ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
1188
+ raise ex
1189
+
889
1190
  rescue => ex
890
1191
  raise ex
891
- else
892
- return [output_json, response]
1192
+ ensure
1193
+ self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
893
1194
  end
894
1195
 
895
1196
  def update_create_tenant
@@ -911,8 +1212,9 @@ module ZuoraAPI
911
1212
  while !response["nextPage"].blank?
912
1213
  url = self.rest_endpoint(response["nextPage"].split('/v1/').last)
913
1214
  Rails.logger.debug("Fetch Catalog URL #{url}")
914
- output_json, response = self.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true )
915
- if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
1215
+ output_json, response = self.rest_call(debug: false, url: url, timeout_retry: true)
1216
+
1217
+ if !/(true|t|yes|y|1)$/.match(output_json['success'].to_s) || output_json['success'].class != TrueClass
916
1218
  raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}", response)
917
1219
  end
918
1220
  output_json["products"].each do |product|
@@ -938,9 +1240,11 @@ module ZuoraAPI
938
1240
  return products, catalog_map
939
1241
  end
940
1242
 
941
- def get_file(url: nil, headers: {}, z_session: true, tempfile: true, output_file_name: 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)
1243
+ 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)
942
1244
  raise "file_path must be of class Pathname" if file_path.class != Pathname
943
1245
 
1246
+ retry_count ||= timeout_retries
1247
+
944
1248
  #Make sure directory exists
945
1249
  require 'fileutils'
946
1250
  FileUtils.mkdir_p(file_path) unless File.exists?(file_path)
@@ -955,8 +1259,9 @@ module ZuoraAPI
955
1259
  headers = headers.merge({"Zuora-Entity-Ids" => self.entity_id}) if !self.entity_id.blank?
956
1260
  end
957
1261
 
1262
+ headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
1263
+
958
1264
  response_save = nil
959
- retry_count ||= timeout_retries
960
1265
  http.request_get(uri.request_uri, headers) do |response|
961
1266
  response_save = response
962
1267
  status_code = response.code if response
@@ -1040,36 +1345,42 @@ module ZuoraAPI
1040
1345
  return file_handle
1041
1346
  when Net::HTTPUnauthorized
1042
1347
  if z_session
1043
- if !(retry_count -= 1).zero?
1348
+ unless (retry_count -= 1).zero?
1044
1349
  self.new_session
1045
- raise response.class
1046
- else
1047
- raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1350
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError, 'Retrying'
1048
1351
  end
1352
+ raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
1353
+ end
1354
+ raise
1355
+ when Net::HTTPNotFound
1356
+ if url.include?(self.fileURL)
1357
+ raise ZuoraAPI::Exceptions::FileDownloadError.new(
1358
+ "The current tenant does not have a file with id '#{url.split('/').last}'"
1359
+ )
1049
1360
  else
1050
- raise
1361
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1051
1362
  end
1052
1363
  else
1053
- raise StandardError.new("File Download Failed #{response.class}")
1364
+ raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
1054
1365
  end
1055
1366
  end
1056
-
1057
- rescue => ex
1367
+
1368
+ rescue => ex
1058
1369
  sleep(5)
1059
1370
  if (retry_count -= 1) >= 0
1060
1371
  retry
1061
- else
1062
- Rails.logger.error("File Download Failed")
1063
- raise
1064
1372
  end
1373
+ Rails.logger.error("File Download Failed")
1374
+ raise
1065
1375
  end
1066
1376
 
1067
- def getDataSourceExport(query, extract: true, encrypted: false, zip: true)
1377
+ def getDataSourceExport(query, extract: true, encrypted: false, zip: true, z_track_id: "")
1378
+ tries ||= 3
1068
1379
  request = Nokogiri::XML::Builder.new do |xml|
1069
1380
  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
1070
1381
  xml['SOAP-ENV'].Header do
1071
1382
  xml['ns1'].SessionHeader do
1072
- xml['ns1'].session self.get_session(prefix: false, auth_type: :basic)
1383
+ xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1073
1384
  end
1074
1385
  end
1075
1386
  xml['SOAP-ENV'].Body do
@@ -1086,17 +1397,20 @@ module ZuoraAPI
1086
1397
  end
1087
1398
  end
1088
1399
 
1089
- response_query = HTTParty.post(self.url, body: request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8"}, :timeout => 120)
1400
+ 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)
1090
1401
 
1091
1402
  output_xml = Nokogiri::XML(response_query.body)
1092
- raise 'Export Creation Unsuccessful : ' + output_xml.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1403
+ raise_errors(type: :SOAP, body: output_xml, response: response_query) if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1404
+
1405
+ # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
1406
+
1093
1407
  id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
1094
1408
 
1095
1409
  confirmRequest = Nokogiri::XML::Builder.new do |xml|
1096
1410
  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
1097
1411
  xml['SOAP-ENV'].Header do
1098
1412
  xml['ns1'].SessionHeader do
1099
- xml['ns1'].session self.get_session(prefix: false, auth_type: :basic)
1413
+ xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
1100
1414
  end
1101
1415
  end
1102
1416
  xml['SOAP-ENV'].Body do
@@ -1110,12 +1424,14 @@ module ZuoraAPI
1110
1424
 
1111
1425
  while result != "Completed"
1112
1426
  sleep 3
1113
- response_query = HTTParty.post(self.url, body: confirmRequest.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8"}, :timeout => 120)
1427
+ 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)
1114
1428
 
1115
1429
  output_xml = Nokogiri::XML(response_query.body)
1116
1430
  result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
1117
1431
  status_code = response_query.code if response_query
1118
- raise "Export Creation Unsuccessful : #{output_xml.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text}" if result.blank? || result == "Failed"
1432
+
1433
+ raise_errors(type: :SOAP, body: output_xml, response: response_query) if result.blank? || result == "Failed"
1434
+ # raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if result.blank? || result == "Failed"
1119
1435
  end
1120
1436
 
1121
1437
  file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
@@ -1134,10 +1450,39 @@ module ZuoraAPI
1134
1450
  else
1135
1451
  return export_file_path
1136
1452
  end
1453
+ rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
1454
+ if !(tries -= 1).zero?
1455
+ Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
1456
+ self.new_session
1457
+ retry
1458
+ end
1459
+ raise ex
1460
+
1461
+ rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
1462
+ if !(tries -= 1).zero?
1463
+ Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
1464
+ sleep 10
1465
+ retry
1466
+ end
1467
+ raise ex
1468
+
1469
+ rescue *ZUORA_API_ERRORS => ex
1470
+ raise ex
1471
+
1472
+ rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
1473
+ if !(tries -= 1).zero?
1474
+ Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
1475
+ sleep 5
1476
+ retry
1477
+ end
1478
+ raise ex
1479
+
1480
+ rescue ZuoraAPI::Exceptions::BadEntityError => ex
1481
+ raise ex
1137
1482
  end
1138
1483
 
1139
1484
  def query(query, parse = false)
1140
- output_xml, input_xml = self.soap_call({:debug => false, :timeout_retry => true}) do |xml|
1485
+ output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true) do |xml|
1141
1486
  xml['ns1'].query do
1142
1487
  xml['ns1'].queryString query
1143
1488
  end
@@ -1199,5 +1544,17 @@ module ZuoraAPI
1199
1544
  end
1200
1545
  return "failure"
1201
1546
  end
1547
+
1548
+ def reset_files(body)
1549
+ return unless body.is_a? Hash
1550
+
1551
+ body.transform_values! do |v|
1552
+ if v.is_a?(File)
1553
+ v.reopen(v.path)
1554
+ else
1555
+ v
1556
+ end
1557
+ end
1558
+ end
1202
1559
  end
1203
1560
  end