zuora_api 1.7.00 → 1.8.00
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +2 -2
- data/CHANGELOG.md +22 -0
- data/README.md +1 -1
- data/lib/insights_api/login.rb +5 -6
- data/lib/zuora_api/exceptions.rb +129 -22
- data/lib/zuora_api/login.rb +733 -424
- data/lib/zuora_api/logins/basic.rb +15 -6
- data/lib/zuora_api/logins/oauth.rb +56 -34
- data/lib/zuora_api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e79d63055902d66670dad6abed776b5c02ccb422b886bad2e632624e43eb16ba
|
4
|
+
data.tar.gz: 8abaee55dad2d4c3636bcbd08159a26dc403a8041aecb7d2ce2f6d84bd9f3917
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72165d5892a404b6e25c6f32e83e52065a23837eeb7f4d72a40f13e7c642c460362a3c4ed83b51760e5efac650c5eaffc4d4baa1b97b6c980818273af3fa9af1
|
7
|
+
data.tar.gz: 271b546b700d026e9ac12a22418eb90fc45a2a2b1bd66f872586a5552a4dfe703cb95f9f7dd78cb38960d4885a65e20acab0095ab0a2b5c5b3339295b129b995
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
@@ -48,8 +48,8 @@ rubygems-deploy:
|
|
48
48
|
- 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
49
|
- 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
50
|
- echo $VERSION
|
51
|
-
- sed -i "s/0.0.1/$VERSION/"
|
52
|
-
- git add
|
51
|
+
- sed -i "s/0.0.1/$VERSION/" lib/zuora_api/version.rb
|
52
|
+
- git add lib/zuora_api/version.rb
|
53
53
|
- git config --global user.email "connect@zuora.com"
|
54
54
|
- git config --global user.name "Connect Automation"
|
55
55
|
- git commit -m "Automated Version Update $VERSION"
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,28 @@
|
|
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
|
+
|
21
|
+
## [1.7.01] - 2018-8-06
|
22
|
+
### Changed
|
23
|
+
- Changed library used to determine host
|
24
|
+
- Added retry for 504 timouts in file download
|
25
|
+
|
4
26
|
## [1.7.00] - 2018-8-05
|
5
27
|
### Changed
|
6
28
|
- Raise proper exception when oauth client is from deactivated user.
|
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
|
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:
|
data/lib/insights_api/login.rb
CHANGED
@@ -190,12 +190,11 @@ module InsightsAPI
|
|
190
190
|
end
|
191
191
|
|
192
192
|
rescue => ex
|
193
|
-
if
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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
|
data/lib/zuora_api/exceptions.rb
CHANGED
@@ -1,14 +1,38 @@
|
|
1
1
|
module ZuoraAPI
|
2
2
|
module Exceptions
|
3
|
-
class Error < StandardError;
|
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.
|
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
|
@@ -23,8 +47,8 @@ module ZuoraAPI
|
|
23
47
|
attr_writer :default_message
|
24
48
|
|
25
49
|
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
26
|
-
@code = response.
|
27
|
-
@message = message
|
50
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
51
|
+
@message = parse_message(message)
|
28
52
|
@response = response
|
29
53
|
@default_message = "Error with Zuora Entity"
|
30
54
|
@errors = errors
|
@@ -41,8 +65,8 @@ module ZuoraAPI
|
|
41
65
|
attr_writer :default_message
|
42
66
|
|
43
67
|
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
44
|
-
@code = response.
|
45
|
-
@message = message
|
68
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
69
|
+
@message = parse_message(message)
|
46
70
|
@response = response
|
47
71
|
@default_message = "Error communicating with Zuora."
|
48
72
|
@errors = errors
|
@@ -54,13 +78,31 @@ module ZuoraAPI
|
|
54
78
|
end
|
55
79
|
end
|
56
80
|
|
81
|
+
class ZuoraAPIInternalServerError < Error
|
82
|
+
attr_reader :code, :response, :errors, :successes
|
83
|
+
attr_writer :default_message
|
84
|
+
|
85
|
+
def initialize(message = nil,response = nil, errors = [], successes = [], *args)
|
86
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
87
|
+
@message = parse_message(message)
|
88
|
+
@response = response
|
89
|
+
@default_message = "Zuora Internal Server Error."
|
90
|
+
@errors = errors
|
91
|
+
@successes = successes
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_s
|
95
|
+
@message || @default_message
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
57
99
|
class ZuoraAPIRequestLimit < Error
|
58
100
|
attr_reader :code, :response
|
59
101
|
attr_writer :default_message
|
60
102
|
|
61
|
-
def initialize(message = nil,response=nil,
|
62
|
-
@code = response.
|
63
|
-
@message = message
|
103
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
104
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
105
|
+
@message = parse_message(message)
|
64
106
|
@response = response
|
65
107
|
@default_message = "Your request limit has been exceeded for zuora."
|
66
108
|
end
|
@@ -70,13 +112,29 @@ module ZuoraAPI
|
|
70
112
|
end
|
71
113
|
end
|
72
114
|
|
115
|
+
class ZuoraAPIUnkownError < Error
|
116
|
+
attr_reader :code, :response
|
117
|
+
attr_writer :default_message
|
118
|
+
|
119
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
120
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
121
|
+
@message = parse_message(message)
|
122
|
+
@response = response
|
123
|
+
@default_message = "An unkown error occured. Workflow is not responsible. Please contact Support."
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
@message || @default_message
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
73
131
|
class ZuoraAPILockCompetition < Error
|
74
132
|
attr_reader :code, :response
|
75
133
|
attr_writer :default_message
|
76
134
|
|
77
|
-
def initialize(message = nil,response=nil,
|
78
|
-
@code = response.
|
79
|
-
@message = message
|
135
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
136
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
137
|
+
@message = parse_message(message)
|
80
138
|
@response = response
|
81
139
|
@default_message = "Operation failed due to lock competition. Please retry"
|
82
140
|
end
|
@@ -90,9 +148,9 @@ module ZuoraAPI
|
|
90
148
|
attr_reader :code, :response
|
91
149
|
attr_writer :default_message
|
92
150
|
|
93
|
-
def initialize(message = nil,response=nil,
|
94
|
-
@code = response.
|
95
|
-
@message = message
|
151
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
152
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
153
|
+
@message = parse_message(message)
|
96
154
|
@response = response
|
97
155
|
@default_message = "Operation failed due to lock competition. Please retry"
|
98
156
|
end
|
@@ -102,13 +160,29 @@ module ZuoraAPI
|
|
102
160
|
end
|
103
161
|
end
|
104
162
|
|
163
|
+
class ZuoraUnexpectedError < Error
|
164
|
+
attr_reader :code, :response
|
165
|
+
attr_writer :default_message
|
166
|
+
|
167
|
+
def initialize(message = nil, response=nil, errors = [], successes = [], *args)
|
168
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
169
|
+
@message = parse_message(message)
|
170
|
+
@response = response
|
171
|
+
@default_message = "An unexpected error occurred"
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_s
|
175
|
+
@message || @default_message
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
105
179
|
class ZuoraAPITemporaryError < Error
|
106
180
|
attr_reader :code, :response
|
107
181
|
attr_writer :default_message
|
108
182
|
|
109
|
-
def initialize(message = nil,response=nil, *args)
|
110
|
-
@code = response.
|
111
|
-
@message = message
|
183
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
184
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
185
|
+
@message = parse_message(message)
|
112
186
|
@response = response
|
113
187
|
@default_message = "There is a temporary error with zuora system."
|
114
188
|
end
|
@@ -122,10 +196,43 @@ module ZuoraAPI
|
|
122
196
|
attr_reader :code, :response
|
123
197
|
attr_writer :default_message
|
124
198
|
|
125
|
-
def initialize(message = nil,response=nil,
|
126
|
-
@code = response.
|
199
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
200
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
201
|
+
@message = parse_message(message)
|
202
|
+
@response = response
|
203
|
+
@default_message = "Authentication type is not supported by this Login"
|
204
|
+
end
|
205
|
+
|
206
|
+
def to_s
|
207
|
+
@message || @default_message
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class ZuoraAPIConnectionTimeout < Net::OpenTimeout
|
212
|
+
attr_reader :code, :response
|
213
|
+
attr_writer :default_message
|
214
|
+
|
215
|
+
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
216
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
217
|
+
@message = message
|
218
|
+
@response = response
|
219
|
+
@default_message = "Authentication type is not supported by this Login"
|
220
|
+
end
|
221
|
+
|
222
|
+
def to_s
|
223
|
+
@message || @default_message
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class ZuoraAPIReadTimeout < Net::ReadTimeout
|
228
|
+
attr_reader :code, :response, :request
|
229
|
+
attr_writer :default_message
|
230
|
+
|
231
|
+
def initialize(message = nil, response = nil, request = nil, errors = [], successes = [], *args)
|
232
|
+
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
127
233
|
@message = message
|
128
234
|
@response = response
|
235
|
+
@request = request
|
129
236
|
@default_message = "Authentication type is not supported by this Login"
|
130
237
|
end
|
131
238
|
|
data/lib/zuora_api/login.rb
CHANGED
@@ -5,13 +5,44 @@ 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
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
CONNECTION_EXCEPTIONS = [
|
14
|
+
Net::OpenTimeout,
|
15
|
+
OpenSSL::SSL::SSLError,
|
16
|
+
Errno::ECONNREFUSED,
|
17
|
+
SocketError,
|
18
|
+
Errno::EHOSTUNREACH,
|
19
|
+
Errno::EADDRNOTAVAIL,
|
20
|
+
Errno::ETIMEDOUT,
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
CONNECTION_READ_EXCEPTIONS = [
|
24
|
+
Net::ReadTimeout,
|
25
|
+
Errno::ECONNRESET,
|
26
|
+
Errno::EPIPE
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
ZUORA_API_ERRORS = [
|
30
|
+
ZuoraAPI::Exceptions::ZuoraAPIError,
|
31
|
+
ZuoraAPI::Exceptions::ZuoraAPIRequestLimit,
|
32
|
+
ZuoraAPI::Exceptions::ZuoraAPILockCompetition,
|
33
|
+
ZuoraAPI::Exceptions::ZuoraAPITemporaryError,
|
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
|
45
|
+
].freeze
|
15
46
|
|
16
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
|
17
48
|
|
@@ -19,10 +50,10 @@ module ZuoraAPI
|
|
19
50
|
raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
|
20
51
|
# raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
|
21
52
|
self.hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
|
22
|
-
if !/apps\/services\/a\/\d
|
53
|
+
if !/apps\/services\/a\/\d+\.\d$/.match(url.strip)
|
23
54
|
self.url = "https://#{hostname}/apps/services/a/#{MIN_Endpoint}"
|
24
|
-
elsif MIN_Endpoint.to_f > url.scan(/(\d
|
25
|
-
self.url = url.gsub(/(\d
|
55
|
+
elsif MIN_Endpoint.to_f > url.scan(/(\d+\.\d)$/).dig(0,0).to_f
|
56
|
+
self.url = url.gsub(/(\d+\.\d)$/, MIN_Endpoint)
|
26
57
|
else
|
27
58
|
self.url = url
|
28
59
|
end
|
@@ -41,27 +72,12 @@ module ZuoraAPI
|
|
41
72
|
|
42
73
|
def get_identity(cookies)
|
43
74
|
zsession = cookies["ZSession"]
|
44
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
45
75
|
begin
|
46
76
|
if !zsession.blank?
|
47
77
|
response = HTTParty.get("https://#{self.hostname}/apps/v1/identity", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
48
78
|
output_json = JSON.parse(response.body)
|
49
|
-
elsif zconnect_accesstoken.present?
|
50
|
-
begin
|
51
|
-
code = zconnect_accesstoken.split("#!").last
|
52
|
-
encrypted_token, tenant_id = Base64.decode64(code).split(":")
|
53
|
-
body = {'token' => encrypted_token}.to_json
|
54
|
-
rescue => ex
|
55
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Invalid ZConnect Cookie")
|
56
|
-
end
|
57
|
-
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/identity", :body => body, :headers => { 'Content-Type' => 'application/json' })
|
58
|
-
output_json = JSON.parse(response.body)
|
59
79
|
else
|
60
|
-
|
61
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
62
|
-
else
|
63
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
64
|
-
end
|
80
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
65
81
|
end
|
66
82
|
rescue JSON::ParserError => ex
|
67
83
|
output_json = {}
|
@@ -72,20 +88,12 @@ module ZuoraAPI
|
|
72
88
|
|
73
89
|
def get_full_nav(cookies)
|
74
90
|
zsession = cookies["ZSession"]
|
75
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
76
91
|
begin
|
77
92
|
if zsession.present?
|
78
93
|
response = HTTParty.get("https://#{self.hostname}/apps/v1/navigation", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
79
94
|
output_json = JSON.parse(response.body)
|
80
|
-
elsif zconnect_accesstoken.present?
|
81
|
-
response = HTTParty.get("https://#{self.hostname}/apps/zconnectsession/navigation", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}",'Content-Type' => 'application/json'})
|
82
|
-
output_json = JSON.parse(response.body)
|
83
95
|
else
|
84
|
-
|
85
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
86
|
-
else
|
87
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
88
|
-
end
|
96
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
89
97
|
end
|
90
98
|
rescue JSON::ParserError => ex
|
91
99
|
output_json = {}
|
@@ -96,20 +104,12 @@ module ZuoraAPI
|
|
96
104
|
|
97
105
|
def set_nav(state, cookies)
|
98
106
|
zsession = cookies["ZSession"]
|
99
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
100
107
|
begin
|
101
108
|
if !zsession.blank?
|
102
109
|
response = HTTParty.put("https://#{self.hostname}/apps/v1/preference/navigation", :body => state.to_json, :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
103
110
|
output_json = JSON.parse(response.body)
|
104
|
-
elsif !zconnect_accesstoken.blank?
|
105
|
-
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/navigationstate", :body => state.to_json, :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
|
106
|
-
output_json = JSON.parse(response.body)
|
107
111
|
else
|
108
|
-
|
109
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
110
|
-
else
|
111
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
112
|
-
end
|
112
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
113
113
|
end
|
114
114
|
rescue JSON::ParserError => ex
|
115
115
|
output_json = {}
|
@@ -120,20 +120,12 @@ module ZuoraAPI
|
|
120
120
|
|
121
121
|
def refresh_nav(cookies)
|
122
122
|
zsession = cookies["ZSession"]
|
123
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
124
123
|
begin
|
125
124
|
if !zsession.blank?
|
126
125
|
response = HTTParty.post("https://#{self.hostname}/apps/v1/navigation/fetch", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
127
126
|
output_json = JSON.parse(response.body)
|
128
|
-
elsif !zconnect_accesstoken.blank?
|
129
|
-
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/refresh-navbarcache", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
|
130
|
-
output_json = JSON.parse(response.body)
|
131
127
|
else
|
132
|
-
|
133
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
134
|
-
else
|
135
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
136
|
-
end
|
128
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
137
129
|
end
|
138
130
|
rescue JSON::ParserError => ex
|
139
131
|
output_json = {}
|
@@ -142,34 +134,28 @@ module ZuoraAPI
|
|
142
134
|
return output_json
|
143
135
|
end
|
144
136
|
|
145
|
-
def get_zconnect_accesstoken(cookies)
|
146
|
-
accesstoken = nil
|
147
|
-
self.update_zconnect_provider
|
148
|
-
if !cookies[self.zconnect_provider].nil? && !cookies[self.zconnect_provider].empty?
|
149
|
-
accesstoken = cookies[self.zconnect_provider]
|
150
|
-
end
|
151
|
-
return accesstoken
|
152
|
-
end
|
153
|
-
|
154
137
|
def reporting_url(path)
|
155
138
|
map = {"US" => {"Sandbox" => "https://zconnectsandbox.zuora.com/api/rest/v1/",
|
156
139
|
"Production" => "https://zconnect.zuora.com/api/rest/v1/",
|
157
|
-
"
|
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/"},
|
158
144
|
"EU" => {"Sandbox" => "https://zconnect.sandbox.eu.zuora.com/api/rest/v1/",
|
159
145
|
"Production" => "https://zconnect.eu.zuora.com/api/rest/v1/",
|
160
|
-
"Services"=> ""},
|
146
|
+
"Services"=> "https://reporting-sbx0000.sbx.aec1.zuora.com/api/rest/v1/"},
|
161
147
|
"NA" => {"Sandbox" => "https://zconnect.sandbox.na.zuora.com/api/rest/v1/",
|
162
148
|
"Production" => "https://zconnect.na.zuora.com/api/rest/v1/",
|
163
149
|
"Services"=> ""}
|
164
150
|
}
|
165
|
-
return map[
|
151
|
+
return map[self.region][self.environment].insert(-1, path)
|
166
152
|
end
|
167
153
|
|
168
154
|
# There are two ways to call this method. The first way is best.
|
169
155
|
# 1. Pass in cookies and optionally custom_authorities, name, and description
|
170
156
|
# 2. Pass in user_id, entity_ids, client_id, client_secret, and optionally custom_authorities, name, and description
|
171
157
|
# https://intranet.zuora.com/confluence/display/Sunburst/Create+an+OAuth+Client+through+API+Gateway#CreateanOAuthClientthroughAPIGateway-ZSession
|
172
|
-
def get_oauth_client (custom_authorities = [], info_name: "No Name", info_desc: "This client was created without a description.", user_id: nil, entity_ids: nil, client_id: nil, client_secret: nil, new_client_id: nil, new_client_secret: nil, cookies: nil)
|
158
|
+
def get_oauth_client (custom_authorities = [], info_name: "No Name", info_desc: "This client was created without a description.", user_id: nil, entity_ids: nil, client_id: nil, client_secret: nil, new_client_id: nil, new_client_secret: nil, cookies: nil, chomp_v1_from_genesis_endpoint: false)
|
173
159
|
authorization = ""
|
174
160
|
new_client_id = SecureRandom.uuid if new_client_id.blank?
|
175
161
|
new_client_secret = SecureRandom.hex(10) if new_client_secret.blank?
|
@@ -195,7 +181,7 @@ module ZuoraAPI
|
|
195
181
|
end
|
196
182
|
|
197
183
|
if !authorization.blank? && !user_id.blank? && !entity_ids.blank?
|
198
|
-
endpoint = self.rest_endpoint("genesis/clients")
|
184
|
+
endpoint = chomp_v1_from_genesis_endpoint ? self.rest_endpoint.chomp("v1/").concat("genesis/clients") : self.rest_endpoint("genesis/clients")
|
199
185
|
oauth_response = HTTParty.post(endpoint, :headers => {'authorization' => authorization, 'Content-Type' => 'application/json'}, :body => {'clientId' => new_client_id, 'clientSecret' => new_client_secret, 'userId' => user_id, 'entityIds' => entity_ids, 'customAuthorities' => custom_authorities, 'additionalInformation' => {'description' => info_desc, 'name' => info_name}}.to_json)
|
200
186
|
output_json = JSON.parse(oauth_response.body)
|
201
187
|
if oauth_response.code == 201
|
@@ -212,7 +198,7 @@ module ZuoraAPI
|
|
212
198
|
end
|
213
199
|
|
214
200
|
def self.environments
|
215
|
-
%w(Sandbox Production Services Performance Staging)
|
201
|
+
%w(Sandbox Production Services Performance Staging Test)
|
216
202
|
end
|
217
203
|
|
218
204
|
def self.regions
|
@@ -224,11 +210,13 @@ module ZuoraAPI
|
|
224
210
|
"Production" => "https://www.zuora.com/apps/services/a/",
|
225
211
|
"Performance" => "https://pt1.zuora.com/apps/services/a/",
|
226
212
|
"Services" => "https://services347.zuora.com/apps/services/a/",
|
227
|
-
"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/"},
|
228
215
|
"EU" => {"Sandbox" => "https://sandbox.eu.zuora.com/apps/services/a/",
|
229
216
|
"Production" => "https://eu.zuora.com/apps/services/a/",
|
230
217
|
"Performance" => "https://pt1.eu.zuora.com/apps/services/a/",
|
231
|
-
"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/"},
|
232
220
|
"NA" => {"Sandbox" => "https://sandbox.na.zuora.com/apps/services/a/",
|
233
221
|
"Production" => "https://na.zuora.com/apps/services/a/",
|
234
222
|
"Performance" => "https://pt1.na.zuora.com/apps/services/a/",
|
@@ -265,15 +253,18 @@ module ZuoraAPI
|
|
265
253
|
|
266
254
|
def update_environment
|
267
255
|
if !self.url.blank?
|
268
|
-
|
256
|
+
case self.hostname
|
257
|
+
when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
|
269
258
|
self.environment = 'Sandbox'
|
270
|
-
|
259
|
+
when /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/
|
271
260
|
self.environment = 'Services'
|
272
|
-
|
261
|
+
when /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/
|
273
262
|
self.environment = 'Performance'
|
274
|
-
|
263
|
+
when /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/
|
275
264
|
self.environment = 'Staging'
|
276
|
-
|
265
|
+
when /(?<=\.|\/|^)(test)(?=\.|\/|$)/
|
266
|
+
self.environment = 'Test'
|
267
|
+
when /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/, /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/
|
277
268
|
self.environment = 'Production'
|
278
269
|
else
|
279
270
|
self.environment = 'Unknown'
|
@@ -283,25 +274,14 @@ module ZuoraAPI
|
|
283
274
|
end
|
284
275
|
end
|
285
276
|
|
286
|
-
def is_prod_env
|
287
|
-
is_prod = false
|
288
|
-
www_or_api = /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/ === self.hostname
|
289
|
-
host_prefix_match = /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/ === self.hostname
|
290
|
-
if www_or_api || host_prefix_match
|
291
|
-
is_prod = true
|
292
|
-
end
|
293
|
-
return is_prod
|
294
|
-
end
|
295
|
-
|
296
277
|
def update_zconnect_provider
|
297
278
|
region = update_region
|
298
279
|
environment = update_environment
|
299
|
-
mappings = {"US" => {"Sandbox" => "ZConnectSbx",
|
300
|
-
"NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "
|
301
|
-
"EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "
|
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"},
|
302
283
|
"Unknown" => {"Unknown" => "Unknown"}}
|
303
284
|
self.zconnect_provider = mappings[region][environment]
|
304
|
-
# raise "Can't find ZConnect Provider for #{region} region and #{environment} environment" if self.zconnect_provider.nil?
|
305
285
|
end
|
306
286
|
|
307
287
|
def aqua_endpoint(url="")
|
@@ -314,46 +294,35 @@ module ZuoraAPI
|
|
314
294
|
return "#{url_slash_apps_slash}api/#{url}"
|
315
295
|
end
|
316
296
|
|
317
|
-
def rest_endpoint(url="")
|
297
|
+
def rest_endpoint(url="", domain=true, prefix='/v1/')
|
318
298
|
update_environment
|
319
299
|
endpoint = url
|
320
|
-
|
300
|
+
url_postfix = {"US" => ".", "EU" => ".eu.", "NA" => ".na."}[self.region]
|
301
|
+
|
321
302
|
case self.environment
|
303
|
+
when 'Test'
|
304
|
+
endpoint = "https://rest.test#{url_postfix}zuora.com"
|
322
305
|
when 'Sandbox'
|
323
|
-
|
324
|
-
|
325
|
-
endpoint = "https://rest.apisandbox.zuora.com/v1/".concat(url)
|
326
|
-
when 'EU'
|
327
|
-
endpoint = "https://rest.sandbox.eu.zuora.com/v1/".concat(url)
|
328
|
-
when 'NA'
|
329
|
-
endpoint = "https://rest.sandbox.na.zuora.com/v1/".concat(url)
|
330
|
-
end
|
306
|
+
endpoint = "https://rest.sandbox#{url_postfix}zuora.com"
|
307
|
+
endpoint = "https://rest.apisandbox.zuora.com" if self.region == "US"
|
331
308
|
when 'Production'
|
332
|
-
|
333
|
-
when 'US'
|
334
|
-
endpoint = "https://rest.zuora.com/v1/".concat(url)
|
335
|
-
when 'EU'
|
336
|
-
endpoint = "https://rest.eu.zuora.com/v1/".concat(url)
|
337
|
-
when 'NA'
|
338
|
-
endpoint = "https://rest.na.zuora.com/v1/".concat(url)
|
339
|
-
end
|
309
|
+
endpoint = "https://rest#{url_postfix}zuora.com"
|
340
310
|
when 'Performance'
|
341
|
-
endpoint = "https://rest.pt1.zuora.com
|
311
|
+
endpoint = "https://rest.pt1.zuora.com"
|
342
312
|
when 'Services'
|
343
313
|
https = /https:\/\/|http:\/\//.match(self.url)[0]
|
344
314
|
host = self.hostname
|
345
|
-
endpoint = "#{https}rest#{host}
|
315
|
+
endpoint = "#{https}rest#{host}"
|
346
316
|
when 'Staging'
|
347
|
-
endpoint = "https://rest-staging2.zuora.com
|
317
|
+
endpoint = "https://rest-staging2.zuora.com"
|
348
318
|
when 'Unknown'
|
349
319
|
raise "Environment unknown, returning passed in parameter unaltered"
|
350
320
|
end
|
351
|
-
return endpoint
|
321
|
+
return domain ? endpoint.concat(prefix).concat(url) : prefix.concat(url)
|
352
322
|
end
|
353
323
|
|
354
324
|
def rest_domain
|
355
|
-
|
356
|
-
return ::Addressable::URI.parse(self.rest_endpoint).host
|
325
|
+
return URI(self.rest_endpoint).host
|
357
326
|
end
|
358
327
|
|
359
328
|
def fileURL(url="")
|
@@ -364,10 +333,10 @@ module ZuoraAPI
|
|
364
333
|
return self.wsdl_number > 68 ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'
|
365
334
|
end
|
366
335
|
|
367
|
-
def new_session(auth_type: :basic, debug: false)
|
336
|
+
def new_session(auth_type: :basic, debug: false, zuora_track_id: nil)
|
368
337
|
end
|
369
338
|
|
370
|
-
def get_session(prefix: false, auth_type: :basic)
|
339
|
+
def get_session(prefix: false, auth_type: :basic, zuora_track_id: nil)
|
371
340
|
Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}") if Rails.env.to_s == 'development'
|
372
341
|
case auth_type
|
373
342
|
when :basic
|
@@ -375,13 +344,13 @@ module ZuoraAPI
|
|
375
344
|
case self.class.to_s
|
376
345
|
when 'ZuoraAPI::Oauth'
|
377
346
|
if self.bearer_token.blank? || self.oauth_expired?
|
378
|
-
self.new_session(auth_type: :bearer)
|
347
|
+
self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
|
379
348
|
end
|
380
|
-
self.get_z_session if self.status == 'Active'
|
349
|
+
self.get_z_session(zuora_track_id: zuora_track_id) if self.status == 'Active'
|
381
350
|
when 'ZuoraAPI::Basic'
|
382
|
-
self.new_session(auth_type: :basic)
|
351
|
+
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
383
352
|
else
|
384
|
-
|
353
|
+
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
385
354
|
end
|
386
355
|
end
|
387
356
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
|
@@ -390,17 +359,33 @@ module ZuoraAPI
|
|
390
359
|
case self.class.to_s
|
391
360
|
when 'ZuoraAPI::Oauth'
|
392
361
|
if self.bearer_token.blank? || self.oauth_expired?
|
393
|
-
self.new_session(auth_type: :bearer)
|
362
|
+
self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
|
394
363
|
end
|
395
364
|
when 'ZuoraAPI::Basic'
|
396
365
|
raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Basic Login, does not support Authentication of Type: #{auth_type}")
|
366
|
+
else
|
367
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Unknown Login, does not support Authentication of Type: #{auth_type}")
|
397
368
|
end
|
369
|
+
|
398
370
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
|
399
371
|
return prefix ? "Bearer #{self.bearer_token}" : self.bearer_token.to_s
|
400
372
|
end
|
401
373
|
end
|
402
374
|
|
403
|
-
def soap_call(
|
375
|
+
def soap_call(
|
376
|
+
ns1: 'ns1',
|
377
|
+
ns2: 'ns2',
|
378
|
+
batch_size: nil,
|
379
|
+
single_transaction: false,
|
380
|
+
debug: false,
|
381
|
+
zuora_track_id: nil,
|
382
|
+
errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
|
383
|
+
z_session: true,
|
384
|
+
timeout_retry: false,
|
385
|
+
timeout: 120,
|
386
|
+
timeout_sleep_interval: self.timeout_sleep,
|
387
|
+
output_exception_messages: true,
|
388
|
+
**keyword_args)
|
404
389
|
tries ||= 2
|
405
390
|
xml = Nokogiri::XML::Builder.new do |xml|
|
406
391
|
xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/",
|
@@ -410,7 +395,7 @@ module ZuoraAPI
|
|
410
395
|
"xmlns:#{ns1}" => "http://api.zuora.com/") do
|
411
396
|
xml['SOAP-ENV'].Header do
|
412
397
|
xml["#{ns1}"].SessionHeader do
|
413
|
-
xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic)
|
398
|
+
xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: zuora_track_id)
|
414
399
|
end
|
415
400
|
if single_transaction
|
416
401
|
xml["#{ns1}"].CallOptions do
|
@@ -433,15 +418,34 @@ module ZuoraAPI
|
|
433
418
|
input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
|
434
419
|
Rails.logger.debug("Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
|
435
420
|
|
436
|
-
|
421
|
+
headers = { 'Content-Type' => "text/xml; charset=utf-8", 'Accept' => 'text/xml'}
|
422
|
+
headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
|
423
|
+
|
424
|
+
request = HTTParty::Request.new(
|
425
|
+
Net::HTTP::Post,
|
426
|
+
self.url,
|
427
|
+
body: xml.doc.to_xml(:save_with => XML_SAVE_OPTIONS).strip,
|
428
|
+
headers: headers,
|
429
|
+
timeout: timeout,
|
430
|
+
)
|
431
|
+
|
432
|
+
response = request.perform
|
433
|
+
|
437
434
|
output_xml = Nokogiri::XML(response.body)
|
438
435
|
Rails.logger.debug("Response SOAP XML: #{output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
|
439
436
|
|
440
437
|
raise_errors(type: :SOAP, body: output_xml, response: response)
|
441
438
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
442
|
-
if !
|
439
|
+
if !tries.zero? && z_session
|
440
|
+
tries -= 1
|
443
441
|
Rails.logger.debug("SOAP Call - Session Invalid")
|
444
|
-
|
442
|
+
|
443
|
+
begin
|
444
|
+
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
445
|
+
rescue *ZUORA_API_ERRORS => ex
|
446
|
+
return output_xml, input_xml, ex.response
|
447
|
+
end
|
448
|
+
|
445
449
|
retry
|
446
450
|
else
|
447
451
|
if errors.include?(ex.class)
|
@@ -454,30 +458,45 @@ module ZuoraAPI
|
|
454
458
|
if errors.include?(ex.class)
|
455
459
|
raise ex
|
456
460
|
else
|
461
|
+
response = ex.response unless response
|
457
462
|
return output_xml, input_xml, response
|
458
463
|
end
|
459
464
|
rescue *CONNECTION_EXCEPTIONS => ex
|
460
|
-
if
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
465
|
+
if tries.zero?
|
466
|
+
if output_exception_messages
|
467
|
+
if Rails.logger.class.to_s == "Ougai::Logger"
|
468
|
+
Rails.logger.error("SOAP Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
469
|
+
else
|
470
|
+
Rails.logger.error("SOAP Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
471
|
+
end
|
472
|
+
end
|
473
|
+
raise ex
|
466
474
|
end
|
475
|
+
|
476
|
+
tries -= 1
|
477
|
+
sleep(timeout_sleep_interval)
|
478
|
+
retry
|
467
479
|
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
468
|
-
if !
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
480
|
+
if !tries.zero?
|
481
|
+
tries -= 1
|
482
|
+
|
483
|
+
if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
|
484
|
+
retry
|
485
|
+
elsif timeout_retry
|
486
|
+
sleep(timeout_sleep_interval)
|
487
|
+
retry
|
488
|
+
end
|
474
489
|
end
|
475
|
-
|
476
|
-
if
|
477
|
-
|
478
|
-
|
479
|
-
|
490
|
+
|
491
|
+
if output_exception_messages
|
492
|
+
if Rails.logger.class.to_s == "Ougai::Logger"
|
493
|
+
Rails.logger.error("SOAP Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
494
|
+
else
|
495
|
+
Rails.logger.error("SOAP Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
496
|
+
end
|
480
497
|
end
|
498
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from #{url}", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
499
|
+
raise ex
|
481
500
|
rescue => ex
|
482
501
|
raise ex
|
483
502
|
else
|
@@ -485,31 +504,60 @@ module ZuoraAPI
|
|
485
504
|
end
|
486
505
|
|
487
506
|
def raise_errors(type: :SOAP, body: nil, response: nil)
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
507
|
+
request_uri, request_path, match_string = "", "", ""
|
508
|
+
if response.class.to_s == "HTTP::Message"
|
509
|
+
request_uri = response.http_header.request_uri.to_s
|
510
|
+
request_path = response.http_header.request_uri.path
|
511
|
+
match_string = "#{response.http_header.request_method}::#{response.code}::#{request_uri}"
|
512
|
+
else
|
513
|
+
request = response.request
|
514
|
+
request_uri = response.request.uri
|
515
|
+
request_path = request.path.path
|
516
|
+
match_string = "#{request.http_method.to_s.split("Net::HTTP::").last.upcase}::#{response.code}::#{request_path}"
|
517
|
+
end
|
492
518
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
end
|
519
|
+
if [502,503].include?(response.code)
|
520
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from #{request_uri}", response)
|
521
|
+
end
|
497
522
|
|
498
|
-
|
499
|
-
|
500
|
-
|
523
|
+
# Check failure response code
|
524
|
+
case response.code
|
525
|
+
when 504
|
526
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from #{request_uri}", response)
|
527
|
+
when 429
|
528
|
+
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
|
+
when 401
|
530
|
+
|
531
|
+
else
|
532
|
+
if body.class == Hash
|
533
|
+
case request_path
|
534
|
+
when /^\/v1\/connections$/
|
535
|
+
response_headers = response.headers.to_h
|
536
|
+
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
|
538
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Missing ZSession cookie for authentication call", response) if z_session_cookie.blank?
|
539
|
+
end
|
501
540
|
end
|
541
|
+
end
|
502
542
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
543
|
+
case type
|
544
|
+
when :SOAP
|
545
|
+
error, success, message = get_soap_error_and_message(body)
|
546
|
+
|
547
|
+
if body.xpath('//ns1:queryResponse', 'ns1' => 'http://api.zuora.com/').present? &&
|
548
|
+
body.xpath(
|
549
|
+
'//ns1:records[@xsi:type="ns2:Export"]',
|
550
|
+
'ns1' => 'http://api.zuora.com/', 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance'
|
551
|
+
).present?
|
552
|
+
result = body.xpath('//ns2:Status', 'ns2' => 'http://object.api.zuora.com/').text
|
553
|
+
if result == 'Failed'
|
554
|
+
reason = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
|
555
|
+
if reason.present?
|
556
|
+
message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
|
557
|
+
error = message.match(/^[\w\d]{16}\: (Unexpected error.|No HTTP Response|Socket Timeout|There is an internal error, please try again later)/).present? ? 'UNEXPECTED_ERROR' : 'FATAL_ERROR'
|
511
558
|
else
|
512
|
-
|
559
|
+
error = 'FATAL_ERROR'
|
560
|
+
message = 'Export failed due to unknown reason. Consult api logs.'
|
513
561
|
end
|
514
562
|
end
|
515
563
|
end
|
@@ -517,44 +565,48 @@ module ZuoraAPI
|
|
517
565
|
#By default response if not passed in for SOAP as all SOAP is 200
|
518
566
|
if error.present?
|
519
567
|
if error.class == String
|
520
|
-
|
521
|
-
when "INVALID_SESSION"
|
522
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{error}::#{message}", response)
|
523
|
-
when "REQUEST_EXCEEDED_LIMIT"
|
524
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{error}::#{message}", response)
|
525
|
-
when "LOCK_COMPETITION"
|
526
|
-
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
|
527
|
-
when "BATCH_FAIL_ERROR"
|
528
|
-
if message.include?("optimistic locking failed")
|
529
|
-
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", response)
|
530
|
-
else
|
531
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
|
532
|
-
end
|
533
|
-
when "TEMPORARY_ERROR"
|
534
|
-
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{error}::#{message}", response)
|
535
|
-
when "INVALID_VALUE"
|
536
|
-
if message.include?("data integrity violation")
|
537
|
-
raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
|
538
|
-
else
|
539
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response)
|
540
|
-
end
|
541
|
-
else
|
542
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", response) if error.present?
|
543
|
-
end
|
568
|
+
raise_errors_helper(error: error, message: message, response: response)
|
544
569
|
elsif error.class == Array
|
545
|
-
if error
|
546
|
-
|
570
|
+
if error.uniq.size == 1
|
571
|
+
err, msg = error[0].split('::')
|
572
|
+
raise_errors_helper(error: err, message: msg, response: response, errors: error, success: success)
|
547
573
|
else
|
548
574
|
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)
|
549
575
|
end
|
550
576
|
end
|
551
577
|
end
|
578
|
+
|
579
|
+
self.errors_via_content_type(response: response, type: :xml)
|
552
580
|
|
553
|
-
|
554
|
-
|
581
|
+
when :JSON
|
582
|
+
case request_path
|
583
|
+
when /^\/query\/jobs.*/ #DataQuery Paths
|
584
|
+
return if body.class != Hash
|
585
|
+
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.
|
587
|
+
if body.dig('data', "errorCode") == "LINK_10000005"
|
588
|
+
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body.dig('data', "errorMessage"), response)
|
589
|
+
elsif (body.dig('data', "errorMessage").present? || body.dig('data', "queryStatus") == "failed")
|
590
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('data', "errorMessage"), response)
|
591
|
+
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.
|
593
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
|
594
|
+
when /^POST::400::\/query\/jobs$/ #Create DQ job
|
595
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body.dig('message'), response) if body.dig('message').present?
|
596
|
+
end
|
597
|
+
when /^\/api\/rest\/v1\/reports.*/ #Reporting Paths
|
598
|
+
reporting_message = response.parsed_response.dig("ZanResponse", "response", "message") || body.dig("message")
|
599
|
+
if reporting_message&.include?("com.zuora.rest.RestUsageException: The user does not have permissions for this API.")
|
600
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response)
|
601
|
+
elsif reporting_message&.include?("500 Internal Server Error")
|
602
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Internal Server Error. The Reporting API is down. Contact Support.", response)
|
603
|
+
end
|
604
|
+
case match_string
|
605
|
+
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
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response) if reporting_message.present?
|
607
|
+
end
|
555
608
|
end
|
556
609
|
|
557
|
-
when :JSON
|
558
610
|
body = body.dig("results").present? ? body["results"] : body if body.class == Hash
|
559
611
|
if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
|
560
612
|
messages_array = body.fetch("reasons", []).map {|error| error['message']}.compact
|
@@ -562,6 +614,14 @@ module ZuoraAPI
|
|
562
614
|
codes_array = body.fetch("reasons", []).map {|error| error['code']}.compact
|
563
615
|
codes_array = codes_array.push(body.dig("error", 'code')).compact if body.dig('error').class == Hash
|
564
616
|
|
617
|
+
if body['message'] == 'request exceeded limit'
|
618
|
+
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)
|
619
|
+
end
|
620
|
+
|
621
|
+
if (body.dig('message') || '').downcase.include?('unexpected error') && response.code != 500
|
622
|
+
raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(body['message'], response)
|
623
|
+
end
|
624
|
+
|
565
625
|
if body['message'] == "No bearer token" && response.code == 400
|
566
626
|
raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Authentication type is not supported by this Login", response)
|
567
627
|
end
|
@@ -575,8 +635,12 @@ module ZuoraAPI
|
|
575
635
|
end
|
576
636
|
|
577
637
|
#Oauth Tokens - User deactivated
|
578
|
-
if body['
|
579
|
-
|
638
|
+
if body['path'] == '/oauth/token'
|
639
|
+
if body['status'] == 403 && response.code == 403
|
640
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Forbidden", response)
|
641
|
+
elsif body['status'] == 400 && response.code == 400 && body['message'].include?("Invalid client id")
|
642
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("Invalid Oauth Client Id", response)
|
643
|
+
end
|
580
644
|
end
|
581
645
|
|
582
646
|
if body['error'] == 'Unauthorized' && body['status'] == 401
|
@@ -584,7 +648,12 @@ module ZuoraAPI
|
|
584
648
|
end
|
585
649
|
#Authentication failed
|
586
650
|
if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
|
587
|
-
|
651
|
+
new_message = messages_array.join(', ')
|
652
|
+
if new_message.present?
|
653
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(new_message, response)
|
654
|
+
else
|
655
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
|
656
|
+
end
|
588
657
|
end
|
589
658
|
|
590
659
|
#Zuora REST Create Amendment error #Authentication failed
|
@@ -592,16 +661,30 @@ module ZuoraAPI
|
|
592
661
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body['faultstring']}", response)
|
593
662
|
end
|
594
663
|
|
664
|
+
#Locking contention
|
665
|
+
if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
|
666
|
+
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
|
667
|
+
end
|
668
|
+
#Internal Server Error
|
669
|
+
if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(60)
|
670
|
+
if messages_array.uniq.size == 1
|
671
|
+
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
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(messages_array.first, response)
|
673
|
+
end
|
674
|
+
end
|
675
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("#{messages_array.join(', ')}", response)
|
676
|
+
end
|
677
|
+
|
678
|
+
#Retryiable Service Error
|
679
|
+
if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(61)
|
680
|
+
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{messages_array.join(', ')}", response)
|
681
|
+
end
|
682
|
+
|
595
683
|
#Request exceeded limit
|
596
684
|
if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(70)
|
597
685
|
raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{messages_array.join(', ')}", response)
|
598
686
|
end
|
599
687
|
|
600
|
-
#Locking contention
|
601
|
-
if codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(50)
|
602
|
-
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{messages_array.join(', ')}", response)
|
603
|
-
end
|
604
|
-
|
605
688
|
#All Errors catch
|
606
689
|
if codes_array.size > 0
|
607
690
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages_array.join(', ')}", response)
|
@@ -609,26 +692,20 @@ module ZuoraAPI
|
|
609
692
|
|
610
693
|
#Zuora REST Query Errors
|
611
694
|
if body["faultcode"].present?
|
612
|
-
|
613
|
-
when "fns:MALFORMED_QUERY"
|
614
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
|
615
|
-
when "fns:REQUEST_EXCEEDED_LIMIT"
|
616
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
|
617
|
-
when "fns:LOCK_COMPETITION"
|
618
|
-
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
|
619
|
-
when "INVALID_SESSION"
|
620
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
|
621
|
-
else
|
622
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{body["faultcode"]}::#{body["faultstring"]}", response)
|
623
|
-
end
|
695
|
+
raise_errors_helper(error: body["faultcode"], message: body["faultstring"], response: response)
|
624
696
|
end
|
625
697
|
|
626
698
|
if body["Errors"].present? || body["errors"].present?
|
627
|
-
|
628
|
-
(
|
629
|
-
(
|
630
|
-
|
631
|
-
|
699
|
+
codes, messages = [[],[]]
|
700
|
+
body.fetch("Errors", []).each { |obj| messages.push(obj["Message"]); messages.push(obj["title"]); codes.push(obj["Code"]); codes.push(obj["code"]) }
|
701
|
+
body.fetch("errors", []).each { |obj| messages.push(obj["Message"]); messages.push(obj["title"]); codes.push(obj["Code"]); codes.push(obj["code"]) }
|
702
|
+
codes, messages = [codes.uniq.compact, messages.uniq.compact]
|
703
|
+
if codes.size > 0
|
704
|
+
if codes.size == 1
|
705
|
+
raise_errors_helper(error: codes.first, message: messages.first, response: response, errors: messages)
|
706
|
+
else
|
707
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{messages.join(", ")}", response, messages)
|
708
|
+
end
|
632
709
|
end
|
633
710
|
end
|
634
711
|
end
|
@@ -646,7 +723,7 @@ module ZuoraAPI
|
|
646
723
|
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("Retry Lock Competition", response)
|
647
724
|
elsif error_messages.first.include?("data integrity violation")
|
648
725
|
raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response)
|
649
|
-
end
|
726
|
+
end
|
650
727
|
end
|
651
728
|
end
|
652
729
|
|
@@ -655,15 +732,128 @@ module ZuoraAPI
|
|
655
732
|
end
|
656
733
|
end
|
657
734
|
|
735
|
+
if body.class == Hash && body['message'].present?
|
736
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(body['message'], response) if response.code == 500
|
737
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['message'], response) if ![200,201].include?(response.code)
|
738
|
+
end
|
739
|
+
|
740
|
+
self.errors_via_content_type(response: response, type: :json)
|
741
|
+
|
658
742
|
#All other errors
|
659
|
-
if ![200,201].include?(response.code)
|
660
|
-
|
661
|
-
|
743
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(response.body, response) if ![200,201].include?(response.code)
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
def errors_via_content_type(response: nil, type: :xml)
|
748
|
+
response_content_types = response.headers.transform_keys(&:downcase).fetch('content-type', []).first || ""
|
749
|
+
|
750
|
+
if response_content_types.include?('application/json') && type != :json
|
751
|
+
output_json = JSON.parse(response.body)
|
752
|
+
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
|
755
|
+
output_xml = Nokogiri::XML(response.body)
|
756
|
+
self.raise_errors(type: :SOAP, body: output_xml, response: response)
|
757
|
+
|
758
|
+
elsif response_content_types.include?('text/html')
|
759
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Akamai Error", response) if response.headers.fetch('server', '') == 'AkamaiGHost'
|
760
|
+
|
761
|
+
parse_body = Nokogiri::HTML(response.body)
|
762
|
+
error_title = parse_body.xpath('//h2').text
|
763
|
+
error_title = parse_body.xpath('//h1').text if error_title.blank?
|
764
|
+
error_message = parse_body.xpath('//p').text
|
765
|
+
|
766
|
+
error_message = error_title if error_message.blank?
|
767
|
+
|
768
|
+
if error_title.present?
|
769
|
+
case error_title
|
770
|
+
when /Service Unavailable/
|
771
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new(error_message, response)
|
772
|
+
when /Client sent a bad request./, /Bad Request/, /403 Forbidden/
|
773
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
|
774
|
+
else
|
775
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Http response body is missing", response) if response.body.blank?
|
780
|
+
end
|
781
|
+
|
782
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(response.body, response) if response.code == 500
|
783
|
+
end
|
784
|
+
|
785
|
+
|
786
|
+
def get_soap_error_and_message(body)
|
787
|
+
error = body.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
|
788
|
+
message = body.xpath('//fns:FaultMessage', 'fns' =>'http://fault.api.zuora.com/').text
|
789
|
+
|
790
|
+
if error.blank? || message.blank?
|
791
|
+
error = body.xpath('//faultcode').text
|
792
|
+
message = body.xpath('//faultstring').text
|
793
|
+
end
|
794
|
+
|
795
|
+
if error.blank? || message.blank?
|
796
|
+
error = body.xpath('//ns1:Code', 'ns1' =>'http://api.zuora.com/').text
|
797
|
+
message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
|
798
|
+
end
|
799
|
+
|
800
|
+
#Update/Create/Delete Calls with multiple requests and responses
|
801
|
+
if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
|
802
|
+
error = []
|
803
|
+
success = []
|
804
|
+
body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
|
805
|
+
if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
|
806
|
+
message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
|
807
|
+
error.push(message)
|
662
808
|
else
|
663
|
-
|
809
|
+
success.push(call.xpath('./ns1:Id', 'ns1' =>'http://api.zuora.com/').text)
|
664
810
|
end
|
665
811
|
end
|
666
812
|
end
|
813
|
+
return error, success, message
|
814
|
+
end
|
815
|
+
|
816
|
+
def raise_errors_helper(error: nil, message: nil, response: nil, errors: [], success: [])
|
817
|
+
case error
|
818
|
+
when /.*INVALID_SESSION/
|
819
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response, errors, success)
|
820
|
+
when /.*REQUEST_EXCEEDED_LIMIT/
|
821
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new(message, response, errors, success)
|
822
|
+
when /.*LOCK_COMPETITION/
|
823
|
+
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(message, response, errors, success)
|
824
|
+
when /.*BATCH_FAIL_ERROR/
|
825
|
+
if message.include?("optimistic locking failed") || message.include?("Operation failed due to a lock competition, please retry later.")
|
826
|
+
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(message, response, errors, success)
|
827
|
+
elsif message.include?("org.hibernate.exception.ConstraintViolationException")
|
828
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
|
829
|
+
end
|
830
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
831
|
+
when /.*TEMPORARY_ERROR/, /.*TRANSACTION_TIMEOUT/
|
832
|
+
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(message, response, errors, success)
|
833
|
+
when /.*INVALID_VALUE/
|
834
|
+
if message.include?("data integrity violation")
|
835
|
+
raise ZuoraAPI::Exceptions::ZuoraDataIntegrity.new("Data Integrity Violation", response, errors), success
|
836
|
+
end
|
837
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
838
|
+
when /.*UNKNOWN_ERROR/
|
839
|
+
if /payment\/refund|Credit Balance Adjustment|Payment Gateway|ARSettlement permission/.match(message).nil?
|
840
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
|
841
|
+
end
|
842
|
+
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/
|
844
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
845
|
+
when /.*UNEXPECTED_ERROR/
|
846
|
+
raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(message, response, errors, success)
|
847
|
+
when /.*soapenv:Server.*/
|
848
|
+
if /^Invalid value.*for type.*|^Id is invalid|^date string can not be less than 19 charactors$/.match(message).present?
|
849
|
+
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?
|
851
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
|
852
|
+
end
|
853
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
|
854
|
+
else
|
855
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Z:#{error}::#{message}", response, errors, success)
|
856
|
+
end
|
667
857
|
end
|
668
858
|
|
669
859
|
def aqua_query(queryName: '', query: '', version: '1.2', jobName: 'Aqua',partner: '', project: '')
|
@@ -743,16 +933,24 @@ module ZuoraAPI
|
|
743
933
|
end
|
744
934
|
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
|
745
935
|
end
|
746
|
-
rescue *(CONNECTION_EXCEPTIONS
|
747
|
-
if
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
936
|
+
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
937
|
+
if tries.zero?
|
938
|
+
if log_errors
|
939
|
+
if Rails.logger.class.to_s == "Ougai::Logger"
|
940
|
+
Rails.logger.error("Describe - Timed out will retry after #{self.timeout_sleep} seconds", ex)
|
941
|
+
else
|
942
|
+
Rails.logger.error("Describe - #{ex.class} Timed out will retry after #{self.timeout_sleep} seconds")
|
943
|
+
end
|
944
|
+
end
|
945
|
+
raise ex
|
753
946
|
end
|
947
|
+
|
948
|
+
tries -= 1
|
949
|
+
sleep(self.timeout_sleep)
|
950
|
+
retry
|
754
951
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
755
|
-
if !
|
952
|
+
if !tries.zero? && self.status == 'Active'
|
953
|
+
tries -= 1
|
756
954
|
Rails.logger.debug("Describe session expired. Starting new session.")
|
757
955
|
self.new_session
|
758
956
|
retry
|
@@ -766,40 +964,84 @@ module ZuoraAPI
|
|
766
964
|
return des_hash
|
767
965
|
end
|
768
966
|
|
769
|
-
def rest_call(
|
967
|
+
def rest_call(
|
968
|
+
method: :get,
|
969
|
+
body: nil,
|
970
|
+
headers: {},
|
971
|
+
url: rest_endpoint("catalog/products?pageSize=4"),
|
972
|
+
debug: false,
|
973
|
+
errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
|
974
|
+
z_session: true,
|
975
|
+
session_type: :basic,
|
976
|
+
timeout_retry: false,
|
977
|
+
timeout: 120,
|
978
|
+
timeout_sleep_interval: self.timeout_sleep,
|
979
|
+
multipart: false,
|
980
|
+
stream_body: false,
|
981
|
+
output_exception_messages: true,
|
982
|
+
**keyword_args,
|
983
|
+
&block
|
984
|
+
)
|
770
985
|
tries ||= 2
|
771
986
|
|
772
|
-
if self.entity_id.present?
|
773
|
-
headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
|
774
|
-
headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
|
775
|
-
end
|
776
|
-
|
777
987
|
raise "Method not supported, supported methods include: :get, :post, :put, :delete, :patch, :head, :options" if ![:get, :post, :put, :delete, :patch, :head, :options].include?(method)
|
778
988
|
|
779
|
-
authentication_headers =
|
989
|
+
authentication_headers = {}
|
990
|
+
if z_session
|
991
|
+
authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type) }
|
992
|
+
if self.entity_id.present?
|
993
|
+
authentication_headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
|
994
|
+
authentication_headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
|
995
|
+
end
|
996
|
+
end
|
997
|
+
|
780
998
|
modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
|
781
999
|
|
782
|
-
response = HTTParty::Request.new("Net::HTTP::#{method.to_s.capitalize}".constantize, url, body: body, headers: modified_headers, timeout: timeout).perform
|
783
|
-
Rails.logger.debug("Response Code: #{response.code}") if debug
|
784
1000
|
begin
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
1001
|
+
request = HTTParty::Request.new(
|
1002
|
+
"Net::HTTP::#{method.to_s.capitalize}".constantize,
|
1003
|
+
url,
|
1004
|
+
body: body,
|
1005
|
+
headers: modified_headers,
|
1006
|
+
timeout: timeout,
|
1007
|
+
multipart: multipart,
|
1008
|
+
stream_body: stream_body
|
1009
|
+
)
|
1010
|
+
|
1011
|
+
response = request.perform(&block)
|
1012
|
+
|
1013
|
+
Rails.logger.debug("Response Code: #{response.code}") if debug
|
1014
|
+
begin
|
1015
|
+
output_json = JSON.parse(response.body)
|
1016
|
+
rescue JSON::ParserError => ex
|
1017
|
+
output_json = {}
|
1018
|
+
end
|
1019
|
+
Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
|
790
1020
|
|
791
|
-
|
1021
|
+
raise_errors(type: :JSON, body: output_json, response: response)
|
1022
|
+
rescue
|
1023
|
+
reset_files(body) if multipart
|
1024
|
+
raise
|
1025
|
+
end
|
792
1026
|
rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
|
793
1027
|
if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
|
794
|
-
|
1028
|
+
session_type = :bearer
|
1029
|
+
retry
|
795
1030
|
else
|
796
1031
|
Rails.logger.debug("Rest Call - Session Bad Auth type")
|
797
1032
|
raise ex
|
798
1033
|
end
|
799
1034
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
800
|
-
if !
|
1035
|
+
if !tries.zero? && z_session
|
1036
|
+
tries -= 1
|
801
1037
|
Rails.logger.debug("Rest Call - Session Invalid #{session_type}")
|
802
|
-
|
1038
|
+
|
1039
|
+
begin
|
1040
|
+
self.new_session(auth_type: session_type)
|
1041
|
+
rescue *ZUORA_API_ERRORS => ex
|
1042
|
+
return [output_json, ex.response]
|
1043
|
+
end
|
1044
|
+
|
803
1045
|
retry
|
804
1046
|
else
|
805
1047
|
if errors.include?(ex.class)
|
@@ -812,32 +1054,46 @@ module ZuoraAPI
|
|
812
1054
|
if errors.include?(ex.class)
|
813
1055
|
raise ex
|
814
1056
|
else
|
1057
|
+
response = ex.response unless response
|
815
1058
|
return [output_json, response]
|
816
1059
|
end
|
817
1060
|
rescue ZuoraAPI::Exceptions::BadEntityError => ex
|
818
1061
|
raise ex
|
819
1062
|
rescue *CONNECTION_EXCEPTIONS => ex
|
820
|
-
if
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
1063
|
+
if tries.zero?
|
1064
|
+
if output_exception_messages
|
1065
|
+
if Rails.logger.class.to_s == "Ougai::Logger"
|
1066
|
+
Rails.logger.error("Rest Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
1067
|
+
else
|
1068
|
+
Rails.logger.error("Rest Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
raise ex
|
826
1072
|
end
|
1073
|
+
|
1074
|
+
tries -= 1
|
1075
|
+
sleep(timeout_sleep_interval)
|
1076
|
+
retry
|
827
1077
|
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
828
|
-
if !
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
1078
|
+
if !tries.zero?
|
1079
|
+
tries -= 1
|
1080
|
+
if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
|
1081
|
+
retry
|
1082
|
+
elsif timeout_retry
|
1083
|
+
sleep(timeout_sleep_interval)
|
1084
|
+
retry
|
1085
|
+
end
|
834
1086
|
end
|
835
|
-
|
836
|
-
if
|
837
|
-
|
838
|
-
|
839
|
-
|
1087
|
+
|
1088
|
+
if output_exception_messages
|
1089
|
+
if Rails.logger.class.to_s == "Ougai::Logger"
|
1090
|
+
Rails.logger.error("Rest Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
1091
|
+
else
|
1092
|
+
Rails.logger.error("Rest Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
1093
|
+
end
|
840
1094
|
end
|
1095
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from #{url}", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
1096
|
+
raise ex
|
841
1097
|
rescue => ex
|
842
1098
|
raise ex
|
843
1099
|
else
|
@@ -890,210 +1146,251 @@ module ZuoraAPI
|
|
890
1146
|
return products, catalog_map
|
891
1147
|
end
|
892
1148
|
|
893
|
-
def get_file(url: nil, headers: {},
|
1149
|
+
def get_file(url: nil, headers: {}, z_session: true, tempfile: true, output_file_name: nil, zuora_track_id: nil, add_timestamp: true, file_path: defined?(Rails.root.join('tmp')) ? Rails.root.join('tmp') : Pathname.new(Dir.pwd), timeout_retries: 3, timeout: 120, session_type: :basic, **execute_params)
|
894
1150
|
raise "file_path must be of class Pathname" if file_path.class != Pathname
|
895
1151
|
|
1152
|
+
retry_count ||= timeout_retries
|
1153
|
+
|
896
1154
|
#Make sure directory exists
|
897
1155
|
require 'fileutils'
|
898
1156
|
FileUtils.mkdir_p(file_path) unless File.exists?(file_path)
|
899
1157
|
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
end
|
1158
|
+
status_code = nil
|
1159
|
+
uri = URI.parse(url)
|
1160
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
1161
|
+
http.read_timeout = timeout #Seconds
|
1162
|
+
http.use_ssl = true if !uri.scheme.nil? && uri.scheme.downcase == 'https'
|
1163
|
+
if z_session
|
1164
|
+
headers = headers.merge({"Authorization" => self.get_session(prefix: true)})
|
1165
|
+
headers = headers.merge({"Zuora-Entity-Ids" => self.entity_id}) if !self.entity_id.blank?
|
1166
|
+
end
|
910
1167
|
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
end
|
929
|
-
self.new_session if z_session
|
930
|
-
return get_file(:url => url, :headers => headers, :count => count - 1, :z_session => z_session, :tempfile => tempfile, :file_path => file_path, :timeout_retries => timeout_retries, :timeout => timeout)
|
1168
|
+
headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
|
1169
|
+
|
1170
|
+
response_save = nil
|
1171
|
+
http.request_get(uri.request_uri, headers) do |response|
|
1172
|
+
response_save = response
|
1173
|
+
status_code = response.code if response
|
1174
|
+
case response
|
1175
|
+
when Net::HTTPOK
|
1176
|
+
headers = {}
|
1177
|
+
response.each_header do |k,v|
|
1178
|
+
headers[k] = v
|
1179
|
+
end
|
1180
|
+
Rails.logger.debug("Headers: #{headers.to_s}")
|
1181
|
+
if output_file_name.present?
|
1182
|
+
file_ending ||= output_file_name.end_with?(".csv.zip") ? ".csv.zip" : File.extname(output_file_name)
|
1183
|
+
filename ||= File.basename(output_file_name, file_ending)
|
1184
|
+
end
|
931
1185
|
|
932
|
-
|
933
|
-
|
1186
|
+
size, export_progress = [0, 0]
|
1187
|
+
encoding, type, full_filename = [nil, nil, nil]
|
1188
|
+
if response.header["Content-Disposition"].present?
|
1189
|
+
case response.header["Content-Disposition"]
|
1190
|
+
when /.*; filename\*=.*/
|
1191
|
+
full_filename ||= /.*; filename\*=(.*)''(.*)/.match(response.header["Content-Disposition"])[2].strip
|
1192
|
+
encoding = /.*; filename\*=(.*)''(.*)/.match(response.header["Content-Disposition"])[1].strip
|
1193
|
+
when /.*; filename=/
|
1194
|
+
full_filename ||= /.*; filename=(.*)/.match(response.header["Content-Disposition"])[1].strip
|
1195
|
+
else
|
1196
|
+
raise "Can't parse Content-Disposition header: #{response.header["Content-Disposition"]}"
|
1197
|
+
end
|
1198
|
+
file_ending ||= full_filename.end_with?(".csv.zip") ? ".csv.zip" : File.extname(full_filename)
|
1199
|
+
filename ||= File.basename(full_filename, file_ending)
|
1200
|
+
end
|
934
1201
|
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
headers[k] = v
|
939
|
-
end
|
940
|
-
Rails.logger.debug("Headers: #{headers.to_s}")
|
941
|
-
if output_file_name.present?
|
942
|
-
file_ending ||= output_file_name.end_with?(".csv.zip") ? ".csv.zip" : File.extname(output_file_name)
|
943
|
-
filename ||= File.basename(output_file_name, file_ending)
|
944
|
-
end
|
1202
|
+
#If user supplied a filename use it, else default to content header filename, else default to uri pattern
|
1203
|
+
file_ending ||= uri.path.end_with?(".csv.zip") ? ".csv.zip" : File.extname(uri.path)
|
1204
|
+
filename ||= File.basename(uri.path, file_ending)
|
945
1205
|
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
raise "Can't parse Content-Disposition header: #{response.header["Content-Disposition"]}"
|
957
|
-
end
|
958
|
-
file_ending ||= full_filename.end_with?(".csv.zip") ? ".csv.zip" : File.extname(full_filename)
|
959
|
-
filename ||= File.basename(full_filename, file_ending)
|
960
|
-
end
|
1206
|
+
if response.header["Content-Type"].present?
|
1207
|
+
case response.header["Content-Type"]
|
1208
|
+
when /.*;charset=.*/
|
1209
|
+
type = /(.*);charset=(.*)/.match(response.header["Content-Type"])[1]
|
1210
|
+
encoding = /(.*);charset=(.*)/.match(response.header["Content-Type"])[2]
|
1211
|
+
else
|
1212
|
+
type = response.header["Content-Type"]
|
1213
|
+
encoding ||= 'UTF-8'
|
1214
|
+
end
|
1215
|
+
end
|
961
1216
|
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
case response.header["Content-Type"]
|
968
|
-
when /.*;charset=.*/
|
969
|
-
type = /(.*);charset=(.*)/.match(response.header["Content-Type"])[1]
|
970
|
-
encoding = /(.*);charset=(.*)/.match(response.header["Content-Type"])[2]
|
971
|
-
else
|
972
|
-
type = response.header["Content-Type"]
|
973
|
-
encoding ||= 'UTF-8'
|
974
|
-
end
|
975
|
-
end
|
1217
|
+
if response.header["Content-Length"].present?
|
1218
|
+
export_size = response.header["Content-Length"].to_i
|
1219
|
+
elsif response.header["ContentLength"].present?
|
1220
|
+
export_size = response.header["ContentLength"].to_i
|
1221
|
+
end
|
976
1222
|
|
977
|
-
|
978
|
-
export_size = response.header["Content-Length"].to_i
|
979
|
-
elsif response.header["ContentLength"].present?
|
980
|
-
export_size = response.header["ContentLength"].to_i
|
981
|
-
end
|
1223
|
+
Rails.logger.info("File: #{filename}#{file_ending} #{encoding} #{type} #{export_size}")
|
982
1224
|
|
983
|
-
|
1225
|
+
file_prefix = add_timestamp ? "#{filename}_#{Time.now.to_i}" : filename
|
1226
|
+
if tempfile
|
1227
|
+
require 'tempfile'
|
1228
|
+
file_handle = ::Tempfile.new([file_prefix, "#{file_ending}"], file_path)
|
1229
|
+
else
|
1230
|
+
file_handle = File.new(file_path.join("#{file_prefix}#{file_ending}"), "w+")
|
1231
|
+
end
|
1232
|
+
file_handle.binmode
|
984
1233
|
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
response.read_body do |chunk|
|
995
|
-
file_handle << chunk
|
996
|
-
|
997
|
-
if defined?(export_size) && export_size != 0 && export_size.class == Integer
|
998
|
-
size += chunk.size
|
999
|
-
new_progress = (size * 100) / export_size
|
1000
|
-
unless new_progress == export_progress
|
1001
|
-
Rails.logger.debug("Login: Export Downloading %s (%3d%%)" % [filename, new_progress])
|
1002
|
-
end
|
1003
|
-
export_progress = new_progress
|
1004
|
-
end
|
1234
|
+
response.read_body do |chunk|
|
1235
|
+
file_handle << chunk
|
1236
|
+
|
1237
|
+
if defined?(export_size) && export_size != 0 && export_size.class == Integer
|
1238
|
+
size += chunk.size
|
1239
|
+
new_progress = (size * 100) / export_size
|
1240
|
+
unless new_progress == export_progress
|
1241
|
+
Rails.logger.debug("Login: Export Downloading %s (%3d%%)" % [filename, new_progress])
|
1005
1242
|
end
|
1243
|
+
export_progress = new_progress
|
1244
|
+
end
|
1245
|
+
end
|
1006
1246
|
|
1007
|
-
|
1008
|
-
|
1247
|
+
file_handle.close
|
1248
|
+
Rails.logger.debug("Filepath: #{file_handle.path} Size: #{File.size(file_handle.path).to_f/1000000} mb")
|
1009
1249
|
|
1010
|
-
|
1250
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Downloaded file is not a file: #{file_handle.class}") if !["Tempfile", "File"].include?(file_handle.class.to_s)
|
1251
|
+
return file_handle
|
1252
|
+
when Net::HTTPUnauthorized
|
1253
|
+
if z_session
|
1254
|
+
if !(retry_count -= 1).zero?
|
1255
|
+
self.new_session
|
1256
|
+
raise response.class
|
1257
|
+
else
|
1258
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
1011
1259
|
end
|
1012
|
-
end
|
1013
|
-
rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS).concat([Net::HTTPBadResponse]) => e
|
1014
|
-
sleep(5)
|
1015
|
-
if (timeout_retries -= 1) >= 0
|
1016
|
-
Rails.logger.warn("Download Failed: #{e.class} : #{e.message}")
|
1017
|
-
retry
|
1018
1260
|
else
|
1019
1261
|
raise
|
1020
1262
|
end
|
1263
|
+
else
|
1264
|
+
raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
|
1021
1265
|
end
|
1022
|
-
|
1023
|
-
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
rescue => ex
|
1269
|
+
sleep(5)
|
1270
|
+
if (retry_count -= 1) >= 0
|
1271
|
+
retry
|
1272
|
+
else
|
1273
|
+
Rails.logger.error("File Download Failed")
|
1024
1274
|
raise
|
1025
1275
|
end
|
1026
1276
|
end
|
1027
1277
|
|
1028
|
-
def getDataSourceExport(query, extract: true, encrypted: false, zip: true)
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1278
|
+
def getDataSourceExport(query, extract: true, encrypted: false, zip: true, z_track_id: "")
|
1279
|
+
begin
|
1280
|
+
tries ||= 3
|
1281
|
+
request = Nokogiri::XML::Builder.new do |xml|
|
1282
|
+
xml['SOAP-ENV'].Envelope('xmlns:SOAP-ENV' => "http://schemas.xmlsoap.org/soap/envelope/", 'xmlns:ns2' => "http://object.api.zuora.com/", 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", 'xmlns:ns1' => "http://api.zuora.com/") do
|
1283
|
+
xml['SOAP-ENV'].Header do
|
1284
|
+
xml['ns1'].SessionHeader do
|
1285
|
+
xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
|
1286
|
+
end
|
1034
1287
|
end
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1288
|
+
xml['SOAP-ENV'].Body do
|
1289
|
+
xml['ns1'].create do
|
1290
|
+
xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
|
1291
|
+
xml['ns2'].Format 'csv'
|
1292
|
+
xml['ns2'].Zip zip
|
1293
|
+
xml['ns2'].Name 'googman'
|
1294
|
+
xml['ns2'].Query query
|
1295
|
+
xml['ns2'].Encrypted encrypted
|
1296
|
+
end
|
1044
1297
|
end
|
1045
1298
|
end
|
1046
1299
|
end
|
1047
1300
|
end
|
1048
|
-
end
|
1049
1301
|
|
1050
|
-
|
1302
|
+
response_query = HTTParty.post(self.url, body: request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8", "Z-Track-Id" => z_track_id}, :timeout => 120)
|
1051
1303
|
|
1052
|
-
|
1053
|
-
|
1054
|
-
id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
|
1304
|
+
output_xml = Nokogiri::XML(response_query.body)
|
1305
|
+
raise_errors(type: :SOAP, body: output_xml, response: response_query) if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
|
1055
1306
|
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1307
|
+
# raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
|
1308
|
+
|
1309
|
+
id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
|
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
|
1061
1317
|
end
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
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
|
1066
1322
|
end
|
1067
1323
|
end
|
1068
1324
|
end
|
1069
|
-
|
1070
|
-
result = 'Waiting'
|
1325
|
+
result = 'Waiting'
|
1071
1326
|
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
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)
|
1075
1330
|
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
raise "Export Creation Unsuccessful : #{output_xml.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text}" if result.blank? || result == "Failed"
|
1080
|
-
end
|
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
|
1081
1334
|
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
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
|
1384
|
+
|
1385
|
+
rescue Errno::ECONNRESET => ex
|
1386
|
+
if !(tries -= 1).zero? && ex.message.include?('SSL_connect')
|
1387
|
+
retry
|
1388
|
+
else
|
1389
|
+
raise ex
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
rescue ZuoraAPI::Exceptions::BadEntityError => ex
|
1393
|
+
raise ex
|
1097
1394
|
end
|
1098
1395
|
end
|
1099
1396
|
|
@@ -1160,5 +1457,17 @@ module ZuoraAPI
|
|
1160
1457
|
end
|
1161
1458
|
return "failure"
|
1162
1459
|
end
|
1460
|
+
|
1461
|
+
def reset_files(body)
|
1462
|
+
return unless body.is_a? Hash
|
1463
|
+
|
1464
|
+
body.transform_values! do |v|
|
1465
|
+
if v.is_a?(File)
|
1466
|
+
v.reopen(v.path)
|
1467
|
+
else
|
1468
|
+
v
|
1469
|
+
end
|
1470
|
+
end
|
1471
|
+
end
|
1163
1472
|
end
|
1164
1473
|
end
|