zuora_api 1.6.3a → 1.6.3b
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 +5 -5
- data/.gitlab-ci.yml +9 -5
- data/CHANGELOG.md +26 -0
- data/lib/insights_api/login.rb +1 -1
- data/lib/zuora_api/exceptions.rb +15 -0
- data/lib/zuora_api/login.rb +466 -139
- data/lib/zuora_api/logins/basic.rb +13 -3
- data/lib/zuora_api/logins/oauth.rb +8 -3
- data/lib/zuora_api/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7a5f3d85642928cb6296595c0d644bb39a4a91543d08536ce8cd500cfb848b60
|
|
4
|
+
data.tar.gz: bd2b2670f4997b05ddd9ad8f0be6be3c5376b5b373d68d695491d9d7a22249fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59356375be3c396d05309b29859ece9d961312ad774abeb5f0138f89a29e004371cce349315ec53b8bf16230dd679e106bae18f629a9cadd0522d2c44dfcc096
|
|
7
|
+
data.tar.gz: e2b094fe4d063f7719a2f428290111e20e7e505a709522e71b885f662459534e2eb914c80e1e3d4635f9dc266cabf0d35fcae0b25495a50e4dc56dc8be85734e
|
data/.gitlab-ci.yml
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
image: ruby:2.3.1
|
|
1
|
+
image: ruby:2.6
|
|
3
2
|
stages:
|
|
4
3
|
- setup
|
|
5
4
|
- test
|
|
@@ -41,8 +40,14 @@ rubygems-deploy:
|
|
|
41
40
|
stage: deploy
|
|
42
41
|
allow_failure: false
|
|
43
42
|
script:
|
|
43
|
+
- echo "deb http://ftp.us.debian.org/debian testing main contrib non-free" >> /etc/apt/sources.list
|
|
44
|
+
- apt-get update
|
|
45
|
+
- apt-get install -y git
|
|
46
|
+
- apt-get clean all
|
|
47
|
+
- gem install dpl
|
|
44
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
|
|
45
|
-
- if [[ "master" == $CI_BUILD_REF_SLUG ]];then export VERSION=`git describe --
|
|
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
|
+
- echo $VERSION
|
|
46
51
|
- sed -i "s/0.0.1/$VERSION/" /Connect/zuora-gem/lib/zuora_api/version.rb
|
|
47
52
|
- git add /Connect/zuora-gem/lib/zuora_api/version.rb
|
|
48
53
|
- git config --global user.email "connect@zuora.com"
|
|
@@ -53,7 +58,6 @@ rubygems-deploy:
|
|
|
53
58
|
- version=$(rake install | grep -o 'pkg/zuora_api-.*gem')
|
|
54
59
|
- curl -u $USERNAME:$PASSWORD https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials
|
|
55
60
|
- gem push $version
|
|
56
|
-
- dpl --provider=rubygems --api-key=$API_KEY --gem=zuora_connect
|
|
57
61
|
only:
|
|
58
62
|
- master
|
|
59
|
-
- staging
|
|
63
|
+
- staging
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
## [1.6.28] - 2018-3-12
|
|
5
|
+
### Added
|
|
6
|
+
- Way to avoid force encoding for filedownload
|
|
7
|
+
|
|
8
|
+
## [1.6.22] - 2019-01-03
|
|
9
|
+
### Changed
|
|
10
|
+
- get_identity method - supports ZSession auth now
|
|
11
|
+
- updated rspecs accordingly
|
|
12
|
+
|
|
13
|
+
## [1.6.18] - 2018-12-06
|
|
14
|
+
### Added
|
|
15
|
+
- zconnect_provider attribute accessor for identifying ZConnect cookies
|
|
16
|
+
- Methods for Hallway integration:
|
|
17
|
+
- get_identity
|
|
18
|
+
- get_full_nav
|
|
19
|
+
- set_nav
|
|
20
|
+
- refresh_nav
|
|
21
|
+
- get_oauth_client
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- The way environment and region are set
|
|
25
|
+
|
|
26
|
+
|
data/lib/insights_api/login.rb
CHANGED
data/lib/zuora_api/exceptions.rb
CHANGED
|
@@ -86,6 +86,21 @@ module ZuoraAPI
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
class ZuoraAPITemporaryError < Error
|
|
90
|
+
attr_reader :code, :response
|
|
91
|
+
attr_writer :default_message
|
|
92
|
+
|
|
93
|
+
def initialize(message = nil,response=nil, code =nil)
|
|
94
|
+
@code = code
|
|
95
|
+
@message = message
|
|
96
|
+
@response = response
|
|
97
|
+
@default_message = "There is a temporary error with zuora system."
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def to_s
|
|
101
|
+
@message || @default_message
|
|
102
|
+
end
|
|
103
|
+
end
|
|
89
104
|
|
|
90
105
|
class ZuoraAPIAuthenticationTypeError < Error
|
|
91
106
|
attr_reader :code, :response
|
data/lib/zuora_api/login.rb
CHANGED
|
@@ -4,22 +4,203 @@ require "uri"
|
|
|
4
4
|
|
|
5
5
|
module ZuoraAPI
|
|
6
6
|
class Login
|
|
7
|
-
ENVIRONMENTS = [SANDBOX = 'Sandbox', PRODUCTION = 'Production', PREFORMANCE = 'Preformance', SERVICES = 'Services', UNKNOWN = 'Unknown' ]
|
|
8
|
-
REGIONS = [EU = 'EU', US = 'US' ]
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
ENVIRONMENTS = [SANDBOX = 'Sandbox', PRODUCTION = 'Production', PREFORMANCE = 'Preformance', SERVICES = 'Services', UNKNOWN = 'Unknown', STAGING = 'Staging' ]
|
|
8
|
+
REGIONS = [EU = 'EU', US = 'US', NA = 'NA' ]
|
|
9
|
+
MIN_Endpoint = '91.0'
|
|
10
|
+
XML_SAVE_OPTIONS = Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
|
11
|
+
CONNECTION_EXCEPTIONS = [Net::OpenTimeout, OpenSSL::SSL::SSLError, Errno::ECONNREFUSED, SocketError]
|
|
12
|
+
CONNECTION_READ_EXCEPTIONS = [Net::ReadTimeout, Errno::ECONNRESET, Errno::EPIPE]
|
|
13
|
+
|
|
14
|
+
attr_accessor :region, :url, :wsdl_number, :current_session, :environment, :status, :errors, :current_error, :user_info, :tenant_id, :tenant_name, :entity_id, :timeout_sleep, :hostname, :zconnect_provider
|
|
11
15
|
|
|
12
16
|
def initialize(url: nil, entity_id: nil, session: nil, status: nil, **keyword_args)
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
|
|
18
|
+
# raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
|
|
19
|
+
@url = url.gsub(/(\d{2}\.\d)$/, MIN_Endpoint)
|
|
20
|
+
@hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
|
|
21
|
+
@entity_id = get_entity_id(entity_id: entity_id)
|
|
15
22
|
@errors = Hash.new
|
|
16
23
|
@current_session = session
|
|
17
24
|
@status = status.blank? ? "Active" : status
|
|
18
25
|
@user_info = Hash.new
|
|
26
|
+
self.update_region
|
|
19
27
|
self.update_environment
|
|
28
|
+
self.update_zconnect_provider
|
|
20
29
|
@timeout_sleep = 5
|
|
21
30
|
end
|
|
22
31
|
|
|
32
|
+
def get_identity(cookies)
|
|
33
|
+
zsession = cookies["ZSession"]
|
|
34
|
+
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
35
|
+
begin
|
|
36
|
+
if !zsession.blank?
|
|
37
|
+
response = HTTParty.get("https://#{self.hostname}/apps/v1/identity", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
38
|
+
output_json = JSON.parse(response.body)
|
|
39
|
+
elsif !zconnect_accesstoken.blank?
|
|
40
|
+
code = zconnect_accesstoken.split("#!").last
|
|
41
|
+
encrypted_token, tenant_id = Base64.decode64(code).split(":")
|
|
42
|
+
begin
|
|
43
|
+
body = {'token' => encrypted_token}.to_json
|
|
44
|
+
rescue => ex
|
|
45
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Invalid ZConnect Cookie", {}, 400)
|
|
46
|
+
end
|
|
47
|
+
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/identity", :body => body, :headers => { 'Content-Type' => 'application/json' })
|
|
48
|
+
output_json = JSON.parse(response.body)
|
|
49
|
+
else
|
|
50
|
+
if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
|
|
51
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}", {}, 400)
|
|
52
|
+
else
|
|
53
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present", {}, 400)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
rescue JSON::ParserError => ex
|
|
57
|
+
output_json = {}
|
|
58
|
+
end
|
|
59
|
+
raise_errors(type: :JSON, body: output_json, response: response)
|
|
60
|
+
return output_json
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def get_full_nav(cookies)
|
|
64
|
+
zsession = cookies["ZSession"]
|
|
65
|
+
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
66
|
+
begin
|
|
67
|
+
if !zsession.blank?
|
|
68
|
+
response = HTTParty.get("https://#{self.hostname}/apps/v1/navigation", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
69
|
+
output_json = JSON.parse(response.body)
|
|
70
|
+
elsif !zconnect_accesstoken.blank?
|
|
71
|
+
response = HTTParty.get("https://#{self.hostname}/apps/zconnectsession/navigation", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}",'Content-Type' => 'application/json'})
|
|
72
|
+
output_json = JSON.parse(response.body)
|
|
73
|
+
else
|
|
74
|
+
if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
|
|
75
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}", {}, 400)
|
|
76
|
+
else
|
|
77
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present", {}, 400)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
rescue JSON::ParserError => ex
|
|
81
|
+
output_json = {}
|
|
82
|
+
end
|
|
83
|
+
raise_errors(type: :JSON, body: output_json, response: response)
|
|
84
|
+
return output_json
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def set_nav(state, cookies)
|
|
88
|
+
zsession = cookies["ZSession"]
|
|
89
|
+
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
90
|
+
begin
|
|
91
|
+
if !zsession.blank?
|
|
92
|
+
response = HTTParty.put("https://#{self.hostname}/apps/v1/preference/navigation", :body => state.to_json, :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
93
|
+
output_json = JSON.parse(response.body)
|
|
94
|
+
elsif !zconnect_accesstoken.blank?
|
|
95
|
+
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/navigationstate", :body => state.to_json, :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
|
|
96
|
+
output_json = JSON.parse(response.body)
|
|
97
|
+
else
|
|
98
|
+
if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
|
|
99
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}", {}, 400)
|
|
100
|
+
else
|
|
101
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present", {}, 400)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
rescue JSON::ParserError => ex
|
|
105
|
+
output_json = {}
|
|
106
|
+
end
|
|
107
|
+
raise_errors(type: :JSON, body: output_json, response: response)
|
|
108
|
+
return output_json
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def refresh_nav(cookies)
|
|
112
|
+
zsession = cookies["ZSession"]
|
|
113
|
+
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
114
|
+
begin
|
|
115
|
+
if !zsession.blank?
|
|
116
|
+
response = HTTParty.post("https://#{self.hostname}/apps/v1/navigation/fetch", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
117
|
+
output_json = JSON.parse(response.body)
|
|
118
|
+
elsif !zconnect_accesstoken.blank?
|
|
119
|
+
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/refresh-navbarcache", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
|
|
120
|
+
output_json = JSON.parse(response.body)
|
|
121
|
+
else
|
|
122
|
+
if zconnect_accesstoken.blank? && cookies.keys.any? { |x| x.include? "ZConnect"}
|
|
123
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}", {}, 400)
|
|
124
|
+
else
|
|
125
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present", {}, 400)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
rescue JSON::ParserError => ex
|
|
129
|
+
output_json = {}
|
|
130
|
+
end
|
|
131
|
+
raise_errors(type: :JSON, body: output_json, response: response)
|
|
132
|
+
return output_json
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def get_zconnect_accesstoken(cookies)
|
|
136
|
+
accesstoken = nil
|
|
137
|
+
self.update_zconnect_provider
|
|
138
|
+
if !cookies[self.zconnect_provider].nil? && !cookies[self.zconnect_provider].empty?
|
|
139
|
+
accesstoken = cookies[self.zconnect_provider]
|
|
140
|
+
end
|
|
141
|
+
return accesstoken
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def reporting_url(path)
|
|
145
|
+
map = {"US" => {"Sandbox" => "https://zconnectsandbox.zuora.com/api/rest/v1/",
|
|
146
|
+
"Production" => "https://zconnect.zuora.com/api/rest/v1/",
|
|
147
|
+
"Services"=> ""},
|
|
148
|
+
"EU" => {"Sandbox" => "https://zconnect.sandbox.eu.zuora.com/api/rest/v1/",
|
|
149
|
+
"Production" => "https://zconnect.eu.zuora.com/api/rest/v1/",
|
|
150
|
+
"Services"=> ""},
|
|
151
|
+
"NA" => {"Sandbox" => "https://zconnect.sandbox.na.zuora.com/api/rest/v1/",
|
|
152
|
+
"Production" => "https://zconnect.na.zuora.com/api/rest/v1/",
|
|
153
|
+
"Services"=> ""}
|
|
154
|
+
}
|
|
155
|
+
return map[zuora_client.region][zuora_client.environment].insert(-1, path)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# There are two ways to call this method. The first way is best.
|
|
159
|
+
# 1. Pass in cookies and optionally custom_authorities, name, and description
|
|
160
|
+
# 2. Pass in user_id, entity_ids, client_id, client_secret, and optionally custom_authorities, name, and description
|
|
161
|
+
# https://intranet.zuora.com/confluence/display/Sunburst/Create+an+OAuth+Client+through+API+Gateway#CreateanOAuthClientthroughAPIGateway-ZSession
|
|
162
|
+
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)
|
|
163
|
+
authorization = ""
|
|
164
|
+
new_client_id = SecureRandom.uuid if new_client_id.blank?
|
|
165
|
+
new_client_secret = SecureRandom.hex(10) if new_client_secret.blank?
|
|
166
|
+
|
|
167
|
+
if !cookies.nil?
|
|
168
|
+
authorization = cookies["ZSession"]
|
|
169
|
+
authorization = "ZSession-a3N2w #{authorization}"
|
|
170
|
+
if entity_ids.blank? && cookies["ZuoraCurrentEntity"].present?
|
|
171
|
+
entity_ids = Array(cookies["ZuoraCurrentEntity"].unpack("a8a4a4a4a12").join('-'))
|
|
172
|
+
else
|
|
173
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Zuora Entity ID not provided", {}, 400)
|
|
174
|
+
end
|
|
175
|
+
if user_id.blank? && cookies["Zuora-User-Id"].present?
|
|
176
|
+
user_id = cookies["Zuora-User-Id"]
|
|
177
|
+
else
|
|
178
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Zuora User ID not provided", {}, 400)
|
|
179
|
+
end
|
|
180
|
+
elsif !client_id.nil? && !client_secret.nil?
|
|
181
|
+
bearer_response = HTTParty.post("https://#{self.hostname}/oauth/token", :headers => {'Content-Type' => 'application/x-www-form-urlencoded', 'Accept' => 'application/json'}, :body => {'client_id' => client_id, 'client_secret' => URI::encode(client_secret), 'grant_type' => 'client_credentials'})
|
|
182
|
+
bearer_hash = JSON.parse(bearer_response.body)
|
|
183
|
+
bearer_token = bearer_hash["access_token"]
|
|
184
|
+
authorization = "Bearer #{bearer_token}"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
if !authorization.blank? && !user_id.blank? && !entity_ids.blank?
|
|
188
|
+
endpoint = self.rest_endpoint("genesis/clients")
|
|
189
|
+
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)
|
|
190
|
+
output_json = JSON.parse(oauth_response.body)
|
|
191
|
+
if oauth_response.code == 201
|
|
192
|
+
output_json["clientSecret"] = new_client_secret
|
|
193
|
+
return output_json
|
|
194
|
+
elsif oauth_response.code == 401 && !oauth_response.message.blank?
|
|
195
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(output_json["message"], {}, oauth_response.code)
|
|
196
|
+
else
|
|
197
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(output_json["error"], {}, oauth_response.code)
|
|
198
|
+
end
|
|
199
|
+
else
|
|
200
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Insufficient credentials provided", {}, 400)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
23
204
|
def self.environments
|
|
24
205
|
%w(Sandbox Production Services Performance Staging)
|
|
25
206
|
end
|
|
@@ -32,7 +213,8 @@ module ZuoraAPI
|
|
|
32
213
|
return {"US" => {"Sandbox" => "https://apisandbox.zuora.com/apps/services/a/",
|
|
33
214
|
"Production" => "https://www.zuora.com/apps/services/a/",
|
|
34
215
|
"Performance" => "https://pt1.zuora.com/apps/services/a/",
|
|
35
|
-
"Services" => "https://services347.zuora.com/apps/services/a/"
|
|
216
|
+
"Services" => "https://services347.zuora.com/apps/services/a/",
|
|
217
|
+
"Staging" => "https://staging2.zuora.com/apps/services/a/"},
|
|
36
218
|
"EU" => {"Sandbox" => "https://sandbox.eu.zuora.com/apps/services/a/",
|
|
37
219
|
"Production" => "https://eu.zuora.com/apps/services/a/",
|
|
38
220
|
"Performance" => "https://pt1.eu.zuora.com/apps/services/a/",
|
|
@@ -44,43 +226,119 @@ module ZuoraAPI
|
|
|
44
226
|
}
|
|
45
227
|
end
|
|
46
228
|
|
|
229
|
+
def get_entity_id(entity_id: nil)
|
|
230
|
+
if entity_id.present?
|
|
231
|
+
entity_match = /[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}$/.match(entity_id)
|
|
232
|
+
if entity_match.blank?
|
|
233
|
+
raise "Entity length is wrong." if entity_id.length != 32
|
|
234
|
+
part_one, part_two, part_three, part_four, part_five = [entity_id[0..7], entity_id[8..11], entity_id[12..15], entity_id[16..19], entity_id[20..31]]
|
|
235
|
+
entity_id = "#{part_one}-#{part_two}-#{part_three}-#{part_four}-#{part_five}"
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
return entity_id
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def update_region
|
|
242
|
+
if !self.hostname.blank?
|
|
243
|
+
if /(?<=\.|\/|^)(eu)(?=\.|\/|$)/ === self.hostname
|
|
244
|
+
self.region = "EU"
|
|
245
|
+
elsif /(?<=\.|\/|^)(na)(?=\.|\/|$)/ === self.hostname
|
|
246
|
+
self.region = "NA"
|
|
247
|
+
else
|
|
248
|
+
self.region = "US"
|
|
249
|
+
end
|
|
250
|
+
else # This will never happen
|
|
251
|
+
# raise "Can't update region because URL is blank"
|
|
252
|
+
self.region = "Unknown"
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
47
256
|
def update_environment
|
|
48
257
|
if !self.url.blank?
|
|
49
|
-
|
|
50
|
-
self.region = self.url.include?("eu.") ? "EU" : self.url.include?("na.") ? "NA" : "US"
|
|
51
|
-
if env_path == 'apisandbox' || self.url.include?('sandbox')
|
|
258
|
+
if /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/ === self.hostname
|
|
52
259
|
self.environment = 'Sandbox'
|
|
53
|
-
elsif
|
|
54
|
-
self.environment = 'Production'
|
|
55
|
-
elsif env_path.include?('service') || env_path.include?('ep-edge')
|
|
260
|
+
elsif /(?<=\.|\/|^)(service|services[\d]*|ep-edge)(?=\.|\/|$)/ === self.hostname
|
|
56
261
|
self.environment = 'Services'
|
|
57
|
-
elsif
|
|
262
|
+
elsif /(?<=\.|\/|-|^)(pt[\d]*)(?=\.|\/|-|$)/ === self.hostname
|
|
58
263
|
self.environment = 'Performance'
|
|
59
|
-
elsif
|
|
60
|
-
self.region = 'US'
|
|
264
|
+
elsif /(?<=\.|\/|^)(staging1|staging2|stg)(?=\.|\/|$)/ === self.hostname
|
|
61
265
|
self.environment = 'Staging'
|
|
266
|
+
elsif is_prod_env
|
|
267
|
+
self.environment = 'Production'
|
|
62
268
|
else
|
|
63
269
|
self.environment = 'Unknown'
|
|
64
270
|
end
|
|
271
|
+
else # this will never happen
|
|
272
|
+
raise "Can't determine environment from blank URL"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def is_prod_env
|
|
277
|
+
is_prod = false
|
|
278
|
+
www_or_api = /(?<=\.|\/|^)(www|api)(?=\.|\/|$)/ === self.hostname
|
|
279
|
+
host_prefix_match = /(^|tls10\.|origin-www\.|zforsf\.|eu\.|na\.)(zuora\.com)/ === self.hostname
|
|
280
|
+
if www_or_api || host_prefix_match
|
|
281
|
+
is_prod = true
|
|
65
282
|
end
|
|
283
|
+
return is_prod
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def update_zconnect_provider
|
|
287
|
+
region = update_region
|
|
288
|
+
environment = update_environment
|
|
289
|
+
mappings = {"US" => {"Sandbox" => "ZConnectSbx", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev", "Services" => "ZConnectQA", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Staging" => "ZConnectQA"},
|
|
290
|
+
"NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectQANA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
|
|
291
|
+
"EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectQAEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU"},
|
|
292
|
+
"Unknown" => {"Unknown" => "Unknown"}}
|
|
293
|
+
self.zconnect_provider = mappings[region][environment]
|
|
294
|
+
# raise "Can't find ZConnect Provider for #{region} region and #{environment} environment" if self.zconnect_provider.nil?
|
|
66
295
|
end
|
|
67
296
|
|
|
68
297
|
def aqua_endpoint(url="")
|
|
69
|
-
|
|
298
|
+
match = /.*(\/apps\/)/.match(self.url)
|
|
299
|
+
if !match.nil?
|
|
300
|
+
url_slash_apps_slash = match[0]
|
|
301
|
+
else
|
|
302
|
+
raise "self.url has no /apps in it"
|
|
303
|
+
end
|
|
304
|
+
return "#{url_slash_apps_slash}api/#{url}"
|
|
70
305
|
end
|
|
71
306
|
|
|
72
307
|
def rest_endpoint(url="")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
308
|
+
update_environment
|
|
309
|
+
endpoint = url
|
|
310
|
+
|
|
311
|
+
case self.environment
|
|
312
|
+
when 'Sandbox'
|
|
313
|
+
case self.region
|
|
314
|
+
when 'US'
|
|
315
|
+
endpoint = "https://rest.apisandbox.zuora.com/v1/".concat(url)
|
|
316
|
+
when 'EU'
|
|
317
|
+
endpoint = "https://rest.sandbox.eu.zuora.com/v1/".concat(url)
|
|
318
|
+
when 'NA'
|
|
319
|
+
endpoint = "https://rest.sandbox.na.zuora.com/v1/".concat(url)
|
|
320
|
+
end
|
|
321
|
+
when 'Production'
|
|
322
|
+
case self.region
|
|
323
|
+
when 'US'
|
|
324
|
+
endpoint = "https://rest.zuora.com/v1/".concat(url)
|
|
325
|
+
when 'EU'
|
|
326
|
+
endpoint = "https://rest.eu.zuora.com/v1/".concat(url)
|
|
327
|
+
when 'NA'
|
|
328
|
+
endpoint = "https://rest.na.zuora.com/v1/".concat(url)
|
|
329
|
+
end
|
|
330
|
+
when 'Performance'
|
|
331
|
+
endpoint = "https://rest.pt1.zuora.com/v1/".concat(url)
|
|
332
|
+
when 'Services'
|
|
333
|
+
https = /https:\/\/|http:\/\//.match(self.url)[0]
|
|
334
|
+
host = self.hostname
|
|
335
|
+
endpoint = "#{https}#{host}/apps/v1/#{url}"
|
|
336
|
+
when 'Staging'
|
|
337
|
+
endpoint = "https://rest-staging2.zuora.com/".concat(url)
|
|
338
|
+
when 'Unknown'
|
|
339
|
+
raise "Environment unknown, returning passed in parameter unaltered"
|
|
83
340
|
end
|
|
341
|
+
return endpoint
|
|
84
342
|
end
|
|
85
343
|
|
|
86
344
|
def fileURL(url="")
|
|
@@ -91,15 +349,14 @@ module ZuoraAPI
|
|
|
91
349
|
return self.wsdl_number > 68 ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'
|
|
92
350
|
end
|
|
93
351
|
|
|
94
|
-
def new_session(auth_type: :basic, debug: false)
|
|
352
|
+
def new_session(auth_type: :basic, debug: false)
|
|
95
353
|
end
|
|
96
354
|
|
|
97
355
|
def get_session(prefix: false, auth_type: :basic)
|
|
98
|
-
Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}")
|
|
356
|
+
Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}") if Rails.env.to_s == 'development'
|
|
99
357
|
case auth_type
|
|
100
358
|
when :basic
|
|
101
359
|
if self.current_session.blank?
|
|
102
|
-
Rails.logger.debug("Create new session")
|
|
103
360
|
case self.class.to_s
|
|
104
361
|
when 'ZuoraAPI::Oauth'
|
|
105
362
|
if self.bearer_token.blank? || self.oauth_expired?
|
|
@@ -184,7 +441,15 @@ module ZuoraAPI
|
|
|
184
441
|
else
|
|
185
442
|
return output_xml, input_xml, response
|
|
186
443
|
end
|
|
187
|
-
rescue
|
|
444
|
+
rescue *CONNECTION_EXCEPTIONS => ex
|
|
445
|
+
if !(tries -= 1).zero?
|
|
446
|
+
Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
|
|
447
|
+
sleep(self.timeout_sleep)
|
|
448
|
+
retry
|
|
449
|
+
else
|
|
450
|
+
raise ex
|
|
451
|
+
end
|
|
452
|
+
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
|
188
453
|
if !(tries -= 1).zero? && timeout_retry
|
|
189
454
|
Rails.logger.info("SOAP Call - #{ex.class} Timed out will retry after 5 seconds")
|
|
190
455
|
sleep(self.timeout_sleep)
|
|
@@ -219,8 +484,7 @@ module ZuoraAPI
|
|
|
219
484
|
error = []
|
|
220
485
|
success = []
|
|
221
486
|
body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').each_with_index do |call, object_index|
|
|
222
|
-
|
|
223
|
-
if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false'
|
|
487
|
+
if call.xpath('./ns1:Success', 'ns1' =>'http://api.zuora.com/').text == 'false' && call.xpath('./ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
|
|
224
488
|
message = "#{call.xpath('./*/ns1:Code', 'ns1' =>'http://api.zuora.com/').text}::#{call.xpath('./*/ns1:Message', 'ns1' =>'http://api.zuora.com/').text}"
|
|
225
489
|
error.push(message)
|
|
226
490
|
else
|
|
@@ -232,22 +496,28 @@ module ZuoraAPI
|
|
|
232
496
|
#By default response if not passed in for SOAP as all SOAP is 200
|
|
233
497
|
if error.present?
|
|
234
498
|
if error.class == String
|
|
235
|
-
|
|
499
|
+
case error
|
|
500
|
+
when "INVALID_SESSION"
|
|
236
501
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{error}::#{message}", body, response.code )
|
|
237
|
-
|
|
238
|
-
if error == "REQUEST_EXCEEDED_LIMIT"
|
|
502
|
+
when "REQUEST_EXCEEDED_LIMIT"
|
|
239
503
|
raise ZuoraAPI::Exceptions::ZuoraAPIRequestLimit.new("#{error}::#{message}", body, response.code)
|
|
240
|
-
|
|
241
|
-
if error == "LOCK_COMPETITION"
|
|
504
|
+
when "LOCK_COMPETITION"
|
|
242
505
|
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", body, response.code)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
506
|
+
when "BATCH_FAIL_ERROR"
|
|
507
|
+
if message.include?("optimistic locking failed")
|
|
508
|
+
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("#{error}::#{message}", body, response.code)
|
|
509
|
+
else
|
|
510
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", body, response.code)
|
|
511
|
+
end
|
|
512
|
+
when "TEMPORARY_ERROR"
|
|
513
|
+
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new("#{error}::#{message}", body, response.code)
|
|
514
|
+
else
|
|
515
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{error}::#{message}", body, response.code) if error.present?
|
|
246
516
|
end
|
|
247
517
|
elsif error.class == Array
|
|
248
518
|
if error[0].include?("LOCK_COMPETITION") && error.count == 1
|
|
249
519
|
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new(error.group_by {|v| v}.map {|k,v| "(#{v.size}x) - #{k == "::" ? 'UNKNOWN::No error provided' : k}"}.join(', '), body, response.code)
|
|
250
|
-
else
|
|
520
|
+
else
|
|
251
521
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(error.group_by {|v| v}.map {|k,v| "(#{v.size}x) - #{k == "::" ? 'UNKNOWN::No error provided' : k}"}.join(', '), body, response.code, error, success)
|
|
252
522
|
end
|
|
253
523
|
end
|
|
@@ -258,6 +528,7 @@ module ZuoraAPI
|
|
|
258
528
|
end
|
|
259
529
|
|
|
260
530
|
when :JSON
|
|
531
|
+
body = body.dig("results").present? ? body["results"] : body if body.class == Hash
|
|
261
532
|
if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
|
|
262
533
|
messages_array = (body["reasons"] || []).map {|error| error['message']}.compact
|
|
263
534
|
codes_array = (body["reasons"] || []).map {|error| error['code']}.compact
|
|
@@ -266,6 +537,10 @@ module ZuoraAPI
|
|
|
266
537
|
raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new()
|
|
267
538
|
end
|
|
268
539
|
|
|
540
|
+
if body['errorMessage']
|
|
541
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['errorMessage'],body,response.code)
|
|
542
|
+
end
|
|
543
|
+
|
|
269
544
|
if body.dig("reasons").nil? ? false : body.dig("reasons")[0].dig("code") == 90000020
|
|
270
545
|
raise ZuoraAPI::Exceptions::BadEntityError.new("#{messages_array.join(', ')}", body, response.code)
|
|
271
546
|
end
|
|
@@ -324,11 +599,18 @@ module ZuoraAPI
|
|
|
324
599
|
end
|
|
325
600
|
end
|
|
326
601
|
|
|
327
|
-
#Zuora REST Actions error (Create, Update, Delete)
|
|
602
|
+
#Zuora REST Actions error (Create, Update, Delete, Amend)
|
|
328
603
|
if body.class == Array
|
|
329
604
|
all_errors = body.select {|obj| !obj['Success'] || !obj['success'] }.map {|obj| obj['Errors'] || obj['errors'] }.compact
|
|
330
605
|
all_success = body.select {|obj| obj['Success'] || obj['success']}.compact
|
|
331
606
|
|
|
607
|
+
if all_success.blank? && all_errors.present?
|
|
608
|
+
error_codes = all_errors.flatten.group_by {|error| error['Code']}.keys.uniq
|
|
609
|
+
if error_codes.size == 1 && error_codes[0] == "LOCK_COMPETITION"
|
|
610
|
+
raise ZuoraAPI::Exceptions::ZuoraAPILockCompetition.new("Retry Lock Competition", body, response.code)
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
|
332
614
|
if all_errors.size > 0
|
|
333
615
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("#{all_errors.flatten.group_by {|error| error['Message']}.keys.uniq.join(' ')}", body, response.code, all_errors, all_success )
|
|
334
616
|
end
|
|
@@ -388,9 +670,11 @@ module ZuoraAPI
|
|
|
388
670
|
|
|
389
671
|
base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
|
|
390
672
|
url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
|
|
391
|
-
headers =
|
|
673
|
+
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"}
|
|
392
674
|
response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 120)
|
|
393
675
|
|
|
676
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401' ) if response.code == 401
|
|
677
|
+
|
|
394
678
|
output_xml = Nokogiri::XML(response.body)
|
|
395
679
|
des_hash = Hash.new
|
|
396
680
|
if object == nil
|
|
@@ -420,7 +704,7 @@ module ZuoraAPI
|
|
|
420
704
|
end
|
|
421
705
|
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
|
|
422
706
|
end
|
|
423
|
-
rescue
|
|
707
|
+
rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS) => ex
|
|
424
708
|
if !(tries -= 1).zero?
|
|
425
709
|
Rails.logger.info("Describe - #{ex.class} Timed out will retry after 5 seconds")
|
|
426
710
|
sleep(self.timeout_sleep)
|
|
@@ -428,17 +712,30 @@ module ZuoraAPI
|
|
|
428
712
|
else
|
|
429
713
|
raise ex
|
|
430
714
|
end
|
|
715
|
+
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
716
|
+
if !(tries -= 1).zero?
|
|
717
|
+
Rails.logger.info("Session expired. Starting new session.")
|
|
718
|
+
self.new_session
|
|
719
|
+
retry
|
|
720
|
+
else
|
|
721
|
+
raise ex
|
|
722
|
+
end
|
|
431
723
|
rescue => ex
|
|
432
724
|
raise ex
|
|
433
725
|
else
|
|
434
726
|
return des_hash
|
|
435
727
|
end
|
|
436
728
|
|
|
437
|
-
def rest_call(method: :get, body: nil,headers: {}, url: rest_endpoint("catalog/products?pageSize=4"), debug: false, errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError, ZuoraAPI::Exceptions::ZuoraAPIError, ZuoraAPI::Exceptions::ZuoraAPIRequestLimit, ZuoraAPI::Exceptions::ZuoraAPILockCompetition], z_session: true, session_type: :basic, timeout_retry: false, timeout: 120, **keyword_args)
|
|
729
|
+
def rest_call(method: :get, body: nil, headers: {}, url: rest_endpoint("catalog/products?pageSize=4"), debug: false, errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError, ZuoraAPI::Exceptions::ZuoraAPIError, ZuoraAPI::Exceptions::ZuoraAPIRequestLimit, ZuoraAPI::Exceptions::ZuoraAPILockCompetition], z_session: true, session_type: :basic, timeout_retry: false, timeout: 120, **keyword_args)
|
|
438
730
|
tries ||= 2
|
|
439
|
-
|
|
731
|
+
|
|
732
|
+
if self.entity_id.present?
|
|
733
|
+
headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
|
|
734
|
+
headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
|
|
735
|
+
end
|
|
736
|
+
|
|
440
737
|
raise "Method not supported, supported methods include: :get, :post, :put, :delete, :patch, :head, :options" if ![:get, :post, :put, :delete, :patch, :head, :options].include?(method)
|
|
441
|
-
|
|
738
|
+
|
|
442
739
|
authentication_headers = z_session ? {"Authorization" => self.get_session(prefix: true, auth_type: session_type) } : {}
|
|
443
740
|
headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(headers).merge(authentication_headers)
|
|
444
741
|
|
|
@@ -455,7 +752,7 @@ module ZuoraAPI
|
|
|
455
752
|
rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
|
|
456
753
|
if self.class.to_s == 'ZuoraAPI::Oauth'
|
|
457
754
|
self.rest_call(method: method.to_sym, url: url, debug: debug, errors: errors, z_session: z_session, session_type: :bearer, timeout_retry: timeout_retry, timeout: timeout)
|
|
458
|
-
else
|
|
755
|
+
else
|
|
459
756
|
Rails.logger.debug("Rest Call - Session Bad Auth type")
|
|
460
757
|
raise ex
|
|
461
758
|
end
|
|
@@ -479,7 +776,15 @@ module ZuoraAPI
|
|
|
479
776
|
end
|
|
480
777
|
rescue ZuoraAPI::Exceptions::BadEntityError => ex
|
|
481
778
|
raise ex
|
|
482
|
-
rescue
|
|
779
|
+
rescue *CONNECTION_EXCEPTIONS => ex
|
|
780
|
+
if !(tries -= 1).zero?
|
|
781
|
+
Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
|
|
782
|
+
sleep(self.timeout_sleep)
|
|
783
|
+
retry
|
|
784
|
+
else
|
|
785
|
+
raise ex
|
|
786
|
+
end
|
|
787
|
+
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
|
483
788
|
if !(tries -= 1).zero? && timeout_retry
|
|
484
789
|
Rails.logger.info("Rest Call - #{ex.class} Timed out will retry after 5 seconds")
|
|
485
790
|
sleep(self.timeout_sleep)
|
|
@@ -539,7 +844,7 @@ module ZuoraAPI
|
|
|
539
844
|
return products, catalog_map
|
|
540
845
|
end
|
|
541
846
|
|
|
542
|
-
def get_file(url: nil, headers: {}, count: 3, z_session: true, tempfile: true, file_path: defined?(Rails.root.join('tmp')) ? Rails.root.join('tmp') : Pathname.new(Dir.pwd), timeout_retries: 2, timeout: 120, **execute_params)
|
|
847
|
+
def get_file(url: nil, headers: {}, count: 3, 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: 2, timeout: 120, session_type: :basic, force_encoding: true, **execute_params)
|
|
543
848
|
raise "file_path must be of class Pathname" if file_path.class != Pathname
|
|
544
849
|
|
|
545
850
|
#Make sure directory exists
|
|
@@ -552,100 +857,126 @@ module ZuoraAPI
|
|
|
552
857
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
553
858
|
http.read_timeout = timeout #Seconds
|
|
554
859
|
http.use_ssl = true if uri.scheme.downcase == 'https'
|
|
555
|
-
|
|
860
|
+
if z_session
|
|
861
|
+
headers = headers.merge({"Authorization" => self.get_session(prefix: true)})
|
|
862
|
+
headers = headers.merge({"Zuora-Entity-Ids" => self.entity_id}) if !self.entity_id.blank?
|
|
863
|
+
end
|
|
556
864
|
|
|
557
865
|
response_save = nil
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
return get_file(:url => url, :headers => headers, :count => count, :z_session => z_session, :tempfile => tempfile, :file_path => file_path, :timeout_retries => timeout_retries - 1, :timeout => timeout)
|
|
576
|
-
|
|
577
|
-
when Net::HTTPClientError
|
|
578
|
-
Rails.logger.fatal("Login: #{self.username} Export")
|
|
579
|
-
raise response
|
|
580
|
-
|
|
581
|
-
when Net::HTTPOK
|
|
582
|
-
headers = {}
|
|
583
|
-
response.each_header do |k,v|
|
|
584
|
-
headers[k] = v
|
|
585
|
-
end
|
|
586
|
-
Rails.logger.debug("Headers: #{headers.to_s}")
|
|
587
|
-
|
|
588
|
-
size, export_progress = [0, 0]
|
|
589
|
-
encoding, type, full_filename = [nil, nil, nil]
|
|
590
|
-
if response.header["Content-Disposition"].present?
|
|
591
|
-
case response.header["Content-Disposition"]
|
|
592
|
-
when /.*; filename\*=.*/
|
|
593
|
-
full_filename = /.*; filename\*=(.*)''(.*)/.match(response.header["Content-Disposition"])[2].strip
|
|
594
|
-
encoding = /.*; filename\*=(.*)''(.*)/.match(response.header["Content-Disposition"])[1].strip
|
|
595
|
-
when /.*; filename=/
|
|
596
|
-
full_filename = /.*; filename=(.*)/.match(response.header["Content-Disposition"])[1].strip
|
|
866
|
+
begin
|
|
867
|
+
http.request_get(uri.request_uri, headers) do |response|
|
|
868
|
+
response_save = response
|
|
869
|
+
status_code = response.code if response
|
|
870
|
+
|
|
871
|
+
case response
|
|
872
|
+
when Net::HTTPNotFound
|
|
873
|
+
Rails.logger.fatal("404 - Not Found")
|
|
874
|
+
raise
|
|
875
|
+
|
|
876
|
+
when Net::HTTPUnauthorized
|
|
877
|
+
if count <= 0
|
|
878
|
+
if z_session
|
|
879
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
|
880
|
+
else
|
|
881
|
+
raise
|
|
882
|
+
end
|
|
597
883
|
end
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
884
|
+
Rails.logger.fatal("Unauthorized: Retry")
|
|
885
|
+
self.new_session if z_session
|
|
886
|
+
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, :force_encoding => force_encoding)
|
|
887
|
+
|
|
888
|
+
when Net::HTTPClientError
|
|
889
|
+
raise
|
|
890
|
+
|
|
891
|
+
when Net::HTTPOK
|
|
892
|
+
headers = {}
|
|
893
|
+
response.each_header do |k,v|
|
|
894
|
+
headers[k] = v
|
|
895
|
+
end
|
|
896
|
+
Rails.logger.debug("Headers: #{headers.to_s}")
|
|
897
|
+
if output_file_name.present?
|
|
898
|
+
file_ending ||= output_file_name.end_with?(".csv.zip") ? ".csv.zip" : File.extname(output_file_name)
|
|
899
|
+
filename ||= File.basename(output_file_name, file_ending)
|
|
611
900
|
end
|
|
612
|
-
end
|
|
613
|
-
Rails.logger.info("File: #{filename}#{file_ending} #{encoding} #{type}")
|
|
614
901
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
902
|
+
size, export_progress = [0, 0]
|
|
903
|
+
encoding, type, full_filename = [nil, nil, nil]
|
|
904
|
+
if response.header["Content-Disposition"].present?
|
|
905
|
+
case response.header["Content-Disposition"]
|
|
906
|
+
when /.*; filename\*=.*/
|
|
907
|
+
full_filename ||= /.*; filename\*=(.*)''(.*)/.match(response.header["Content-Disposition"])[2].strip
|
|
908
|
+
encoding = /.*; filename\*=(.*)''(.*)/.match(response.header["Content-Disposition"])[1].strip
|
|
909
|
+
when /.*; filename=/
|
|
910
|
+
full_filename ||= /.*; filename=(.*)/.match(response.header["Content-Disposition"])[1].strip
|
|
911
|
+
else
|
|
912
|
+
raise "Can't parse Content-Disposition header: #{response.header["Content-Disposition"]}"
|
|
913
|
+
end
|
|
914
|
+
file_ending ||= full_filename.end_with?(".csv.zip") ? ".csv.zip" : File.extname(full_filename)
|
|
915
|
+
filename ||= File.basename(full_filename, file_ending)
|
|
916
|
+
end
|
|
620
917
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
918
|
+
#If user supplied a filename use it, else default to content header filename, else default to uri pattern
|
|
919
|
+
file_ending ||= uri.path.end_with?(".csv.zip") ? ".csv.zip" : File.extname(uri.path)
|
|
920
|
+
filename ||= File.basename(uri.path, file_ending)
|
|
921
|
+
|
|
922
|
+
if response.header["Content-Type"].present?
|
|
923
|
+
case response.header["Content-Type"]
|
|
924
|
+
when /.*;charset=.*/
|
|
925
|
+
type = /(.*);charset=(.*)/.match(response.header["Content-Type"])[1]
|
|
926
|
+
encoding = /(.*);charset=(.*)/.match(response.header["Content-Type"])[2]
|
|
927
|
+
else
|
|
928
|
+
type = response.header["Content-Type"]
|
|
929
|
+
encoding ||= 'UTF-8'
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
if response.header["Content-Length"].present?
|
|
934
|
+
export_size = response.header["Content-Length"].to_i
|
|
935
|
+
elsif response.header["ContentLength"].present?
|
|
936
|
+
export_size = response.header["ContentLength"].to_i
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
Rails.logger.info("File: #{filename}#{file_ending} #{encoding} #{type} #{export_size}")
|
|
940
|
+
|
|
941
|
+
file_prefix = add_timestamp ? "#{filename}_#{Time.now.to_i}" : filename
|
|
942
|
+
if tempfile
|
|
943
|
+
require 'tempfile'
|
|
944
|
+
file_handle = ::Tempfile.new([file_prefix, "#{file_ending}"], file_path)
|
|
945
|
+
else
|
|
946
|
+
file_handle = File.new(file_path.join("#{file_prefix}#{file_ending}"), "w+")
|
|
947
|
+
end
|
|
629
948
|
file_handle.binmode
|
|
630
|
-
end
|
|
631
949
|
|
|
632
|
-
|
|
633
|
-
|
|
950
|
+
response.read_body do |chunk|
|
|
951
|
+
if force_encoding
|
|
952
|
+
file_handle << chunk.force_encoding(encoding)
|
|
953
|
+
else
|
|
954
|
+
file_handle << chunk
|
|
955
|
+
end
|
|
634
956
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
957
|
+
if defined?(export_size) && export_size != 0 && export_size.class == Integer
|
|
958
|
+
size += chunk.size
|
|
959
|
+
new_progress = (size * 100) / export_size
|
|
960
|
+
unless new_progress == export_progress
|
|
961
|
+
Rails.logger.debug("Login: Export Downloading %s (%3d%%)" % [filename, new_progress])
|
|
962
|
+
end
|
|
963
|
+
export_progress = new_progress
|
|
640
964
|
end
|
|
641
|
-
export_progress = new_progress
|
|
642
965
|
end
|
|
643
|
-
end
|
|
644
966
|
|
|
645
|
-
|
|
646
|
-
|
|
967
|
+
file_handle.close
|
|
968
|
+
Rails.logger.debug("Filepath: #{file_handle.path} Size: #{File.size(file_handle.path).to_f/1000000} mb")
|
|
647
969
|
|
|
648
|
-
|
|
970
|
+
return file_handle
|
|
971
|
+
end
|
|
972
|
+
end
|
|
973
|
+
rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS).concat([Net::HTTPBadResponse]) => e
|
|
974
|
+
sleep(5)
|
|
975
|
+
if (timeout_retries -= 1) >= 0
|
|
976
|
+
Rails.logger.warn("Download Failed: #{e.class} : #{e.message}")
|
|
977
|
+
retry
|
|
978
|
+
else
|
|
979
|
+
raise
|
|
649
980
|
end
|
|
650
981
|
end
|
|
651
982
|
rescue Exception => e
|
|
@@ -656,8 +987,6 @@ module ZuoraAPI
|
|
|
656
987
|
end
|
|
657
988
|
|
|
658
989
|
def getDataSourceExport(query, extract: true, encrypted: false, zip: true)
|
|
659
|
-
Rails.logger.debug("Build export")
|
|
660
|
-
Rails.logger.debug("#{query}")
|
|
661
990
|
request = Nokogiri::XML::Builder.new do |xml|
|
|
662
991
|
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
|
|
663
992
|
xml['SOAP-ENV'].Header do
|
|
@@ -680,7 +1009,7 @@ module ZuoraAPI
|
|
|
680
1009
|
end
|
|
681
1010
|
|
|
682
1011
|
response_query = HTTParty.post(self.url, body: request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8"}, :timeout => 120)
|
|
683
|
-
|
|
1012
|
+
|
|
684
1013
|
output_xml = Nokogiri::XML(response_query.body)
|
|
685
1014
|
raise 'Export Creation Unsuccessful : ' + output_xml.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text if output_xml.xpath('//ns1:Success', 'ns1' =>'http://api.zuora.com/').text != "true"
|
|
686
1015
|
id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
|
|
@@ -704,7 +1033,7 @@ module ZuoraAPI
|
|
|
704
1033
|
while result != "Completed"
|
|
705
1034
|
sleep 3
|
|
706
1035
|
response_query = HTTParty.post(self.url, body: confirmRequest.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8"}, :timeout => 120)
|
|
707
|
-
|
|
1036
|
+
|
|
708
1037
|
output_xml = Nokogiri::XML(response_query.body)
|
|
709
1038
|
result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
|
|
710
1039
|
status_code = response_query.code if response_query
|
|
@@ -712,7 +1041,6 @@ module ZuoraAPI
|
|
|
712
1041
|
end
|
|
713
1042
|
|
|
714
1043
|
file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
|
|
715
|
-
Rails.logger.debug('=====> Export finished')
|
|
716
1044
|
export_file = get_file(:url => self.fileURL(file_id))
|
|
717
1045
|
export_file_path = export_file.path
|
|
718
1046
|
Rails.logger.debug("=====> Export path #{export_file.path}")
|
|
@@ -731,7 +1059,6 @@ module ZuoraAPI
|
|
|
731
1059
|
end
|
|
732
1060
|
|
|
733
1061
|
def query(query, parse = false)
|
|
734
|
-
Rails.logger.debug("Querying Zuora for #{query}")
|
|
735
1062
|
output_xml, input_xml = self.soap_call({:debug => false, :timeout_retry => true}) do |xml|
|
|
736
1063
|
xml['ns1'].query do
|
|
737
1064
|
xml['ns1'].queryString query
|
|
@@ -756,7 +1083,7 @@ module ZuoraAPI
|
|
|
756
1083
|
response = Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
|
|
757
1084
|
http.request req
|
|
758
1085
|
end
|
|
759
|
-
|
|
1086
|
+
|
|
760
1087
|
Rails.logger.debug("Response #{response.code} #{response.message}: #{response.body}")
|
|
761
1088
|
|
|
762
1089
|
result = JSON.parse(response.body)
|
|
@@ -27,11 +27,11 @@ module ZuoraAPI
|
|
|
27
27
|
|
|
28
28
|
input_xml = Nokogiri::XML(request.to_xml(:save_with => XML_SAVE_OPTIONS).strip)
|
|
29
29
|
input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
|
|
30
|
-
Rails.logger.debug('Connect') {"Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}"} if debug
|
|
30
|
+
Rails.logger.debug('Connect') {"Request Code: #{@response_query.code} SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}"} if debug
|
|
31
31
|
|
|
32
32
|
@response_query = HTTParty.post(self.url,:body => request.to_xml(:save_with => XML_SAVE_OPTIONS).strip, :headers => {'Content-Type' => "text/xml; charset=utf-8"}, :timeout => 10)
|
|
33
33
|
@output_xml = Nokogiri::XML(@response_query.body)
|
|
34
|
-
Rails.logger.debug('Connect') {"Response SOAP XML: #{@output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}"} if debug
|
|
34
|
+
Rails.logger.debug('Connect') {"Response Code: #{@response_query.code} SOAP XML: #{@output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}"} if debug
|
|
35
35
|
|
|
36
36
|
if !@response_query.success?
|
|
37
37
|
self.current_session = nil
|
|
@@ -70,6 +70,9 @@ module ZuoraAPI
|
|
|
70
70
|
self.status = 'Locked'
|
|
71
71
|
self.current_error = 'Locked user login, please wait or navigate to Zuora to unlock user'
|
|
72
72
|
self.errors[:username] = self.current_error
|
|
73
|
+
elsif self.current_error.include?('Entity not exist:')
|
|
74
|
+
self.status = 'Entity Missing'
|
|
75
|
+
self.errors[:base] = self.current_error
|
|
73
76
|
else
|
|
74
77
|
self.status = 'Unknown'
|
|
75
78
|
self.current_error = @output_xml.xpath('//faultstring').text if self.current_error.blank?
|
|
@@ -85,10 +88,11 @@ module ZuoraAPI
|
|
|
85
88
|
#Username & password combo
|
|
86
89
|
retrieved_session = @output_xml.xpath('//ns1:Session', 'ns1' =>'http://api.zuora.com/').text
|
|
87
90
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("No session found for api call.") if retrieved_session.blank?
|
|
91
|
+
self.status = 'Active'
|
|
88
92
|
self.current_session = retrieved_session
|
|
89
93
|
end
|
|
90
94
|
return self.status
|
|
91
|
-
rescue
|
|
95
|
+
rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS) => ex
|
|
92
96
|
if !(tries -= 1).zero?
|
|
93
97
|
Rails.logger.info {"#{ex.class} Timed out will retry after 5 seconds"}
|
|
94
98
|
sleep(self.timeout_sleep)
|
|
@@ -98,6 +102,12 @@ module ZuoraAPI
|
|
|
98
102
|
self.status = 'Timeout'
|
|
99
103
|
return self.status
|
|
100
104
|
end
|
|
105
|
+
rescue EOFError
|
|
106
|
+
if self.url.match?(/.*services\d{1,}.zuora.com*/)
|
|
107
|
+
self.current_error = "Services tenant '#{self.url.scan(/.*\/\/(services\d{1,}).zuora.com*/).last.first}' is no longer available."
|
|
108
|
+
self.status = 'Not Available'
|
|
109
|
+
return self.status
|
|
110
|
+
end
|
|
101
111
|
end
|
|
102
112
|
end
|
|
103
113
|
end
|
|
@@ -24,9 +24,14 @@ module ZuoraAPI
|
|
|
24
24
|
return self.status
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def get_active_bearer_token
|
|
28
|
+
self.get_bearer_token if self.oauth_expired?
|
|
29
|
+
return self.bearer_token
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
def get_z_session(debug: false)
|
|
28
33
|
tries ||= 2
|
|
29
|
-
headers = self.entity_id.present? ? {
|
|
34
|
+
headers = self.entity_id.present? ? {"Zuora-Entity-Ids" => self.entity_id } : {}
|
|
30
35
|
output_json, response = self.rest_call(:url => self.rest_endpoint("connections"), :session_type => :bearer, :headers => headers)
|
|
31
36
|
self.current_session = response.headers.to_h['set-cookie'][0].split(';')[0].split('=',2)[1].gsub('%3D', '=')
|
|
32
37
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
@@ -47,7 +52,7 @@ module ZuoraAPI
|
|
|
47
52
|
else
|
|
48
53
|
return [output_json, response]
|
|
49
54
|
end
|
|
50
|
-
rescue
|
|
55
|
+
rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS) => ex
|
|
51
56
|
if !(tries -= 1).zero?
|
|
52
57
|
Rails.logger.info {"#{ex.class} Timed out will retry after 5 seconds"}
|
|
53
58
|
sleep(self.timeout_sleep)
|
|
@@ -87,7 +92,7 @@ module ZuoraAPI
|
|
|
87
92
|
else
|
|
88
93
|
return [output_json, response]
|
|
89
94
|
end
|
|
90
|
-
rescue
|
|
95
|
+
rescue *(CONNECTION_EXCEPTIONS).concat(CONNECTION_READ_EXCEPTIONS) => ex
|
|
91
96
|
if !(tries -= 1).zero?
|
|
92
97
|
Rails.logger.info {"#{ex.class} Timed out will retry after 5 seconds"}
|
|
93
98
|
sleep(self.timeout_sleep)
|
data/lib/zuora_api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: zuora_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.3b
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zuora Strategic Solutions Group
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-05-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -153,6 +153,7 @@ files:
|
|
|
153
153
|
- ".gitlab-ci.yml"
|
|
154
154
|
- ".rspec"
|
|
155
155
|
- ".travis.yml"
|
|
156
|
+
- CHANGELOG.md
|
|
156
157
|
- Gemfile
|
|
157
158
|
- Gemfile.lock
|
|
158
159
|
- README.md
|
|
@@ -185,8 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
185
186
|
- !ruby/object:Gem::Version
|
|
186
187
|
version: 1.3.1
|
|
187
188
|
requirements: []
|
|
188
|
-
|
|
189
|
-
rubygems_version: 2.6.8
|
|
189
|
+
rubygems_version: 3.0.3
|
|
190
190
|
signing_key:
|
|
191
191
|
specification_version: 4
|
|
192
192
|
summary: Gem that provides easy integration to Zuora
|