zuora_api 1.7.66 → 1.7.80
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 +2 -1
- data/.gitlab-ci.yml +17 -17
- data/Gemfile +1 -1
- data/catalog-info.yaml +12 -0
- data/docs/index.md +147 -0
- data/gemfiles/Gemfile-rails.5.0.x +5 -0
- data/gemfiles/Gemfile-rails.5.1.x +5 -0
- data/gemfiles/Gemfile-rails.5.2.x +5 -0
- data/gemfiles/Gemfile-rails.6.0.x +5 -0
- data/lib/zuora_api/exceptions.rb +16 -2
- data/lib/zuora_api/login.rb +328 -314
- data/lib/zuora_api/logins/basic.rb +10 -101
- data/lib/zuora_api/logins/oauth.rb +24 -84
- data/lib/zuora_api/version.rb +1 -1
- data/zuora_api.gemspec +4 -3
- metadata +30 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0865872f720e280a3a4805d314d27a3e2649b861495f80cf8523f4eb43f2c1e
|
|
4
|
+
data.tar.gz: cc80008e33f6b47245acc1a0b8cdb0ec326f4a714c842e434b25bd5643e94123
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 051c3a5244695357f0ecf6a42ee1eda7981c0145ed3b50d9e0b751dbbdf6e4aba1d7e36e69eec28056e2fb3b56888c48113699d32ab0f6479272e339b3c1ade2
|
|
7
|
+
data.tar.gz: f20c6fd7e534de55b5b94120e812ba19e18fda5f41e63f8f9183fb66d8ea4a0a01f441918d84196b2aa027724228b63332f54aea94b9515142534d5fff15574c
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
image: ruby:2.
|
|
1
|
+
image: ruby:2.7
|
|
2
2
|
stages:
|
|
3
|
-
- setup
|
|
4
3
|
- test
|
|
5
4
|
- deploy
|
|
6
5
|
|
|
7
|
-
setup:
|
|
8
|
-
stage: setup
|
|
9
|
-
allow_failure: true
|
|
10
|
-
cache:
|
|
11
|
-
key: gems
|
|
12
|
-
paths:
|
|
13
|
-
- vendor/bundle
|
|
14
|
-
script:
|
|
15
|
-
- apt-get update -qy
|
|
16
|
-
- apt-get install -y nodejs
|
|
17
|
-
- bundle install
|
|
18
|
-
|
|
19
6
|
rubocop-testing:
|
|
20
7
|
stage: test
|
|
21
8
|
allow_failure: true
|
|
@@ -30,11 +17,24 @@ security-testing:
|
|
|
30
17
|
- gem install brakeman
|
|
31
18
|
- brakeman
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
ruby:test:
|
|
34
21
|
stage: test
|
|
22
|
+
cache:
|
|
23
|
+
key: ruby:$RUBY_VERSION-rails:$RAILS_VERSION
|
|
24
|
+
paths:
|
|
25
|
+
- vendor/ruby
|
|
26
|
+
parallel:
|
|
27
|
+
matrix:
|
|
28
|
+
- RUBY_VERSION: "2.7"
|
|
29
|
+
RAILS_VERSION: ["5.0", "5.1", "5.2", "6.0"]
|
|
30
|
+
before_script:
|
|
31
|
+
- bundle config set path 'vendor/ruby'
|
|
32
|
+
- bundle config --global gemfile "gemfiles/Gemfile-rails.$RAILS_VERSION.x"
|
|
33
|
+
- bundle install
|
|
35
34
|
script:
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
- bundle exec rails -v
|
|
36
|
+
- bundle exec rspec
|
|
37
|
+
coverage: '/\(\d+.\d+\%\) covered/'
|
|
38
38
|
|
|
39
39
|
rubygems-deploy:
|
|
40
40
|
stage: deploy
|
data/Gemfile
CHANGED
data/catalog-info.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
apiVersion: backstage.io/v1alpha1
|
|
3
|
+
kind: Component
|
|
4
|
+
metadata:
|
|
5
|
+
annotations:
|
|
6
|
+
backstage.io/techdocs-ref: "gitlab:https://gitlab.zeta.tools/extension-products/shared-libraries/zuora-gem.git"
|
|
7
|
+
description: "Zuora API Rails Gem"
|
|
8
|
+
name: Zuora-Gem
|
|
9
|
+
spec:
|
|
10
|
+
lifecycle: production
|
|
11
|
+
owner: connect@zuora.com
|
|
12
|
+
type: library
|
data/docs/index.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Zuora Gem
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/zuora_api) [](https://gitlab.0.ecc.auw2.zuora/extension-products/shared-libraries/zuora-gem/commits/master)
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
Add this line to your application's Gemfile:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
gem 'zuora_api'
|
|
10
|
+
```
|
|
11
|
+
Then execute `bundle install` in your terminal
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Zuora Login Object
|
|
16
|
+
In order to make API calls a Zuora Login object must be created
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
zuora_client = ZuoraAPI::Login.new(username: "username", password: "password", url: "url")
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| Name | Type | Description | Example |
|
|
23
|
+
| ------------------- | ----------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| username | `Attribute` | Username to the Zuora environment | `zuora_client.username = "username"` |
|
|
25
|
+
| password | `Attribute` | password to the Zuora environment | `zuora_client.password = "Password"` |
|
|
26
|
+
| url | `Attribute` | Endpoint to the Zuora tenant | `zuora_client.url = "www.zuora.com"` |
|
|
27
|
+
| wsdl_number | `Attribute` | WSDL number of the zuora login | `wsdl = zuora_client.wsdl_number` |
|
|
28
|
+
| status | `Attribute` | Status of the login | `zuora_client.status` |
|
|
29
|
+
| current_session | `Attribute` | Current session for the login | `zuora_client.current_session` |
|
|
30
|
+
| environment | `Attribute` | environment of the login | `zuora_client.environment` |
|
|
31
|
+
| errors | `Attribute` | Any errors that the login has based on the login call | `zuora_client.errors` |
|
|
32
|
+
| current_error | `Attribute` | Current error from the new_session call | `zuora_client.current_error` |
|
|
33
|
+
| user_info | `Attribute` | Information related to the login | `zuora_client.user_info` |
|
|
34
|
+
| tenant_id | `Attribute` | Tenant ID the login is associated to | `zuora_client.tenant_id` |
|
|
35
|
+
| tenant_name | `Attribute` | Tenant Name of tenant the login is associated to | `zuora_client.tenant_name` |
|
|
36
|
+
| entity_id | `Attribute` | Current entity the login session is associated to | `zuora_client.entity_id` |
|
|
37
|
+
| rest_call | `Method` | Executes a REST call | `zuora_client.rest_call()` |
|
|
38
|
+
| soap_call | `Method` | Executes a SOAP call | `output_xml, input_xml = zuora_client.soap_call() do `|xml, args|` xml['ns1'].query do xml['ns1'].queryString "select id, name from account" end end` |
|
|
39
|
+
| query | `Method` | Executes a query call | `zuora_client.query("select id, name from account")` |
|
|
40
|
+
| getDataSourceExport | `Method` | Pulls a data source export with the given query and returns the file location | `zuora_client.getDataSourceExport("select id, name from account")` |
|
|
41
|
+
| describe_call | `Method` | Performs the describe call against the Zuora tenant for all objects or a specific object | `response = zuora_client.describe_call("Account")` |
|
|
42
|
+
| createJournalRun | `Method` | Creates a Journal Run | `zuora_client.createJournalRun(call)` |
|
|
43
|
+
| checkJRStatus | `Method` | Checks the status of a journal run | `zuora_client.checkJRStatus(journal_run_id)` |
|
|
44
|
+
| update_environment | `Method` | Sets the login's environment based on the url | `zuora_client.update_environment` |
|
|
45
|
+
| aqua_endpoint | `Method` | Returns the AQuA endpoint for the login based off the environment | `zuora_client.aqua_endpoint` |
|
|
46
|
+
| rest_endpoint | `Method` | Returns the REST endpoint for the login based off the environment | `zuora_client.rest_endpoint` |
|
|
47
|
+
| fileURL | `Method` | Returns the URL for files | `zuora_client.fileURL` |
|
|
48
|
+
| dateFormat | `Method` | Returns the data format syntax based on the wsdl_number | `zuora_client.dateFormat` |
|
|
49
|
+
| new_session | `Method` | Create a new session | `zuora_client.new_session` |
|
|
50
|
+
| get_session | `Method` | Returns the current session | `zuora_client.get_session`|
|
|
51
|
+
|
|
52
|
+
## Rest Call
|
|
53
|
+
```ruby
|
|
54
|
+
zuora_client.rest_call(method: :get, body: {}, url: zuora_client.rest_endpoint("catalog/products?pageSize=4"))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Soap Call
|
|
58
|
+
Returns both output and input XML
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
zuora_client.soap_call(ns1: 'ns1', ns2: 'ns2', batch_size: nil, single_transaction: false)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Example Call
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
output_xml, input_xml = zuora_client.soap_call() do |xml, args|
|
|
68
|
+
xml['ns1'].query do
|
|
69
|
+
xml['ns1'].queryString "select id, name from account"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
### Query
|
|
74
|
+
```ruby
|
|
75
|
+
zuora_client.query("select id from account")
|
|
76
|
+
```
|
|
77
|
+
### Data Export
|
|
78
|
+
Returns the file location of the data source export after downloading from Zuora
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
zuora_client.getDataSourceExport("select id from account")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
| Name | Description | Default | Example |
|
|
85
|
+
| --------- | ---------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------- |
|
|
86
|
+
| query | The query to execute | `N/A` | `zuora_client.getDataSourceExport("select id from account")` |
|
|
87
|
+
| zip | Indicates if the data source export should be a zip | `true` | `zuora_client.getDataSourceExport("select id from account", zip: false)` |
|
|
88
|
+
| extract | Indicates if the data source export should be extracted if it is a zip | `true` | `zuora_client.getDataSourceExport("select id from account", extract: false)` |
|
|
89
|
+
| encrypted | Indicates if the data source export should be encrypted | `false` | `zuora_client.getDataSourceExport("select id from account", encrypted: true)` |
|
|
90
|
+
|
|
91
|
+
### Describe Call
|
|
92
|
+
This returns all available objects from the describe call as a hash. This response can be accessed by using response["Account"] to retrieve all related data about that object.
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
response = zuora_client.describe_call("Account")
|
|
96
|
+
```
|
|
97
|
+
This returns all information and fields related to that object model as a hash.
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
response = zuora_client.describe_call()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Journal Run
|
|
104
|
+
```ruby
|
|
105
|
+
zuora_client.createJournalRun(call)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Insights API
|
|
109
|
+
|
|
110
|
+
In order to make API calls a Zuora Login object must be created by running:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
insightsapi = InsightsAPI::Login.new(api_token: "api token", url: "Nw1.api.insights.zuora.com/api/")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Note that the login will default to the insights production url.
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
Date format: "YYYY-MM-DDT00:00:00Z"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Uploading Data into Insights
|
|
123
|
+
```ruby
|
|
124
|
+
insightsapi.upload_into_insights(dataSourceName, recordType, batchDate, filePath)
|
|
125
|
+
```
|
|
126
|
+
dataSourceName: What system the data is coming from.
|
|
127
|
+
recordType: The type of records ie: "EVENTS, ATTRIBUTES, and METRICS"
|
|
128
|
+
batachDate: The date the data applies to.
|
|
129
|
+
|
|
130
|
+
### Describing Insights Data
|
|
131
|
+
```ruby
|
|
132
|
+
insightsapi.describe(type: "ACCOUNT/USER", object: "ATTRIBUTES/EVENTS/SEGMENTS/METRICS")
|
|
133
|
+
```
|
|
134
|
+
Returns json payload describing attributes, events, metrics for each Account or User.
|
|
135
|
+
|
|
136
|
+
### Downloading Data from Insights
|
|
137
|
+
```ruby
|
|
138
|
+
insightsapi.data_export_insights(objecttype, segmentuuid, startDate: nil, endDate: nil, tries: 30)
|
|
139
|
+
```
|
|
140
|
+
```ruby
|
|
141
|
+
insightsapi.data_export_insights_file(objecttype, segmentuuid, startDate: nil, endDate: nil, tries: 30)
|
|
142
|
+
```
|
|
143
|
+
Both do the same thing except one returns a url(data_export_insights) to download the file yourself and the other returns an actual Ruby temporary file(data_export_insights_file).
|
|
144
|
+
|
|
145
|
+
objectype: "ACCOUNT/USER"
|
|
146
|
+
|
|
147
|
+
segmentuuid: A single or array of string or int of a segment uuid(s) that you get from the describe call. The csv holds a column with a bool that represents if that User or Account belongs to that segment.
|
data/lib/zuora_api/exceptions.rb
CHANGED
|
@@ -40,6 +40,19 @@ module ZuoraAPI
|
|
|
40
40
|
def to_s
|
|
41
41
|
@message || @default_message
|
|
42
42
|
end
|
|
43
|
+
|
|
44
|
+
def parse_message(message)
|
|
45
|
+
case message
|
|
46
|
+
when /^Invalid Oauth Client Id$/, /^Unable to generate token.$/
|
|
47
|
+
@message = "Invalid login, please check client ID and Client Secret or URL endpoint"
|
|
48
|
+
when /^Forbidden$/
|
|
49
|
+
@message = "The user associated to OAuth credential set has been deactivated."
|
|
50
|
+
when /^Invalid login. User name and password do not match.$/
|
|
51
|
+
@message = "Invalid login, please check username and password or URL endpoint"
|
|
52
|
+
else
|
|
53
|
+
@message = message
|
|
54
|
+
end
|
|
55
|
+
end
|
|
43
56
|
end
|
|
44
57
|
|
|
45
58
|
class BadEntityError < Error
|
|
@@ -177,14 +190,15 @@ module ZuoraAPI
|
|
|
177
190
|
end
|
|
178
191
|
|
|
179
192
|
class ZuoraAPITemporaryError < Error
|
|
180
|
-
attr_reader :code, :response
|
|
193
|
+
attr_reader :code, :response, :errors
|
|
181
194
|
attr_writer :default_message
|
|
182
195
|
|
|
183
|
-
def initialize(message = nil,response=nil, errors = [], successes = [], *args)
|
|
196
|
+
def initialize(message = nil, response = nil, errors = [], successes = [], *args)
|
|
184
197
|
@code = response.class.to_s == "HTTParty::Response" ? response.code : nil
|
|
185
198
|
@message = parse_message(message)
|
|
186
199
|
@response = response
|
|
187
200
|
@default_message = "There is a temporary error with zuora system."
|
|
201
|
+
@errors = errors
|
|
188
202
|
end
|
|
189
203
|
|
|
190
204
|
def to_s
|
data/lib/zuora_api/login.rb
CHANGED
|
@@ -7,7 +7,7 @@ module ZuoraAPI
|
|
|
7
7
|
class Login
|
|
8
8
|
ENVIRONMENTS = [TEST = 'Test', SANDBOX = 'Sandbox', PRODUCTION = 'Production', PREFORMANCE = 'Preformance', SERVICES = 'Services', UNKNOWN = 'Unknown', STAGING = 'Staging' ]
|
|
9
9
|
REGIONS = [EU = 'EU', US = 'US', NA = 'NA' ]
|
|
10
|
-
|
|
10
|
+
MIN_Endpoints = {'Test': '107.0', 'Sandbox': '107.0', 'Production': '107.0', 'Performance': '107.0', 'Services': '96.0', 'Unknown': '96.0', 'Staging': '107.0'}.freeze
|
|
11
11
|
XML_SAVE_OPTIONS = Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
|
12
12
|
|
|
13
13
|
CONNECTION_EXCEPTIONS = [
|
|
@@ -39,8 +39,8 @@ module ZuoraAPI
|
|
|
39
39
|
|
|
40
40
|
ZUORA_SERVER_ERRORS = [
|
|
41
41
|
ZuoraAPI::Exceptions::ZuoraAPIInternalServerError,
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout,
|
|
43
|
+
ZuoraAPI::Exceptions::ZuoraAPIReadTimeout,
|
|
44
44
|
ZuoraAPI::Exceptions::ZuoraUnexpectedError
|
|
45
45
|
].freeze
|
|
46
46
|
|
|
@@ -50,10 +50,12 @@ module ZuoraAPI
|
|
|
50
50
|
raise "URL is nil or empty, but URL is required" if url.nil? || url.empty?
|
|
51
51
|
# raise "URL is improper. URL must contain zuora.com, zuora.eu, or zuora.na" if /zuora.com|zuora.eu|zuora.na/ === url
|
|
52
52
|
self.hostname = /(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url)[0] if !/(?<=https:\/\/|http:\/\/)(.*?)(?=\/|$)/.match(url).nil?
|
|
53
|
+
self.update_environment
|
|
54
|
+
min_endpoint = MIN_Endpoints[self.environment.to_sym]
|
|
53
55
|
if !/apps\/services\/a\/\d+\.\d$/.match(url.strip)
|
|
54
|
-
self.url = "https://#{hostname}/apps/services/a/#{
|
|
55
|
-
elsif
|
|
56
|
-
self.url = url.gsub(/(\d+\.\d)$/,
|
|
56
|
+
self.url = "https://#{hostname}/apps/services/a/#{min_endpoint}"
|
|
57
|
+
elsif min_endpoint.to_f > url.scan(/(\d+\.\d)$/).dig(0,0).to_f
|
|
58
|
+
self.url = url.gsub(/(\d+\.\d)$/, min_endpoint)
|
|
57
59
|
else
|
|
58
60
|
self.url = url
|
|
59
61
|
end
|
|
@@ -65,36 +67,18 @@ module ZuoraAPI
|
|
|
65
67
|
self.status = status.blank? ? "Active" : status
|
|
66
68
|
self.user_info = Hash.new
|
|
67
69
|
self.update_region
|
|
68
|
-
self.update_environment
|
|
69
70
|
self.update_zconnect_provider
|
|
70
71
|
@timeout_sleep = 5
|
|
71
72
|
end
|
|
72
73
|
|
|
73
74
|
def get_identity(cookies)
|
|
74
75
|
zsession = cookies["ZSession"]
|
|
75
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
76
76
|
begin
|
|
77
77
|
if !zsession.blank?
|
|
78
78
|
response = HTTParty.get("https://#{self.hostname}/apps/v1/identity", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
79
79
|
output_json = JSON.parse(response.body)
|
|
80
|
-
elsif zconnect_accesstoken.present?
|
|
81
|
-
begin
|
|
82
|
-
code = zconnect_accesstoken.split("#!").last
|
|
83
|
-
encrypted_token, tenant_id = Base64.decode64(code).split(":")
|
|
84
|
-
body = {'token' => encrypted_token}.to_json
|
|
85
|
-
rescue => ex
|
|
86
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Invalid ZConnect Cookie")
|
|
87
|
-
end
|
|
88
|
-
Rails.logger.info("Using ZConnect cookie in get_identity method")
|
|
89
|
-
|
|
90
|
-
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/identity", :body => body, :headers => { 'Content-Type' => 'application/json' })
|
|
91
|
-
output_json = JSON.parse(response.body)
|
|
92
80
|
else
|
|
93
|
-
|
|
94
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
|
95
|
-
else
|
|
96
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
97
|
-
end
|
|
81
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
98
82
|
end
|
|
99
83
|
rescue JSON::ParserError => ex
|
|
100
84
|
output_json = {}
|
|
@@ -105,21 +89,12 @@ module ZuoraAPI
|
|
|
105
89
|
|
|
106
90
|
def get_full_nav(cookies)
|
|
107
91
|
zsession = cookies["ZSession"]
|
|
108
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
109
92
|
begin
|
|
110
93
|
if zsession.present?
|
|
111
94
|
response = HTTParty.get("https://#{self.hostname}/apps/v1/navigation", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
112
95
|
output_json = JSON.parse(response.body)
|
|
113
|
-
elsif zconnect_accesstoken.present?
|
|
114
|
-
Rails.logger.info("Using ZConnect cookie in get_full_nav method")
|
|
115
|
-
response = HTTParty.get("https://#{self.hostname}/apps/zconnectsession/navigation", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}",'Content-Type' => 'application/json'})
|
|
116
|
-
output_json = JSON.parse(response.body)
|
|
117
96
|
else
|
|
118
|
-
|
|
119
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
|
120
|
-
else
|
|
121
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
122
|
-
end
|
|
97
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
123
98
|
end
|
|
124
99
|
rescue JSON::ParserError => ex
|
|
125
100
|
output_json = {}
|
|
@@ -130,21 +105,12 @@ module ZuoraAPI
|
|
|
130
105
|
|
|
131
106
|
def set_nav(state, cookies)
|
|
132
107
|
zsession = cookies["ZSession"]
|
|
133
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
134
108
|
begin
|
|
135
109
|
if !zsession.blank?
|
|
136
110
|
response = HTTParty.put("https://#{self.hostname}/apps/v1/preference/navigation", :body => state.to_json, :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
137
111
|
output_json = JSON.parse(response.body)
|
|
138
|
-
elsif !zconnect_accesstoken.blank?
|
|
139
|
-
Rails.logger.info("Using ZConnect cookie in set_nav method")
|
|
140
|
-
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/navigationstate", :body => state.to_json, :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
|
|
141
|
-
output_json = JSON.parse(response.body)
|
|
142
112
|
else
|
|
143
|
-
|
|
144
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
|
145
|
-
else
|
|
146
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
147
|
-
end
|
|
113
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
148
114
|
end
|
|
149
115
|
rescue JSON::ParserError => ex
|
|
150
116
|
output_json = {}
|
|
@@ -155,21 +121,12 @@ module ZuoraAPI
|
|
|
155
121
|
|
|
156
122
|
def refresh_nav(cookies)
|
|
157
123
|
zsession = cookies["ZSession"]
|
|
158
|
-
zconnect_accesstoken = get_zconnect_accesstoken(cookies)
|
|
159
124
|
begin
|
|
160
125
|
if !zsession.blank?
|
|
161
126
|
response = HTTParty.post("https://#{self.hostname}/apps/v1/navigation/fetch", :headers => {'Cookie' => "ZSession=#{zsession}", 'Content-Type' => 'application/json'})
|
|
162
127
|
output_json = JSON.parse(response.body)
|
|
163
|
-
elsif !zconnect_accesstoken.blank?
|
|
164
|
-
Rails.logger.info("Using ZConnect cookie in refresh_nav method")
|
|
165
|
-
response = HTTParty.post("https://#{self.hostname}/apps/zconnectsession/refresh-navbarcache", :headers => {'Cookie' => "#{self.zconnect_provider}=#{zconnect_accesstoken}", 'Content-Type' => 'application/json'})
|
|
166
|
-
output_json = JSON.parse(response.body)
|
|
167
128
|
else
|
|
168
|
-
|
|
169
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZConnect cookie present matching #{self.hostname}")
|
|
170
|
-
else
|
|
171
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
172
|
-
end
|
|
129
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("No ZSession cookie present")
|
|
173
130
|
end
|
|
174
131
|
rescue JSON::ParserError => ex
|
|
175
132
|
output_json = {}
|
|
@@ -178,15 +135,6 @@ module ZuoraAPI
|
|
|
178
135
|
return output_json
|
|
179
136
|
end
|
|
180
137
|
|
|
181
|
-
def get_zconnect_accesstoken(cookies)
|
|
182
|
-
accesstoken = nil
|
|
183
|
-
self.update_zconnect_provider
|
|
184
|
-
if !cookies[self.zconnect_provider].nil? && !cookies[self.zconnect_provider].empty?
|
|
185
|
-
accesstoken = cookies[self.zconnect_provider]
|
|
186
|
-
end
|
|
187
|
-
return accesstoken
|
|
188
|
-
end
|
|
189
|
-
|
|
190
138
|
def reporting_url(path)
|
|
191
139
|
map = {"US" => {"Sandbox" => "https://zconnectsandbox.zuora.com/api/rest/v1/",
|
|
192
140
|
"Production" => "https://zconnect.zuora.com/api/rest/v1/",
|
|
@@ -208,7 +156,7 @@ module ZuoraAPI
|
|
|
208
156
|
# 1. Pass in cookies and optionally custom_authorities, name, and description
|
|
209
157
|
# 2. Pass in user_id, entity_ids, client_id, client_secret, and optionally custom_authorities, name, and description
|
|
210
158
|
# https://intranet.zuora.com/confluence/display/Sunburst/Create+an+OAuth+Client+through+API+Gateway#CreateanOAuthClientthroughAPIGateway-ZSession
|
|
211
|
-
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)
|
|
159
|
+
def get_oauth_client (custom_authorities = [], info_name: "No Name", info_desc: "This client was created without a description.", user_id: nil, entity_ids: nil, client_id: nil, client_secret: nil, new_client_id: nil, new_client_secret: nil, cookies: nil, chomp_v1_from_genesis_endpoint: false, use_api_generated_client_secret: false)
|
|
212
160
|
authorization = ""
|
|
213
161
|
new_client_id = SecureRandom.uuid if new_client_id.blank?
|
|
214
162
|
new_client_secret = SecureRandom.hex(10) if new_client_secret.blank?
|
|
@@ -234,11 +182,11 @@ module ZuoraAPI
|
|
|
234
182
|
end
|
|
235
183
|
|
|
236
184
|
if !authorization.blank? && !user_id.blank? && !entity_ids.blank?
|
|
237
|
-
endpoint = self.rest_endpoint("genesis/clients")
|
|
185
|
+
endpoint = chomp_v1_from_genesis_endpoint ? self.rest_endpoint.chomp("v1/").concat("genesis/clients") : self.rest_endpoint("genesis/clients")
|
|
238
186
|
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)
|
|
239
187
|
output_json = JSON.parse(oauth_response.body)
|
|
240
188
|
if oauth_response.code == 201
|
|
241
|
-
output_json["clientSecret"] = new_client_secret
|
|
189
|
+
output_json["clientSecret"] = new_client_secret if !use_api_generated_client_secret
|
|
242
190
|
return output_json
|
|
243
191
|
elsif oauth_response.code == 401 && !oauth_response.message.blank?
|
|
244
192
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(output_json["message"], oauth_response)
|
|
@@ -305,7 +253,7 @@ module ZuoraAPI
|
|
|
305
253
|
end
|
|
306
254
|
|
|
307
255
|
def update_environment
|
|
308
|
-
if !self.
|
|
256
|
+
if !self.hostname.blank?
|
|
309
257
|
case self.hostname
|
|
310
258
|
when /(?<=\.|\/|-|^)(apisandbox|sandbox)(?=\.|\/|-|$)/
|
|
311
259
|
self.environment = 'Sandbox'
|
|
@@ -328,13 +276,13 @@ module ZuoraAPI
|
|
|
328
276
|
end
|
|
329
277
|
|
|
330
278
|
def update_zconnect_provider
|
|
331
|
-
|
|
332
|
-
|
|
279
|
+
update_region if self.region.blank?
|
|
280
|
+
update_environment if self.environment.blank?
|
|
333
281
|
mappings = {"US" => {"Sandbox" => "ZConnectSbx", "Services" => "ZConnectSvcUS", "Production" => "ZConnectProd", "Performance" => "ZConnectPT1", "Test" => "ZConnectTest", "Staging" => "ZConnectQA", "KubeSTG" => "ZConnectDev", "KubeDEV" => "ZConnectDev", "KubePROD" => "ZConnectDev"},
|
|
334
282
|
"NA" => {"Sandbox" => "ZConnectSbxNA", "Services" => "ZConnectSvcNA", "Production" => "ZConnectProdNA", "Performance" => "ZConnectPT1NA"},
|
|
335
283
|
"EU" => {"Sandbox" => "ZConnectSbxEU", "Services" => "ZConnectSvcEU", "Production" => "ZConnectProdEU", "Performance" => "ZConnectPT1EU", "Test" => "ZConnectTest"},
|
|
336
284
|
"Unknown" => {"Unknown" => "Unknown"}}
|
|
337
|
-
self.zconnect_provider = mappings[region][environment]
|
|
285
|
+
self.zconnect_provider = mappings[self.region][self.environment]
|
|
338
286
|
end
|
|
339
287
|
|
|
340
288
|
def aqua_endpoint(url="")
|
|
@@ -374,8 +322,8 @@ module ZuoraAPI
|
|
|
374
322
|
return domain ? endpoint.concat(prefix).concat(url) : prefix.concat(url)
|
|
375
323
|
end
|
|
376
324
|
|
|
377
|
-
def rest_domain
|
|
378
|
-
return URI(
|
|
325
|
+
def rest_domain(endpoint: self.rest_endpoint)
|
|
326
|
+
return URI(endpoint).host
|
|
379
327
|
end
|
|
380
328
|
|
|
381
329
|
def fileURL(url="")
|
|
@@ -387,10 +335,41 @@ module ZuoraAPI
|
|
|
387
335
|
end
|
|
388
336
|
|
|
389
337
|
def new_session(auth_type: :basic, debug: false, zuora_track_id: nil)
|
|
338
|
+
retries ||= 2
|
|
339
|
+
yield
|
|
340
|
+
|
|
341
|
+
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
342
|
+
self.status = 'Invalid'
|
|
343
|
+
self.current_error = ex.message
|
|
344
|
+
raise
|
|
345
|
+
rescue ZuoraAPI::Exceptions::ZuoraAPIError => ex
|
|
346
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(ex.message, ex.response)
|
|
347
|
+
|
|
348
|
+
rescue ZuoraAPI::Exceptions::ZuoraAPIInternalServerError => ex
|
|
349
|
+
raise ex if retries.zero?
|
|
350
|
+
|
|
351
|
+
retries -= 1
|
|
352
|
+
sleep(self.timeout_sleep)
|
|
353
|
+
retry
|
|
354
|
+
|
|
355
|
+
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
|
356
|
+
self.log(location: "BasicLogin", exception: ex, message: "Timed out", level: :error)
|
|
357
|
+
|
|
358
|
+
self.current_error = "Request timed out. Try again"
|
|
359
|
+
self.status = 'Timeout'
|
|
360
|
+
|
|
361
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
|
362
|
+
|
|
363
|
+
rescue EOFError
|
|
364
|
+
if self.url.match?(/.*services\d{1,}.zuora.com*/)
|
|
365
|
+
self.current_error = "Services tenant '#{self.url.scan(/.*\/\/(services\d{1,}).zuora.com*/).last.first}' is no longer available."
|
|
366
|
+
self.status = 'Not Available'
|
|
367
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
|
368
|
+
end
|
|
369
|
+
|
|
390
370
|
end
|
|
391
371
|
|
|
392
372
|
def get_session(prefix: false, auth_type: :basic, zuora_track_id: nil)
|
|
393
|
-
Rails.logger.debug("Get session for #{auth_type} - #{self.class.to_s}") if Rails.env.to_s == 'development'
|
|
394
373
|
case auth_type
|
|
395
374
|
when :basic
|
|
396
375
|
if self.current_session.blank?
|
|
@@ -399,14 +378,13 @@ module ZuoraAPI
|
|
|
399
378
|
if self.bearer_token.blank? || self.oauth_expired?
|
|
400
379
|
self.new_session(auth_type: :bearer, zuora_track_id: zuora_track_id)
|
|
401
380
|
end
|
|
402
|
-
self.get_z_session(zuora_track_id: zuora_track_id)
|
|
381
|
+
self.get_z_session(zuora_track_id: zuora_track_id)
|
|
403
382
|
when 'ZuoraAPI::Basic'
|
|
404
383
|
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
|
405
384
|
else
|
|
406
385
|
self.new_session(auth_type: :basic, zuora_track_id: zuora_track_id)
|
|
407
386
|
end
|
|
408
387
|
end
|
|
409
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
|
|
410
388
|
return prefix ? "ZSession #{self.current_session}" : self.current_session.to_s
|
|
411
389
|
when :bearer
|
|
412
390
|
case self.class.to_s
|
|
@@ -419,8 +397,6 @@ module ZuoraAPI
|
|
|
419
397
|
else
|
|
420
398
|
raise ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError.new("Unknown Login, does not support Authentication of Type: #{auth_type}")
|
|
421
399
|
end
|
|
422
|
-
|
|
423
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error) if self.status != 'Active'
|
|
424
400
|
return prefix ? "Bearer #{self.bearer_token}" : self.bearer_token.to_s
|
|
425
401
|
end
|
|
426
402
|
end
|
|
@@ -428,16 +404,18 @@ module ZuoraAPI
|
|
|
428
404
|
def soap_call(
|
|
429
405
|
ns1: 'ns1',
|
|
430
406
|
ns2: 'ns2',
|
|
431
|
-
batch_size: nil,
|
|
407
|
+
batch_size: nil,
|
|
408
|
+
headers: {},
|
|
432
409
|
single_transaction: false,
|
|
433
410
|
debug: false,
|
|
434
411
|
zuora_track_id: nil,
|
|
435
412
|
errors: [ZuoraAPI::Exceptions::ZuoraAPISessionError].concat(ZUORA_API_ERRORS),
|
|
436
413
|
z_session: true,
|
|
437
414
|
timeout_retry: false,
|
|
438
|
-
timeout:
|
|
415
|
+
timeout: 130,
|
|
439
416
|
timeout_sleep_interval: self.timeout_sleep,
|
|
440
417
|
output_exception_messages: true,
|
|
418
|
+
skip_session: false,
|
|
441
419
|
**keyword_args)
|
|
442
420
|
tries ||= 2
|
|
443
421
|
xml = Nokogiri::XML::Builder.new do |xml|
|
|
@@ -447,8 +425,10 @@ module ZuoraAPI
|
|
|
447
425
|
'xmlns:api' => "http://api.zuora.com/",
|
|
448
426
|
"xmlns:#{ns1}" => "http://api.zuora.com/") do
|
|
449
427
|
xml['SOAP-ENV'].Header do
|
|
450
|
-
|
|
451
|
-
xml["#{ns1}"].
|
|
428
|
+
if !skip_session
|
|
429
|
+
xml["#{ns1}"].SessionHeader do
|
|
430
|
+
xml["#{ns1}"].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: zuora_track_id)
|
|
431
|
+
end
|
|
452
432
|
end
|
|
453
433
|
if single_transaction
|
|
454
434
|
xml["#{ns1}"].CallOptions do
|
|
@@ -466,12 +446,11 @@ module ZuoraAPI
|
|
|
466
446
|
end
|
|
467
447
|
end
|
|
468
448
|
end
|
|
469
|
-
|
|
470
449
|
input_xml = Nokogiri::XML(xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip)
|
|
471
450
|
input_xml.xpath('//ns1:session', 'ns1' =>'http://api.zuora.com/').children.remove
|
|
472
451
|
Rails.logger.debug("Request SOAP XML: #{input_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
|
|
473
452
|
|
|
474
|
-
headers
|
|
453
|
+
headers.merge!({ 'Content-Type' => "text/xml; charset=utf-8", 'Accept' => 'text/xml'})
|
|
475
454
|
headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
|
|
476
455
|
|
|
477
456
|
request = HTTParty::Request.new(
|
|
@@ -488,7 +467,11 @@ module ZuoraAPI
|
|
|
488
467
|
Rails.logger.debug("Response SOAP XML: #{output_xml.to_xml(:save_with => XML_SAVE_OPTIONS).strip}") if debug
|
|
489
468
|
|
|
490
469
|
raise_errors(type: :SOAP, body: output_xml, response: response)
|
|
470
|
+
|
|
471
|
+
return output_xml, input_xml, response
|
|
472
|
+
|
|
491
473
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
474
|
+
raise if skip_session
|
|
492
475
|
if !tries.zero? && z_session
|
|
493
476
|
tries -= 1
|
|
494
477
|
Rails.logger.debug("SOAP Call - Session Invalid")
|
|
@@ -500,39 +483,33 @@ module ZuoraAPI
|
|
|
500
483
|
end
|
|
501
484
|
|
|
502
485
|
retry
|
|
503
|
-
else
|
|
504
|
-
if errors.include?(ex.class)
|
|
505
|
-
raise ex
|
|
506
|
-
else
|
|
507
|
-
return output_xml, input_xml, response
|
|
508
|
-
end
|
|
509
486
|
end
|
|
487
|
+
|
|
488
|
+
raise ex if errors.include?(ex.class)
|
|
489
|
+
|
|
490
|
+
return output_xml, input_xml, response
|
|
491
|
+
|
|
510
492
|
rescue *ZUORA_API_ERRORS => ex
|
|
511
|
-
if errors.include?(ex.class)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
end
|
|
493
|
+
raise ex if errors.include?(ex.class)
|
|
494
|
+
|
|
495
|
+
response = ex.response unless response
|
|
496
|
+
return output_xml, input_xml, response
|
|
497
|
+
|
|
517
498
|
rescue *CONNECTION_EXCEPTIONS => ex
|
|
518
|
-
if tries.zero?
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
Rails.logger.error("SOAP Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
|
524
|
-
end
|
|
525
|
-
end
|
|
526
|
-
raise ex
|
|
499
|
+
if !tries.zero?
|
|
500
|
+
tries -= 1
|
|
501
|
+
self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
|
502
|
+
sleep(timeout_sleep_interval)
|
|
503
|
+
retry
|
|
527
504
|
end
|
|
528
505
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
506
|
+
self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
|
507
|
+
raise ex
|
|
508
|
+
|
|
532
509
|
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
|
533
510
|
if !tries.zero?
|
|
534
511
|
tries -= 1
|
|
535
|
-
|
|
512
|
+
self.log(location: "SOAP Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
|
536
513
|
if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
|
|
537
514
|
retry
|
|
538
515
|
elsif timeout_retry
|
|
@@ -541,19 +518,74 @@ module ZuoraAPI
|
|
|
541
518
|
end
|
|
542
519
|
end
|
|
543
520
|
|
|
544
|
-
if output_exception_messages
|
|
545
|
-
|
|
546
|
-
Rails.logger.error("SOAP Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
|
547
|
-
else
|
|
548
|
-
Rails.logger.error("SOAP Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
|
549
|
-
end
|
|
550
|
-
end
|
|
551
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from #{url}", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
|
521
|
+
self.log(location: "SOAP Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
|
522
|
+
ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
|
552
523
|
raise ex
|
|
524
|
+
|
|
553
525
|
rescue => ex
|
|
554
526
|
raise ex
|
|
555
|
-
|
|
556
|
-
|
|
527
|
+
ensure
|
|
528
|
+
self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def error_logger(ex)
|
|
532
|
+
exception_args = Rails.logger.with_fields.merge(self.exception_args(ex))
|
|
533
|
+
case ex
|
|
534
|
+
when ZuoraAPI::Exceptions::ZuoraAPIUnkownError, ZuoraAPI::Exceptions::ZuoraDataIntegrity
|
|
535
|
+
Rails.logger.error('Zuora Unknown/Integrity Error', ex, exception_args)
|
|
536
|
+
when ZuoraAPI::Exceptions::ZuoraAPIRequestLimit
|
|
537
|
+
Rails.logger.info('Zuora APILimit Reached', ex, exception_args)
|
|
538
|
+
when *(ZuoraAPI::Login::ZUORA_API_ERRORS-ZuoraAPI::Login::ZUORA_SERVER_ERRORS)
|
|
539
|
+
#Rails.logger.debug('Zuora API Error', ex, self.exception_args(ex))
|
|
540
|
+
when *ZuoraAPI::Login::ZUORA_SERVER_ERRORS
|
|
541
|
+
Rails.logger.error('Zuora Server Error', ex, exception_args)
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def log(location: "Rest Call", exception: nil, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :info )
|
|
546
|
+
level = :debug if ![:debug, :info, :warn, :error, :fatal].include?(level)
|
|
547
|
+
if Rails.logger.class.to_s == "Ougai::Logger"
|
|
548
|
+
Rails.logger.send(level.to_sym, "#{location} - #{message}", exception)
|
|
549
|
+
else
|
|
550
|
+
Rails.logger.send(level.to_sym, "#{location} - #{exception.class} #{message}")
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
def exception_args(ex)
|
|
555
|
+
args = {}
|
|
556
|
+
if defined?(ex.response) && ex.response.present?
|
|
557
|
+
args.merge!({
|
|
558
|
+
request: {
|
|
559
|
+
path: ex.response.request.path.to_s,
|
|
560
|
+
method: ex.response.request.http_method.to_s.split("Net::HTTP::").last.upcase,
|
|
561
|
+
params: ex.response.request.raw_body.to_s,
|
|
562
|
+
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,
|
|
563
|
+
},
|
|
564
|
+
response: {
|
|
565
|
+
status: ex.response.code,
|
|
566
|
+
params: ex.response.body.to_s,
|
|
567
|
+
headers: ex.response.headers.to_s,
|
|
568
|
+
},
|
|
569
|
+
zuora_trace_id: ex.response.headers["zuora-request-id"],
|
|
570
|
+
zuora_track_id: ex.response.request.options[:headers]["Zuora-Track-Id"],
|
|
571
|
+
})
|
|
572
|
+
elsif defined?(ex.request) && ex.request.present?
|
|
573
|
+
args.merge!({
|
|
574
|
+
request: {
|
|
575
|
+
path: ex.request.path.to_s,
|
|
576
|
+
method: ex.request.http_method.to_s.split("Net::HTTP::").last.upcase,
|
|
577
|
+
params: ex.request.options[:body],
|
|
578
|
+
headers: ex.request.options[:headers].map{|k,v| [k.to_s, k.to_s.downcase.strip == "authorization" ? "VALUE FILTERED" : v]}.to_h.to_s
|
|
579
|
+
}
|
|
580
|
+
})
|
|
581
|
+
args.merge!({
|
|
582
|
+
zuora_track_id: ex.request.options[:headers]["Zuora-Track-Id"]
|
|
583
|
+
}) if ex.request.options[:headers]["Zuora-Track-Id"].present?
|
|
584
|
+
end
|
|
585
|
+
rescue => ex
|
|
586
|
+
Rails.logger.error("Failed to create exception arguments", ex, args)
|
|
587
|
+
ensure
|
|
588
|
+
return args
|
|
557
589
|
end
|
|
558
590
|
|
|
559
591
|
def raise_errors(type: :SOAP, body: nil, response: nil)
|
|
@@ -570,13 +602,13 @@ module ZuoraAPI
|
|
|
570
602
|
end
|
|
571
603
|
|
|
572
604
|
if [502,503].include?(response.code)
|
|
573
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from
|
|
605
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new("Received #{response.code} from 'https://#{rest_domain(endpoint: request_uri)}'", response)
|
|
574
606
|
end
|
|
575
607
|
|
|
576
608
|
# Check failure response code
|
|
577
609
|
case response.code
|
|
578
610
|
when 504
|
|
579
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from
|
|
611
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received 504 from 'https://#{rest_domain(endpoint: request_uri)}'", response)
|
|
580
612
|
when 429
|
|
581
613
|
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)
|
|
582
614
|
when 401
|
|
@@ -597,6 +629,10 @@ module ZuoraAPI
|
|
|
597
629
|
when :SOAP
|
|
598
630
|
error, success, message = get_soap_error_and_message(body)
|
|
599
631
|
|
|
632
|
+
if body.xpath('//fns:LoginFault', 'fns' =>'http://fault.api.zuora.com/').present?
|
|
633
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(message, response)
|
|
634
|
+
end
|
|
635
|
+
|
|
600
636
|
if body.xpath('//ns1:queryResponse', 'ns1' => 'http://api.zuora.com/').present? &&
|
|
601
637
|
body.xpath(
|
|
602
638
|
'//ns1:records[@xsi:type="ns2:Export"]',
|
|
@@ -604,12 +640,12 @@ module ZuoraAPI
|
|
|
604
640
|
).present?
|
|
605
641
|
result = body.xpath('//ns2:Status', 'ns2' => 'http://object.api.zuora.com/').text
|
|
606
642
|
if result == 'Failed'
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
643
|
+
message = body.xpath('//ns2:StatusReason', 'ns2' => 'http://object.api.zuora.com/').text
|
|
644
|
+
error = 'FATAL_ERROR'
|
|
645
|
+
if message.present?
|
|
646
|
+
identifier, new_message = message.scan(/^([\w\d]{16})\: (.*)/).first
|
|
647
|
+
error, message = ['UNEXPECTED_ERROR', new_message] if new_message.present?
|
|
611
648
|
else
|
|
612
|
-
error = 'FATAL_ERROR'
|
|
613
649
|
message = 'Export failed due to unknown reason. Consult api logs.'
|
|
614
650
|
end
|
|
615
651
|
end
|
|
@@ -658,14 +694,31 @@ module ZuoraAPI
|
|
|
658
694
|
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.
|
|
659
695
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(reporting_message, response) if reporting_message.present?
|
|
660
696
|
end
|
|
697
|
+
when /\/objects\/batch\//
|
|
698
|
+
if body['code'].present? && /61$/.match(body['code'].to_s).present? # if last 2 digits of code are 61
|
|
699
|
+
raise ZuoraAPI::Exceptions::ZuoraAPITemporaryError.new(body['message'], nil, body['details'])
|
|
700
|
+
end
|
|
701
|
+
when /^\/api\/v1\/payment_plans.*/
|
|
702
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(body['error'], response) if body['error']
|
|
661
703
|
end
|
|
662
704
|
|
|
663
705
|
body = body.dig("results").present? ? body["results"] : body if body.class == Hash
|
|
664
706
|
if body.class == Hash && (!body["success"] || !body["Success"] || response.code != 200)
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
codes_array =
|
|
668
|
-
|
|
707
|
+
reason_keys = %w(reasons errors)
|
|
708
|
+
message_keys = %w(message title)
|
|
709
|
+
messages_array, codes_array = [[],[]]
|
|
710
|
+
reason_keys.each do |rsn_key|
|
|
711
|
+
message_keys.each do |msg_key|
|
|
712
|
+
messages_array = body.fetch(rsn_key, []).map {|error| error[msg_key]}.compact
|
|
713
|
+
break if messages_array.present?
|
|
714
|
+
end
|
|
715
|
+
codes_array = body.fetch(rsn_key, []).map {|error| error['code']}.compact
|
|
716
|
+
break if messages_array.present? && codes_array.present?
|
|
717
|
+
end
|
|
718
|
+
if body.dig('error').class == Hash
|
|
719
|
+
messages_array = messages_array.push(body.dig("error", 'message')).compact
|
|
720
|
+
codes_array = codes_array.push(body.dig("error", 'code')).compact
|
|
721
|
+
end
|
|
669
722
|
|
|
670
723
|
if body['message'] == 'request exceeded limit'
|
|
671
724
|
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)
|
|
@@ -697,7 +750,11 @@ module ZuoraAPI
|
|
|
697
750
|
end
|
|
698
751
|
|
|
699
752
|
if body['error'] == 'Unauthorized' && body['status'] == 401
|
|
700
|
-
|
|
753
|
+
if body['message'].present?
|
|
754
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(body['message'], response)
|
|
755
|
+
else
|
|
756
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new("#{messages_array.join(', ')}", response)
|
|
757
|
+
end
|
|
701
758
|
end
|
|
702
759
|
#Authentication failed
|
|
703
760
|
if (codes_array.map{|code| code.to_s.slice(6,7).to_i}.include?(11) || response.code == 401) && !codes_array.include?(422)
|
|
@@ -804,7 +861,7 @@ module ZuoraAPI
|
|
|
804
861
|
output_json = JSON.parse(response.body)
|
|
805
862
|
self.raise_errors(type: :JSON, body: output_json, response: response)
|
|
806
863
|
|
|
807
|
-
elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml')) and type != :xml
|
|
864
|
+
elsif (response_content_types.include?('application/xml') || response_content_types.include?('text/xml') || response_content_types.include?('application/soap+xml')) and type != :xml
|
|
808
865
|
output_xml = Nokogiri::XML(response.body)
|
|
809
866
|
self.raise_errors(type: :SOAP, body: output_xml, response: response)
|
|
810
867
|
|
|
@@ -824,6 +881,8 @@ module ZuoraAPI
|
|
|
824
881
|
raise ZuoraAPI::Exceptions::ZuoraAPIConnectionTimeout.new(error_message, response)
|
|
825
882
|
when /Client sent a bad request./, /Bad Request/, /403 Forbidden/
|
|
826
883
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
|
|
884
|
+
when /414 Request-URI Too Large/
|
|
885
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Request URL is too long", response)
|
|
827
886
|
else
|
|
828
887
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(error_message, response)
|
|
829
888
|
end
|
|
@@ -850,6 +909,11 @@ module ZuoraAPI
|
|
|
850
909
|
message = body.xpath('//ns1:Message', 'ns1' =>'http://api.zuora.com/').text
|
|
851
910
|
end
|
|
852
911
|
|
|
912
|
+
if error.blank? || message.blank?
|
|
913
|
+
error = body.xpath('//soapenv:Value', 'soapenv'=>'http://www.w3.org/2003/05/soap-envelope').text
|
|
914
|
+
message = body.xpath('//soapenv:Text', 'soapenv'=>'http://www.w3.org/2003/05/soap-envelope').text
|
|
915
|
+
end
|
|
916
|
+
|
|
853
917
|
#Update/Create/Delete Calls with multiple requests and responses
|
|
854
918
|
if body.xpath('//ns1:result', 'ns1' =>'http://api.zuora.com/').size > 0 && body.xpath('//ns1:Errors', 'ns1' =>'http://api.zuora.com/').size > 0
|
|
855
919
|
error = []
|
|
@@ -893,17 +957,22 @@ module ZuoraAPI
|
|
|
893
957
|
raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
|
|
894
958
|
end
|
|
895
959
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
|
896
|
-
when /invalid/, /^DUPLICATE_VALUE/, /^REQUEST_REJECTED/, /INVALID_ID/, /MAX_RECORDS_EXCEEDED/, /INVALID_FIELD/, /MALFORMED_QUERY/, /NO_PERMISSION/, /PDF_QUERY_ERROR/, /MISSING_REQUIRED_VALUE/, /INVALID_TYPE/, /TRANSACTION_FAILED/, /API_DISABLED/, /CANNOT_DELETE/, /ACCOUNTING_PERIOD_CLOSED/
|
|
960
|
+
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/
|
|
897
961
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
|
898
962
|
when /.*UNEXPECTED_ERROR/
|
|
899
963
|
raise ZuoraAPI::Exceptions::ZuoraUnexpectedError.new(message, response, errors, success)
|
|
900
964
|
when /.*soapenv:Server.*/
|
|
901
965
|
if /^Invalid value.*for type.*|^Id is invalid|^date string can not be less than 19 charactors$/.match(message).present?
|
|
902
966
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
|
903
|
-
elsif /^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
|
|
967
|
+
elsif /^unknown$|^Invalid white space character \(.*\) in text to output$|^Invalid null character in text to output$/.match(message).present?
|
|
904
968
|
raise ZuoraAPI::Exceptions::ZuoraAPIUnkownError.new(message, response, errors, success)
|
|
905
969
|
end
|
|
906
970
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
|
|
971
|
+
when /soapenv:Receiver/
|
|
972
|
+
if /^com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character.*$/.match(message).present?
|
|
973
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new(message, response, errors, success)
|
|
974
|
+
end
|
|
975
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new(message, response, errors, success)
|
|
907
976
|
else
|
|
908
977
|
raise ZuoraAPI::Exceptions::ZuoraAPIInternalServerError.new("Z:#{error}::#{message}", response, errors, success)
|
|
909
978
|
end
|
|
@@ -953,7 +1022,7 @@ module ZuoraAPI
|
|
|
953
1022
|
base = self.url.include?(".com") ? self.url.split(".com")[0].concat(".com") : self.url.split(".eu")[0].concat(".eu")
|
|
954
1023
|
url = object ? "#{base}/apps/api/describe/#{object}" : "#{base}/apps/api/describe/"
|
|
955
1024
|
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"}
|
|
956
|
-
response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout =>
|
|
1025
|
+
response = HTTParty.get(url, headers: {"Authorization" => self.get_session(prefix: true, auth_type: :basic)}.merge(headers), :timeout => 130)
|
|
957
1026
|
|
|
958
1027
|
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error.present? ? self.current_error : 'Describe call 401', response) if response.code == 401
|
|
959
1028
|
|
|
@@ -986,35 +1055,31 @@ module ZuoraAPI
|
|
|
986
1055
|
end
|
|
987
1056
|
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
|
|
988
1057
|
end
|
|
1058
|
+
|
|
1059
|
+
return des_hash
|
|
989
1060
|
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
|
990
|
-
if tries.zero?
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
Rails.logger.error("Describe - #{ex.class} Timed out will retry after #{self.timeout_sleep} seconds")
|
|
996
|
-
end
|
|
997
|
-
end
|
|
998
|
-
raise ex
|
|
1061
|
+
if !tries.zero?
|
|
1062
|
+
tries -= 1
|
|
1063
|
+
self.log(location: "Describe", exception: ex, message: "Timed out will retry after #{self.timeout_sleep} seconds", level: :debug)
|
|
1064
|
+
sleep(self.timeout_sleep)
|
|
1065
|
+
retry
|
|
999
1066
|
end
|
|
1000
1067
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1068
|
+
self.log(location: "Describe", exception: ex, message: "Timed out", level: :error) if log_errors
|
|
1069
|
+
raise ex
|
|
1070
|
+
|
|
1004
1071
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
1005
1072
|
if !tries.zero? && self.status == 'Active'
|
|
1006
1073
|
tries -= 1
|
|
1007
1074
|
Rails.logger.debug("Describe session expired. Starting new session.")
|
|
1008
1075
|
self.new_session
|
|
1009
1076
|
retry
|
|
1010
|
-
else
|
|
1011
|
-
Rails.logger.error("Describe session expired. Starting new session.") if log_errors
|
|
1012
|
-
raise ex
|
|
1013
1077
|
end
|
|
1078
|
+
|
|
1079
|
+
Rails.logger.error("Describe session expired. Starting new session.") if log_errors
|
|
1080
|
+
raise ex
|
|
1014
1081
|
rescue => ex
|
|
1015
1082
|
raise ex
|
|
1016
|
-
else
|
|
1017
|
-
return des_hash
|
|
1018
1083
|
end
|
|
1019
1084
|
|
|
1020
1085
|
def rest_call(
|
|
@@ -1027,11 +1092,12 @@ module ZuoraAPI
|
|
|
1027
1092
|
z_session: true,
|
|
1028
1093
|
session_type: :basic,
|
|
1029
1094
|
timeout_retry: false,
|
|
1030
|
-
timeout:
|
|
1095
|
+
timeout: 130,
|
|
1031
1096
|
timeout_sleep_interval: self.timeout_sleep,
|
|
1032
1097
|
multipart: false,
|
|
1033
1098
|
stream_body: false,
|
|
1034
1099
|
output_exception_messages: true,
|
|
1100
|
+
zuora_track_id: nil,
|
|
1035
1101
|
**keyword_args,
|
|
1036
1102
|
&block
|
|
1037
1103
|
)
|
|
@@ -1041,12 +1107,13 @@ module ZuoraAPI
|
|
|
1041
1107
|
|
|
1042
1108
|
authentication_headers = {}
|
|
1043
1109
|
if z_session
|
|
1044
|
-
authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type) }
|
|
1110
|
+
authentication_headers = {"Authorization" => self.get_session(prefix: true, auth_type: session_type, zuora_track_id: zuora_track_id) }
|
|
1045
1111
|
if self.entity_id.present?
|
|
1046
1112
|
authentication_headers["Zuora-Entity-Ids"] = self.entity_id if headers.dig("Zuora-Entity-Ids").nil?
|
|
1047
1113
|
authentication_headers.delete_if { |key, value| ["entityId", "entityName"].include?(key.to_s) }
|
|
1048
1114
|
end
|
|
1049
1115
|
end
|
|
1116
|
+
headers['Zuora-Track-Id'] = zuora_track_id if zuora_track_id.present?
|
|
1050
1117
|
|
|
1051
1118
|
modified_headers = {'Content-Type' => "application/json; charset=utf-8"}.merge(authentication_headers).merge(headers)
|
|
1052
1119
|
|
|
@@ -1072,18 +1139,20 @@ module ZuoraAPI
|
|
|
1072
1139
|
Rails.logger.debug("Response JSON: #{output_json}") if debug && output_json.present?
|
|
1073
1140
|
|
|
1074
1141
|
raise_errors(type: :JSON, body: output_json, response: response)
|
|
1075
|
-
rescue
|
|
1142
|
+
rescue => ex
|
|
1076
1143
|
reset_files(body) if multipart
|
|
1077
1144
|
raise
|
|
1078
1145
|
end
|
|
1146
|
+
|
|
1147
|
+
return [output_json, response]
|
|
1079
1148
|
rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
|
|
1080
1149
|
if self.class.to_s == 'ZuoraAPI::Oauth' && ex.message.include?("Authentication type is not supported by this Login")
|
|
1081
1150
|
session_type = :bearer
|
|
1082
1151
|
retry
|
|
1083
|
-
else
|
|
1084
|
-
Rails.logger.debug("Rest Call - Session Bad Auth type")
|
|
1085
|
-
raise ex
|
|
1086
1152
|
end
|
|
1153
|
+
Rails.logger.debug("Rest Call - Session Bad Auth type")
|
|
1154
|
+
raise ex
|
|
1155
|
+
|
|
1087
1156
|
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
1088
1157
|
if !tries.zero? && z_session
|
|
1089
1158
|
tries -= 1
|
|
@@ -1096,40 +1165,35 @@ module ZuoraAPI
|
|
|
1096
1165
|
end
|
|
1097
1166
|
|
|
1098
1167
|
retry
|
|
1099
|
-
else
|
|
1100
|
-
if errors.include?(ex.class)
|
|
1101
|
-
raise ex
|
|
1102
|
-
else
|
|
1103
|
-
return [output_json, response]
|
|
1104
|
-
end
|
|
1105
1168
|
end
|
|
1169
|
+
|
|
1170
|
+
raise ex if errors.include?(ex.class)
|
|
1171
|
+
return [output_json, response]
|
|
1172
|
+
|
|
1106
1173
|
rescue *ZUORA_API_ERRORS => ex
|
|
1107
|
-
if errors.include?(ex.class)
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
end
|
|
1174
|
+
raise ex if errors.include?(ex.class)
|
|
1175
|
+
|
|
1176
|
+
response = ex.response unless response
|
|
1177
|
+
return [output_json, response]
|
|
1178
|
+
|
|
1113
1179
|
rescue ZuoraAPI::Exceptions::BadEntityError => ex
|
|
1114
1180
|
raise ex
|
|
1115
1181
|
rescue *CONNECTION_EXCEPTIONS => ex
|
|
1116
|
-
if tries.zero?
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
Rails.logger.error("Rest Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
|
1122
|
-
end
|
|
1123
|
-
end
|
|
1124
|
-
raise ex
|
|
1182
|
+
if !tries.zero?
|
|
1183
|
+
tries -= 1
|
|
1184
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
|
1185
|
+
sleep(timeout_sleep_interval)
|
|
1186
|
+
retry
|
|
1125
1187
|
end
|
|
1126
1188
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1189
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
|
1190
|
+
raise ex
|
|
1191
|
+
|
|
1130
1192
|
rescue *CONNECTION_READ_EXCEPTIONS => ex
|
|
1193
|
+
|
|
1131
1194
|
if !tries.zero?
|
|
1132
1195
|
tries -= 1
|
|
1196
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out will retry after #{timeout_sleep_interval} seconds", level: :debug)
|
|
1133
1197
|
if ex.is_a?(Errno::ECONNRESET) && ex.message.include?('SSL_connect')
|
|
1134
1198
|
retry
|
|
1135
1199
|
elsif timeout_retry
|
|
@@ -1137,20 +1201,15 @@ module ZuoraAPI
|
|
|
1137
1201
|
retry
|
|
1138
1202
|
end
|
|
1139
1203
|
end
|
|
1140
|
-
|
|
1141
|
-
if output_exception_messages
|
|
1142
|
-
|
|
1143
|
-
Rails.logger.error("Rest Call - Timed out will retry after #{timeout_sleep_interval} seconds", ex)
|
|
1144
|
-
else
|
|
1145
|
-
Rails.logger.error("Rest Call - #{ex.class} Timed out will retry after #{timeout_sleep_interval} seconds")
|
|
1146
|
-
end
|
|
1147
|
-
end
|
|
1148
|
-
raise ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from #{url}", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
|
1204
|
+
|
|
1205
|
+
self.log(location: "Rest Call", exception: ex, message: "Timed out", level: :error) if output_exception_messages
|
|
1206
|
+
ex = ZuoraAPI::Exceptions::ZuoraAPIReadTimeout.new("Received read timeout from 'https://#{rest_domain(endpoint: url)}'", nil, request) if ex.instance_of?(Net::ReadTimeout)
|
|
1149
1207
|
raise ex
|
|
1208
|
+
|
|
1150
1209
|
rescue => ex
|
|
1151
1210
|
raise ex
|
|
1152
|
-
|
|
1153
|
-
|
|
1211
|
+
ensure
|
|
1212
|
+
self.error_logger(ex) if defined?(ex) && Rails.logger.class.to_s == "Ougai::Logger"
|
|
1154
1213
|
end
|
|
1155
1214
|
|
|
1156
1215
|
def update_create_tenant
|
|
@@ -1172,8 +1231,9 @@ module ZuoraAPI
|
|
|
1172
1231
|
while !response["nextPage"].blank?
|
|
1173
1232
|
url = self.rest_endpoint(response["nextPage"].split('/v1/').last)
|
|
1174
1233
|
Rails.logger.debug("Fetch Catalog URL #{url}")
|
|
1175
|
-
output_json, response = self.rest_call(:
|
|
1176
|
-
|
|
1234
|
+
output_json, response = self.rest_call(debug: false, url: url, timeout_retry: true)
|
|
1235
|
+
|
|
1236
|
+
if !/(true|t|yes|y|1)$/.match(output_json['success'].to_s) || output_json['success'].class != TrueClass
|
|
1177
1237
|
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}", response)
|
|
1178
1238
|
end
|
|
1179
1239
|
output_json["products"].each do |product|
|
|
@@ -1199,7 +1259,7 @@ module ZuoraAPI
|
|
|
1199
1259
|
return products, catalog_map
|
|
1200
1260
|
end
|
|
1201
1261
|
|
|
1202
|
-
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:
|
|
1262
|
+
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)
|
|
1203
1263
|
raise "file_path must be of class Pathname" if file_path.class != Pathname
|
|
1204
1264
|
|
|
1205
1265
|
retry_count ||= timeout_retries
|
|
@@ -1304,14 +1364,20 @@ module ZuoraAPI
|
|
|
1304
1364
|
return file_handle
|
|
1305
1365
|
when Net::HTTPUnauthorized
|
|
1306
1366
|
if z_session
|
|
1307
|
-
|
|
1367
|
+
unless (retry_count -= 1).zero?
|
|
1308
1368
|
self.new_session
|
|
1309
|
-
raise
|
|
1310
|
-
else
|
|
1311
|
-
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
|
1369
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError, 'Retrying'
|
|
1312
1370
|
end
|
|
1371
|
+
raise ZuoraAPI::Exceptions::ZuoraAPISessionError.new(self.current_error)
|
|
1372
|
+
end
|
|
1373
|
+
raise
|
|
1374
|
+
when Net::HTTPNotFound
|
|
1375
|
+
if url.include?(self.fileURL)
|
|
1376
|
+
raise ZuoraAPI::Exceptions::FileDownloadError.new(
|
|
1377
|
+
"The current tenant does not have a file with id '#{url.split('/').last}'"
|
|
1378
|
+
)
|
|
1313
1379
|
else
|
|
1314
|
-
raise
|
|
1380
|
+
raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
|
|
1315
1381
|
end
|
|
1316
1382
|
else
|
|
1317
1383
|
raise ZuoraAPI::Exceptions::FileDownloadError.new("File Download Failed #{response.class}")
|
|
@@ -1322,133 +1388,81 @@ module ZuoraAPI
|
|
|
1322
1388
|
sleep(5)
|
|
1323
1389
|
if (retry_count -= 1) >= 0
|
|
1324
1390
|
retry
|
|
1325
|
-
else
|
|
1326
|
-
Rails.logger.error("File Download Failed")
|
|
1327
|
-
raise
|
|
1328
1391
|
end
|
|
1392
|
+
Rails.logger.error("File Download Failed")
|
|
1393
|
+
raise
|
|
1329
1394
|
end
|
|
1330
1395
|
|
|
1331
1396
|
def getDataSourceExport(query, extract: true, encrypted: false, zip: true, z_track_id: "")
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
xml['
|
|
1342
|
-
xml['ns1'].create do
|
|
1343
|
-
xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
|
|
1344
|
-
xml['ns2'].Format 'csv'
|
|
1345
|
-
xml['ns2'].Zip zip
|
|
1346
|
-
xml['ns2'].Name 'googman'
|
|
1347
|
-
xml['ns2'].Query query
|
|
1348
|
-
xml['ns2'].Encrypted encrypted
|
|
1349
|
-
end
|
|
1350
|
-
end
|
|
1351
|
-
end
|
|
1397
|
+
tries ||= 3
|
|
1398
|
+
|
|
1399
|
+
output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true, zuora_track_id: z_track_id) do |xml|
|
|
1400
|
+
xml['ns1'].create do
|
|
1401
|
+
xml['ns1'].zObjects('xsi:type' => "ns2:Export") do
|
|
1402
|
+
xml['ns2'].Format 'csv'
|
|
1403
|
+
xml['ns2'].Zip zip
|
|
1404
|
+
xml['ns2'].Name 'googman'
|
|
1405
|
+
xml['ns2'].Query query
|
|
1406
|
+
xml['ns2'].Encrypted encrypted
|
|
1352
1407
|
end
|
|
1353
1408
|
end
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
confirmRequest = Nokogiri::XML::Builder.new do |xml|
|
|
1365
|
-
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
|
|
1366
|
-
xml['SOAP-ENV'].Header do
|
|
1367
|
-
xml['ns1'].SessionHeader do
|
|
1368
|
-
xml['ns1'].session self.get_session(prefix: false, auth_type: :basic, zuora_track_id: z_track_id)
|
|
1369
|
-
end
|
|
1370
|
-
end
|
|
1371
|
-
xml['SOAP-ENV'].Body do
|
|
1372
|
-
xml['ns1'].query do
|
|
1373
|
-
xml['ns1'].queryString "SELECT Id, CreatedById, CreatedDate, Encrypted, FileId, Format, Name, Query, Size, Status, StatusReason, UpdatedById, UpdatedDate, Zip From Export where Id = '#{id}'"
|
|
1374
|
-
end
|
|
1375
|
-
end
|
|
1409
|
+
end
|
|
1410
|
+
|
|
1411
|
+
id = output_xml.xpath('//ns1:Id', 'ns1' =>'http://api.zuora.com/').text
|
|
1412
|
+
|
|
1413
|
+
result = 'Waiting'
|
|
1414
|
+
while result != "Completed"
|
|
1415
|
+
sleep 3
|
|
1416
|
+
output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true, zuora_track_id: z_track_id) do |xml|
|
|
1417
|
+
xml['ns1'].query do
|
|
1418
|
+
xml['ns1'].queryString "SELECT Id, CreatedById, CreatedDate, Encrypted, FileId, Format, Name, Query, Size, Status, StatusReason, UpdatedById, UpdatedDate, Zip From Export where Id = '#{id}'"
|
|
1376
1419
|
end
|
|
1377
1420
|
end
|
|
1378
|
-
result = '
|
|
1379
|
-
|
|
1380
|
-
while result != "Completed"
|
|
1381
|
-
sleep 3
|
|
1382
|
-
response_query = HTTParty.post(self.url, body: confirmRequest.to_xml(:save_with => XML_SAVE_OPTIONS).strip, headers: {'Content-Type' => "application/json; charset=utf-8", "Z-Track-Id" => z_track_id}, :timeout => 120)
|
|
1383
|
-
|
|
1384
|
-
output_xml = Nokogiri::XML(response_query.body)
|
|
1385
|
-
result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
|
|
1386
|
-
status_code = response_query.code if response_query
|
|
1387
|
-
|
|
1388
|
-
raise_errors(type: :SOAP, body: output_xml, response: response_query) if result.blank? || result == "Failed"
|
|
1389
|
-
# raise "Export Creation Unsuccessful : #{response_query.code}: #{response_query.parsed_response}" if result.blank? || result == "Failed"
|
|
1390
|
-
end
|
|
1391
|
-
|
|
1392
|
-
file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
|
|
1393
|
-
export_file = get_file(:url => self.fileURL(file_id))
|
|
1394
|
-
export_file_path = export_file.path
|
|
1395
|
-
Rails.logger.debug("=====> Export path #{export_file.path}")
|
|
1396
|
-
|
|
1397
|
-
if extract && zip
|
|
1398
|
-
require "zip"
|
|
1399
|
-
new_path = export_file_path.partition('.zip').first
|
|
1400
|
-
zipped = Zip::File.open(export_file_path)
|
|
1401
|
-
file_handle = zipped.entries.first
|
|
1402
|
-
file_handle.extract(new_path)
|
|
1403
|
-
File.delete(export_file_path)
|
|
1404
|
-
return new_path
|
|
1405
|
-
else
|
|
1406
|
-
return export_file_path
|
|
1407
|
-
end
|
|
1408
|
-
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
1409
|
-
if !(tries -= 1).zero?
|
|
1410
|
-
Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
|
|
1411
|
-
self.new_session
|
|
1412
|
-
retry
|
|
1413
|
-
else
|
|
1414
|
-
raise ex
|
|
1415
|
-
end
|
|
1416
|
-
|
|
1417
|
-
rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
|
|
1418
|
-
if !(tries -= 1).zero?
|
|
1419
|
-
Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
|
|
1420
|
-
sleep 10
|
|
1421
|
-
retry
|
|
1422
|
-
else
|
|
1423
|
-
raise ex
|
|
1424
|
-
end
|
|
1425
|
-
|
|
1426
|
-
rescue *ZUORA_API_ERRORS => ex
|
|
1427
|
-
raise ex
|
|
1428
|
-
|
|
1429
|
-
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
|
1430
|
-
if !(tries -= 1).zero?
|
|
1431
|
-
Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
|
|
1432
|
-
sleep 5
|
|
1433
|
-
retry
|
|
1434
|
-
else
|
|
1435
|
-
raise ex
|
|
1436
|
-
end
|
|
1421
|
+
result = output_xml.xpath('//ns2:Status', 'ns2' =>'http://object.api.zuora.com/').text
|
|
1422
|
+
end
|
|
1437
1423
|
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1424
|
+
file_id = output_xml.xpath('//ns2:FileId', 'ns2' =>'http://object.api.zuora.com/').text
|
|
1425
|
+
export_file = get_file(:url => self.fileURL(file_id))
|
|
1426
|
+
export_file_path = export_file.path
|
|
1427
|
+
|
|
1428
|
+
if extract && zip
|
|
1429
|
+
require "zip"
|
|
1430
|
+
new_path = export_file_path.partition('.zip').first
|
|
1431
|
+
zipped = Zip::File.open(export_file_path)
|
|
1432
|
+
file_handle = zipped.entries.first
|
|
1433
|
+
file_handle.extract(new_path)
|
|
1434
|
+
File.delete(export_file_path)
|
|
1435
|
+
return new_path
|
|
1436
|
+
else
|
|
1437
|
+
return export_file_path
|
|
1438
|
+
end
|
|
1439
|
+
rescue ZuoraAPI::Exceptions::ZuoraAPISessionError => ex
|
|
1440
|
+
if !(tries -= 1).zero?
|
|
1441
|
+
Rails.logger.info("Export call failed - Trace ID: #{z_track_id}")
|
|
1442
|
+
self.new_session
|
|
1443
|
+
retry
|
|
1444
|
+
end
|
|
1445
|
+
raise ex
|
|
1444
1446
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
+
rescue ZuoraAPI::Exceptions::ZuoraUnexpectedError => ex
|
|
1448
|
+
if !(tries -= 1).zero?
|
|
1449
|
+
Rails.logger.info("Trace ID: #{z_track_id} UnexpectedError, will retry after 10 seconds")
|
|
1450
|
+
sleep(self.timeout_sleep)
|
|
1451
|
+
retry
|
|
1447
1452
|
end
|
|
1453
|
+
raise ex
|
|
1454
|
+
|
|
1455
|
+
rescue *(CONNECTION_EXCEPTIONS + CONNECTION_READ_EXCEPTIONS) => ex
|
|
1456
|
+
if !(tries -= 1).zero?
|
|
1457
|
+
Rails.logger.info("Trace ID: #{z_track_id} Timed out will retry after 5 seconds")
|
|
1458
|
+
sleep(self.timeout_sleep)
|
|
1459
|
+
retry
|
|
1460
|
+
end
|
|
1461
|
+
raise ex
|
|
1448
1462
|
end
|
|
1449
1463
|
|
|
1450
1464
|
def query(query, parse = false)
|
|
1451
|
-
output_xml, input_xml = self.soap_call(
|
|
1465
|
+
output_xml, input_xml = self.soap_call(debug: false, timeout_retry: true) do |xml|
|
|
1452
1466
|
xml['ns1'].query do
|
|
1453
1467
|
xml['ns1'].queryString query
|
|
1454
1468
|
end
|