zuora_api 1.7.06 → 1.7.7a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +4 -3
- data/CHANGELOG.md +17 -0
- data/Gemfile +1 -1
- data/README.md +1 -1
- data/lib/insights_api/login.rb +5 -6
- data/lib/zuora_api/exceptions.rb +118 -29
- data/lib/zuora_api/login.rb +685 -328
- data/lib/zuora_api/logins/basic.rb +11 -93
- data/lib/zuora_api/logins/oauth.rb +38 -76
- data/lib/zuora_api/version.rb +1 -1
- data/zuora_api.gemspec +3 -2
- metadata +24 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2697794114e89b4d76001a4746c1148229aeada880a18ab2cd3a97673549d2c6
|
|
4
|
+
data.tar.gz: 7a66c8c6b0d604bc9d1229424ecca8a557c6c36a84304049970988cbf7e6c771
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7a8ae407cd8d86a1249b404ef27be7ee13e13e8f620ec7cda03d84db3c17c077ead6cbf8f90d6bb8be66fb5594d90adf7184e707a7ac1f1639b84434868fe4f
|
|
7
|
+
data.tar.gz: b9732ae7b0397cc983e87e3459f80fa013c81170f2fd1fec15256a51e79e6722eeac58dcda457eb7d4ec323cbe5efd869094f3abbc29124e737d95689d4150a1
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
image: ruby:2.
|
|
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/"
|
|
52
|
-
- git add
|
|
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"
|
data/CHANGELOG.md
CHANGED
|
@@ -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
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Zuora Gem
|
|
2
2
|
|
|
3
|
-
[](https://badge.fury.io/rb/zuora_api) [](https://badge.fury.io/rb/zuora_api) [](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
|
|
@@ -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.
|
|
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.
|
|
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,
|
|
62
|
-
@code = response.
|
|
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,
|
|
78
|
-
@code = response.
|
|
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,
|
|
94
|
-
@code = response.
|
|
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
|
|
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.
|
|
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,
|
|
126
|
-
@code = response.
|
|
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,
|
|
142
|
-
@code = response.
|
|
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,
|
|
158
|
-
@code = response.
|
|
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
|
|
data/lib/zuora_api/login.rb
CHANGED
|
@@ -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
|
|
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
|
|
45
|
-
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
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[
|
|
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
|
-
|
|
256
|
+
case self.hostname
|
|
257
|
+
when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
|
|
289
258
|
self.environment = 'Sandbox'
|
|
290
|
-
|
|
259
|
+
when /(?<=\.|\/|^)(service[\d]*|services[\d]*|ep-edge)(?=\.|\/|$)/
|
|
291
260
|
self.environment = 'Services'
|
|
292
|
-
|
|
261
|
+
when /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/
|
|
293
262
|
self.environment = 'Performance'
|
|
294
|
-
|
|
263
|
+
when /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/
|
|
295
264
|
self.environment = 'Staging'
|
|
296
|
-
|
|
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",
|
|
320
|
-
"NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "
|
|
321
|
-
"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"},
|
|
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
|
-
|
|
344
|
-
|
|
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
|
-
|
|
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
|
|
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}
|
|
315
|
+
endpoint = "#{https}rest#{host}"
|
|
366
316
|
when 'Staging'
|
|
367
|
-
endpoint = "https://rest-staging2.zuora.com
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
432
|
-
xml["#{ns1}"].
|
|
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
|
-
|
|
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
|
|
473
|
+
raise if skip_session
|
|
474
|
+
if !tries.zero? && z_session
|
|
475
|
+
tries -= 1
|
|
462
476
|
Rails.logger.debug("SOAP Call - Session Invalid")
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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 !
|
|
480
|
-
|
|
481
|
-
|
|
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 !
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
-
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
return
|
|
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
|
|
609
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
|
|
509
610
|
end
|
|
510
611
|
|
|
511
|
-
|
|
512
|
-
|
|
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
|
|
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
|
|
526
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
573
|
-
|
|
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
|
-
|
|
581
|
-
|
|
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['
|
|
606
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
655
|
-
(
|
|
656
|
-
(
|
|
657
|
-
|
|
658
|
-
|
|
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
|
-
|
|
688
|
-
|
|
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
|
-
|
|
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 =>
|
|
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 !
|
|
775
|
-
|
|
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 !
|
|
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:
|
|
1076
|
+
timeout: 130,
|
|
1077
|
+
timeout_sleep_interval: self.timeout_sleep,
|
|
807
1078
|
multipart: false,
|
|
808
|
-
|
|
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 =
|
|
820
|
-
|
|
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
|
-
|
|
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
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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
|
-
|
|
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
|
-
|
|
843
|
-
|
|
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 !
|
|
1138
|
+
if !tries.zero? && z_session
|
|
1139
|
+
tries -= 1
|
|
849
1140
|
Rails.logger.debug("Rest Call - Session Invalid #{session_type}")
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
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
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
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 !
|
|
869
|
-
|
|
870
|
-
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
retry
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
892
|
-
|
|
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(:
|
|
915
|
-
|
|
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:
|
|
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
|
-
|
|
1348
|
+
unless (retry_count -= 1).zero?
|
|
1044
1349
|
self.new_session
|
|
1045
|
-
raise
|
|
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
|
|
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 =>
|
|
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
|
-
|
|
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 =>
|
|
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
|
-
|
|
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(
|
|
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
|