zuora_connect 1.5.30 → 1.5.32
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/app/models/zuora_connect/app_instance_base.rb +579 -482
- data/config/initializers/object_method_hooks.rb +27 -0
- data/config/initializers/redis.rb +2 -2
- data/db/migrate/20180301052853_add_catalog_attempted_at.rb +5 -0
- data/lib/zuora_connect/configuration.rb +0 -1
- data/lib/zuora_connect/controllers/helpers.rb +10 -6
- data/lib/zuora_connect/exceptions.rb +2 -0
- data/lib/zuora_connect/version.rb +1 -2
- metadata +48 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 616c086c694841b9daf5d37a6183caef1a19dcc3
|
|
4
|
+
data.tar.gz: 1d1c561faa6707b7a33d8f19707a34c4b95493b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71415ba2c52c037051fb5f2beec09ed22f9f88283769222e8ca5b43dd9fe77333f83e57cdb74663d2549b86146e7940d65896932969c379663b031ad6deb651d
|
|
7
|
+
data.tar.gz: 529ca7bf5106cc79759c2ef1c2488257c54348c994bf949dbadabd3ea0f3bded5aff30adb0e3990f3c40f2c8e69435cb3bcd93f0c39d7ceb9819dff6d2724e8c
|
|
@@ -5,6 +5,12 @@ module ZuoraConnect
|
|
|
5
5
|
self.table_name = "zuora_connect_app_instances"
|
|
6
6
|
attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version
|
|
7
7
|
|
|
8
|
+
REFRESH_TIMEOUT = 2.minute #Used to determine how long to wait on current refresh call before executing another
|
|
9
|
+
INSTANCE_REFRESH_WINDOW = 30.minutes #Used to set how how long till app starts attempting to refresh cached task connect data
|
|
10
|
+
INSTANCE_REDIS_CACHE_PERIOD = 60.minutes #Used to determine how long to cached task data will live for
|
|
11
|
+
API_LIMIT_TIMEOUT = 2.minutes #Used to set the default for expiring timeout when api rate limiting is in effect
|
|
12
|
+
BLANK_OBJECT_ID_LOOKUP = 'BlankValueSupplied'
|
|
13
|
+
|
|
8
14
|
def init
|
|
9
15
|
@options = Hash.new
|
|
10
16
|
@logins = Hash.new
|
|
@@ -12,594 +18,693 @@ module ZuoraConnect
|
|
|
12
18
|
self.attr_builder("timezone", ZuoraConnect.configuration.default_time_zone)
|
|
13
19
|
self.attr_builder("locale", ZuoraConnect.configuration.default_locale)
|
|
14
20
|
PaperTrail.whodunnit = "Backend" if defined?(PaperTrail)
|
|
15
|
-
|
|
21
|
+
if INSTANCE_REFRESH_WINDOW > INSTANCE_REDIS_CACHE_PERIOD
|
|
22
|
+
raise "The instance refresh window cannot be greater than the instance cache period"
|
|
23
|
+
end
|
|
24
|
+
self.apartment_switch(nil, true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def apartment_switch(method = nil, migrate = false)
|
|
28
|
+
begin
|
|
16
29
|
Apartment::Tenant.switch!(self.id) if self.persisted?
|
|
17
30
|
rescue Apartment::TenantNotFound => ex
|
|
18
31
|
Apartment::Tenant.create(self.id.to_s)
|
|
19
32
|
retry
|
|
20
33
|
end
|
|
21
|
-
if
|
|
34
|
+
if migrate && ActiveRecord::Migrator.needs_migration?
|
|
22
35
|
Apartment::Migrator.migrate(self.id)
|
|
23
36
|
end
|
|
24
37
|
Thread.current[:appinstance] = self
|
|
25
38
|
end
|
|
26
39
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token, holding_pattern: false)
|
|
41
|
+
@api_version = "v2"
|
|
42
|
+
@username = username
|
|
43
|
+
@password = password
|
|
44
|
+
@last_refresh = session["#{self.id}::last_refresh"]
|
|
45
|
+
|
|
46
|
+
## DEV MODE TASK DATA MOCKUP
|
|
47
|
+
if ZuoraConnect.configuration.mode != "Production"
|
|
48
|
+
mock_task_data = {
|
|
49
|
+
"mode" => ZuoraConnect.configuration.dev_mode_mode
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
case ZuoraConnect.configuration.dev_mode_options.class
|
|
53
|
+
when Hash
|
|
54
|
+
@options = ZuoraConnect.configuration.dev_mode_options
|
|
55
|
+
when Array
|
|
56
|
+
mock_task_data["options"] = ZuoraConnect.configuration.dev_mode_options
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
ZuoraConnect.configuration.dev_mode_logins.each do |k,v|
|
|
60
|
+
v = v.merge({"entities": [] }) if !v.keys.include?("entities")
|
|
61
|
+
mock_task_data[k] = v
|
|
39
62
|
end
|
|
63
|
+
|
|
64
|
+
build_task(mock_task_data, session)
|
|
40
65
|
else
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
end
|
|
66
|
+
time_expire = (session["#{self.id}::last_refresh"] || Time.now).to_i - INSTANCE_REFRESH_WINDOW.ago.to_i
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Redis.current.del("Deleted:#{self.id}")
|
|
50
|
-
end
|
|
51
|
-
end
|
|
68
|
+
if session.empty?
|
|
69
|
+
Rails.logger.info("[#{self.id}] REFRESHING - Session Empty")
|
|
70
|
+
raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
|
|
71
|
+
self.refresh(session)
|
|
52
72
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
73
|
+
elsif (self.id != session["appInstance"].to_i)
|
|
74
|
+
Rails.logger.info("[#{self.id}] REFRESHING - AppInstance ID(#{self.id}) does not match session id(#{session["appInstance"].to_i})")
|
|
75
|
+
raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
|
|
76
|
+
self.refresh(session)
|
|
77
|
+
|
|
78
|
+
elsif session["#{self.id}::task_data"].blank?
|
|
79
|
+
Rails.logger.info("[#{self.id}] REFRESHING - Task Data Blank")
|
|
80
|
+
raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
|
|
81
|
+
self.refresh(session)
|
|
82
|
+
|
|
83
|
+
elsif session["#{self.id}::last_refresh"].blank?
|
|
84
|
+
Rails.logger.info("[#{self.id}] REFRESHING - No Time on Cookie")
|
|
85
|
+
raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
|
|
86
|
+
self.refresh(session)
|
|
87
|
+
|
|
88
|
+
# If the cache is expired and we can aquire a refresh lock
|
|
89
|
+
elsif (session["#{self.id}::last_refresh"].to_i < INSTANCE_REFRESH_WINDOW.ago.to_i) && self.mark_for_refresh
|
|
90
|
+
Rails.logger.info("[#{self.id}] REFRESHING - Session Old by #{time_expire.abs} second")
|
|
91
|
+
self.refresh(session)
|
|
58
92
|
else
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Rails.logger.
|
|
63
|
-
return rescue_return
|
|
93
|
+
if time_expire < 0
|
|
94
|
+
Rails.logger.info(["[#{self.id}] REBUILDING - Expired by #{time_expire} seconds", self.marked_for_refresh? ? " cache updating as of #{self.reset_mark_refreshed_at} seconds ago" : nil].compact.join(','))
|
|
95
|
+
else
|
|
96
|
+
Rails.logger.info("[#{self.id}] REBUILDING - Expires in #{time_expire} seconds")
|
|
64
97
|
end
|
|
98
|
+
build_task(session["#{self.id}::task_data"], session)
|
|
65
99
|
end
|
|
66
|
-
rescue JSON::ParserError => ex
|
|
67
|
-
Rails.logger.fatal('Error Parsing')
|
|
68
|
-
return rescue_return
|
|
69
100
|
end
|
|
101
|
+
begin
|
|
102
|
+
I18n.locale = self.locale
|
|
103
|
+
rescue I18n::InvalidLocale => ex
|
|
104
|
+
Rails.logger.error("Invalid Locale: #{ex.message}")
|
|
105
|
+
end
|
|
106
|
+
Time.zone = self.timezone
|
|
107
|
+
return self
|
|
108
|
+
rescue ZuoraConnect::Exceptions::HoldingPattern => ex
|
|
109
|
+
while self.marked_for_refresh?
|
|
110
|
+
Rails.logger.info("[#{self.id}] Holding - Expires in #{self.reset_mark_expires_at}")
|
|
111
|
+
sleep(5)
|
|
112
|
+
end
|
|
113
|
+
self.reload_attributes([:refresh_token, :oauth_expires_at, :access_token])
|
|
114
|
+
session = self.data_lookup(session: session)
|
|
115
|
+
retry
|
|
70
116
|
end
|
|
71
117
|
|
|
72
|
-
def
|
|
73
|
-
|
|
118
|
+
def refresh(session = nil)
|
|
119
|
+
refresh_count ||= 0
|
|
74
120
|
|
|
75
|
-
|
|
76
|
-
|
|
121
|
+
start = Time.now
|
|
122
|
+
response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
|
|
123
|
+
response_time = Time.now - start
|
|
124
|
+
|
|
125
|
+
Rails.logger.info("[#{self.id}] REFRESH TASK - Connect Task Info Request Time #{response_time.round(2).to_s}")
|
|
126
|
+
if response.code == 200
|
|
127
|
+
build_task(JSON.parse(response.body), session)
|
|
128
|
+
@last_refresh = Time.now.to_i
|
|
129
|
+
self.cache_app_instance
|
|
130
|
+
self.reset_mark_for_refresh
|
|
77
131
|
else
|
|
78
|
-
|
|
132
|
+
Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed Code #{response.code}")
|
|
133
|
+
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
|
|
79
134
|
end
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
|
|
85
|
-
key_generator = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base, iterations: key_iter_num)
|
|
86
|
-
secret, sign_secret = [key_generator.generate_key(salt), key_generator.generate_key(signed_salt)]
|
|
87
|
-
return ActiveSupport::MessageEncryptor.new(secret, sign_secret)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def api_limit(start: true, time: 2.minutes.to_i)
|
|
91
|
-
if start
|
|
92
|
-
Redis.current.set("APILimits:#{self.id}", true)
|
|
93
|
-
Redis.current.expire("APILimits:#{self.id}", time)
|
|
135
|
+
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
|
|
136
|
+
if (refresh_count += 1) < 3
|
|
137
|
+
Rails.logger.info("[#{self.id}] REFRESH TASK - #{ex.class} Retrying(#{refresh_count})")
|
|
138
|
+
retry
|
|
94
139
|
else
|
|
95
|
-
|
|
140
|
+
Rails.logger.fatal("[#{self.id}] REFRESH TASK - #{ex.class} Failed #{refresh_count}x")
|
|
141
|
+
raise
|
|
96
142
|
end
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return Redis.current.get("resque:PauseQueue:#{self.id}").to_bool
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def queue_pause(time: nil)
|
|
108
|
-
if time.present?
|
|
109
|
-
raise "Time must be fixnum of seconds." if time.class != Fixnum
|
|
110
|
-
Redis.current.set("resque:PauseQueue:#{self.id}", true)
|
|
111
|
-
Redis.current.expire("resque:PauseQueue:#{self.id}", time)
|
|
143
|
+
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
|
|
144
|
+
if (refresh_count += 1) < 3
|
|
145
|
+
Rails.logger.info("[#{self.id}] REFRESH TASK - Failed Retrying(#{refresh_count})")
|
|
146
|
+
if ex.code == 401
|
|
147
|
+
self.refresh_oauth
|
|
148
|
+
end
|
|
149
|
+
retry
|
|
112
150
|
else
|
|
113
|
-
|
|
151
|
+
Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed #{refresh_count}x")
|
|
152
|
+
raise
|
|
114
153
|
end
|
|
115
154
|
end
|
|
116
155
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
156
|
+
#### START Task Mathods ####
|
|
157
|
+
def build_task(task_data, session)
|
|
158
|
+
@task_data = task_data
|
|
159
|
+
@mode = @task_data["mode"]
|
|
160
|
+
@task_data.each do |k,v|
|
|
161
|
+
if k.match(/^(.*)_login$/)
|
|
162
|
+
tmp = ZuoraConnect::Login.new(v)
|
|
163
|
+
if !session.nil? && v["tenant_type"] == "Zuora"
|
|
164
|
+
if tmp.entities.size > 0
|
|
165
|
+
tmp.entities.each do |value|
|
|
166
|
+
entity_id = value["id"]
|
|
167
|
+
tmp.client(entity_id).current_session = session["#{self.id}::#{k}::#{entity_id}:session"] if !session.nil? && v["tenant_type"] == "Zuora" && session["#{self.id}::#{k}::#{entity_id}:session"]
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
tmp.client.current_session = session["#{self.id}::#{k}:session"] if !session.nil? && v["tenant_type"] == "Zuora" && session["#{self.id}::#{k}:session"]
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
@logins[k] = tmp
|
|
174
|
+
self.attr_builder(k, @logins[k])
|
|
175
|
+
elsif k == "options"
|
|
176
|
+
v.each do |opt|
|
|
177
|
+
@options[opt["config_name"]] = opt
|
|
178
|
+
end
|
|
179
|
+
elsif k == "user_settings"
|
|
180
|
+
self.timezone = v["timezone"]
|
|
181
|
+
self.locale = v["local"]
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
124
185
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
186
|
+
def updateOption(optionId, value)
|
|
187
|
+
return HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
|
|
188
|
+
end
|
|
128
189
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
# child_objects: Whether to include child objects of the object in question.
|
|
134
|
-
# cache: Store individual "1" object lookup in redis for caching.
|
|
135
|
-
def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)
|
|
136
|
-
entity_reference = entity_id.blank? ? 'Default' : entity_id
|
|
190
|
+
#This can update an existing login, add a new login, change to another existing login
|
|
191
|
+
#EXAMPLE: {"name": "ftp_login_14","username": "ftplogin7","tenant_type": "Custom","password": "test2","url": "www.ftp.com","custom_data": { "path": "/var/usr/test"}}
|
|
192
|
+
def update_logins(options)
|
|
193
|
+
update_login_count ||= 0
|
|
137
194
|
|
|
138
|
-
|
|
139
|
-
|
|
195
|
+
response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/logins",:body => {:access_token => self.username}.merge(options))
|
|
196
|
+
if response.code == 200
|
|
197
|
+
return JSON.parse(response.body)
|
|
198
|
+
else
|
|
199
|
+
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
|
|
200
|
+
end
|
|
201
|
+
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError
|
|
202
|
+
if (update_login_count += 1) < 3
|
|
203
|
+
retry
|
|
204
|
+
else
|
|
205
|
+
raise
|
|
206
|
+
end
|
|
207
|
+
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
|
|
208
|
+
if (update_login_count += 1) < 3
|
|
209
|
+
if ex.code == 401
|
|
210
|
+
self.refresh_oauth
|
|
211
|
+
end
|
|
212
|
+
retry
|
|
213
|
+
else
|
|
214
|
+
raise
|
|
215
|
+
end
|
|
140
216
|
end
|
|
217
|
+
#### END Task Mathods ####
|
|
141
218
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
219
|
+
#### START Connect OAUTH methods ####
|
|
220
|
+
def check_oauth_state(method)
|
|
221
|
+
#Refresh token if already expired
|
|
222
|
+
if self.oauth_expired?
|
|
223
|
+
Rails.logger.debug("[#{self.id}] Before '#{method}' method, Oauth expired")
|
|
224
|
+
self.refresh_oauth
|
|
225
|
+
end
|
|
145
226
|
end
|
|
146
227
|
|
|
147
|
-
|
|
148
|
-
|
|
228
|
+
def oauth_expired?
|
|
229
|
+
return self.oauth_expires_at.present? ? (self.oauth_expires_at < Time.now) : true
|
|
149
230
|
end
|
|
150
231
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
232
|
+
def refresh_oauth
|
|
233
|
+
refresh_oauth_count ||= 0
|
|
234
|
+
|
|
235
|
+
start = Time.now
|
|
236
|
+
params = {
|
|
237
|
+
:grant_type => "refresh_token",
|
|
238
|
+
:redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
|
|
239
|
+
:refresh_token => self.refresh_token
|
|
240
|
+
}
|
|
241
|
+
response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token",:body => params)
|
|
242
|
+
response_time = Time.now - start
|
|
243
|
+
Rails.logger.info("[#{self.id}] REFRESH OAUTH - In #{response_time.round(2).to_s}")
|
|
244
|
+
|
|
245
|
+
if response.code == 200
|
|
246
|
+
response_body = JSON.parse(response.body)
|
|
247
|
+
|
|
248
|
+
self.refresh_token = response_body["refresh_token"]
|
|
249
|
+
self.access_token = response_body["access_token"]
|
|
250
|
+
self.oauth_expires_at = Time.at(response_body["created_at"].to_i) + response_body["expires_in"].seconds
|
|
251
|
+
self.save(:validate => false)
|
|
162
252
|
else
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
"SELECT "\
|
|
166
|
-
"(catalog #> '{%s, %s}') #{child_objects ? '' : '- \'productRatePlans\''} AS item "\
|
|
167
|
-
"FROM "\
|
|
168
|
-
"\"public\".\"zuora_connect_app_instances\" "\
|
|
169
|
-
"WHERE "\
|
|
170
|
-
"\"id\" = %s" % [entity_reference, object_id, self.id]
|
|
171
|
-
elsif object_id.class == Array
|
|
172
|
-
string =
|
|
173
|
-
"SELECT "\
|
|
174
|
-
"json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
|
|
175
|
-
"FROM "\
|
|
176
|
-
"\"public\".\"zuora_connect_app_instances\", "\
|
|
177
|
-
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
|
|
178
|
-
"WHERE "\
|
|
179
|
-
"\"product_id\" IN (\'%s\') AND "\
|
|
180
|
-
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
|
|
181
|
-
end
|
|
253
|
+
Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed Code #{response.code}")
|
|
254
|
+
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Refreshing Access Token", response.body, response.code)
|
|
182
255
|
end
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"SELECT "\
|
|
188
|
-
"json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
|
|
189
|
-
"FROM "\
|
|
190
|
-
"\"public\".\"zuora_connect_app_instances\", "\
|
|
191
|
-
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
192
|
-
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
|
|
193
|
-
"WHERE "\
|
|
194
|
-
"\"id\" = %s" % [entity_reference, self.id]
|
|
256
|
+
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
|
|
257
|
+
if (refresh_oauth_count += 1) < 3
|
|
258
|
+
Rails.logger.info("[#{self.id}] REFRESH OAUTH - #{ex.class} Retrying(#{refresh_oauth_count})")
|
|
259
|
+
retry
|
|
195
260
|
else
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"SELECT "\
|
|
199
|
-
"(catalog #> '{%s, %s, productRatePlans, %s}') #{child_objects ? '' : '- \'productRatePlanCharges\''} AS item "\
|
|
200
|
-
"FROM "\
|
|
201
|
-
"\"public\".\"zuora_connect_app_instances\" "\
|
|
202
|
-
"WHERE "\
|
|
203
|
-
"\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_id, self.id]
|
|
204
|
-
elsif object_id.class == Array
|
|
205
|
-
string =
|
|
206
|
-
"SELECT "\
|
|
207
|
-
"json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
|
|
208
|
-
"FROM "\
|
|
209
|
-
"\"public\".\"zuora_connect_app_instances\", "\
|
|
210
|
-
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
211
|
-
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
|
|
212
|
-
"WHERE "\
|
|
213
|
-
"\"rateplan_id\" IN (\'%s\') AND "\
|
|
214
|
-
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
|
|
215
|
-
end
|
|
261
|
+
Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - #{ex.class} Failed #{refresh_oauth_count}x")
|
|
262
|
+
raise
|
|
216
263
|
end
|
|
264
|
+
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
|
|
265
|
+
sleep(5)
|
|
266
|
+
self.reload_attributes([:refresh_token, :oauth_expires_at, :access_token]) #Reload only the refresh token for retry
|
|
217
267
|
|
|
218
|
-
|
|
219
|
-
if
|
|
220
|
-
string =
|
|
221
|
-
"SELECT "\
|
|
222
|
-
"json_object_agg(charge_id, charge) as item "\
|
|
223
|
-
"FROM "\
|
|
224
|
-
"\"public\".\"zuora_connect_app_instances\", "\
|
|
225
|
-
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
226
|
-
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
|
|
227
|
-
"jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
|
|
228
|
-
"WHERE "\
|
|
229
|
-
"\"id\" = %s" % [entity_reference, self.id]
|
|
230
|
-
else
|
|
231
|
-
if object_id.class == String
|
|
232
|
-
string =
|
|
233
|
-
"SELECT "\
|
|
234
|
-
"catalog #> '{%s, %s, productRatePlans, %s, productRatePlanCharges, %s}' AS item "\
|
|
235
|
-
"FROM "\
|
|
236
|
-
"\"public\".\"zuora_connect_app_instances\" "\
|
|
237
|
-
"WHERE "\
|
|
238
|
-
"\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_hierarchy['productRatePlanId'], object_id, self.id]
|
|
268
|
+
#After reload, if nolonger expired return
|
|
269
|
+
return if !self.oauth_expired?
|
|
239
270
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
247
|
-
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
|
|
248
|
-
"jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
|
|
249
|
-
"WHERE "\
|
|
250
|
-
"\"charge_id\" IN (\'%s\') AND "\
|
|
251
|
-
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
|
|
252
|
-
end
|
|
271
|
+
if (refresh_oauth_count += 1) < 3
|
|
272
|
+
Rails.logger.info("[#{self.id}] REFRESH OAUTH - Failed Retrying(#{refresh_oauth_count})")
|
|
273
|
+
retry
|
|
274
|
+
else
|
|
275
|
+
Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed #{refresh_oauth_count}x")
|
|
276
|
+
raise
|
|
253
277
|
end
|
|
254
|
-
else
|
|
255
|
-
raise "Available objects include [:product, :rateplan, :charge]"
|
|
256
278
|
end
|
|
279
|
+
#### END Connect OAUTH methods ####
|
|
257
280
|
|
|
258
|
-
|
|
281
|
+
#### START AppInstance Temporary Persistance Methods ####
|
|
282
|
+
def marked_for_refresh?
|
|
283
|
+
return defined?(Redis.current) ? Redis.current.get("AppInstance:#{self.id}:Refreshing").to_bool : false
|
|
284
|
+
end
|
|
259
285
|
|
|
260
|
-
|
|
261
|
-
Redis.current.
|
|
262
|
-
Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog)) if cache
|
|
286
|
+
def reset_mark_for_refresh
|
|
287
|
+
Redis.current.del("AppInstance:#{self.id}:Refreshing") if defined?(Redis.current)
|
|
263
288
|
end
|
|
264
289
|
|
|
265
|
-
|
|
266
|
-
|
|
290
|
+
def reset_mark_refreshed_at
|
|
291
|
+
return defined?(Redis.current) ? REFRESH_TIMEOUT.to_i - Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
|
|
292
|
+
end
|
|
267
293
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
def reset_mark_expires_at
|
|
295
|
+
return defined?(Redis.current) ? Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
|
|
296
|
+
end
|
|
271
297
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
self.logins.each do |name, login|
|
|
275
|
-
results << login if login.tenant_type == type
|
|
298
|
+
def mark_for_refresh
|
|
299
|
+
return defined?(Redis.current) ? Redis.current.set("AppInstance:#{self.id}:Refreshing", true, {:nx => true, :ex => REFRESH_TIMEOUT.to_i}) : true
|
|
276
300
|
end
|
|
277
|
-
return results
|
|
278
|
-
end
|
|
279
301
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
302
|
+
def data_lookup(session: {})
|
|
303
|
+
if defined?(PaperTrail)
|
|
304
|
+
PaperTrail.whodunnit = session["#{self.id}::user::email"].present? ? session["#{self.id}::user::email"] : nil if session.present?
|
|
305
|
+
end
|
|
306
|
+
if defined?(Redis.current)
|
|
307
|
+
cached_instance = Redis.current.get("AppInstance:#{self.id}")
|
|
308
|
+
if cached_instance.blank?
|
|
309
|
+
Rails.logger.debug("[#{self.id}] Cached AppInstance Missing")
|
|
310
|
+
return session
|
|
311
|
+
else
|
|
312
|
+
Rails.logger.debug("[#{self.id}] Cached AppInstance Found")
|
|
313
|
+
return decrypt_data(data: cached_instance, rescue_return: session)
|
|
314
|
+
end
|
|
315
|
+
else
|
|
316
|
+
return session
|
|
317
|
+
end
|
|
318
|
+
end
|
|
284
319
|
|
|
285
|
-
|
|
320
|
+
def cache_app_instance
|
|
321
|
+
if defined?(Redis.current)
|
|
322
|
+
#Task data must be present and the last refresh cannot be old. We dont want to overwite new cache data with old
|
|
323
|
+
if self.task_data.present? && (self.last_refresh.to_i > INSTANCE_REFRESH_WINDOW.ago.to_i)
|
|
324
|
+
Rails.logger.info("[#{self.id}] Caching AppInstance")
|
|
325
|
+
Redis.current.setex("AppInstance:#{self.id}", INSTANCE_REDIS_CACHE_PERIOD.to_i, encrypt_data(data: self.save_data))
|
|
326
|
+
end
|
|
327
|
+
Redis.current.del("Deleted:#{self.id}")
|
|
328
|
+
end
|
|
329
|
+
end
|
|
286
330
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
331
|
+
def save_data(session = Hash.new)
|
|
332
|
+
self.logins.each do |key, login|
|
|
333
|
+
if login.tenant_type == "Zuora"
|
|
334
|
+
if login.available_entities.size > 1 && Rails.application.config.session_store != ActionDispatch::Session::CookieStore
|
|
335
|
+
login.available_entities.each do |entity_key|
|
|
336
|
+
session["#{self.id}::#{key}::#{entity_key}:session"] = login.client(entity_key).current_session
|
|
337
|
+
end
|
|
338
|
+
else
|
|
339
|
+
session["#{self.id}::#{key}:session"] = login.client.current_session
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
session["#{self.id}::task_data"] = self.task_data
|
|
344
|
+
session["#{self.id}::last_refresh"] = self.last_refresh
|
|
345
|
+
session["appInstance"] = self.id
|
|
346
|
+
return session
|
|
347
|
+
end
|
|
290
348
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
Rails.
|
|
295
|
-
|
|
296
|
-
|
|
349
|
+
def encryptor
|
|
350
|
+
# Default values for Rails 4 apps
|
|
351
|
+
key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
|
|
352
|
+
key_generator = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base, iterations: key_iter_num)
|
|
353
|
+
secret, sign_secret = [key_generator.generate_key(salt), key_generator.generate_key(signed_salt)]
|
|
354
|
+
return ActiveSupport::MessageEncryptor.new(secret, sign_secret)
|
|
355
|
+
end
|
|
297
356
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
357
|
+
def decrypt_data(data: nil, rescue_return: nil)
|
|
358
|
+
return data if data.blank?
|
|
359
|
+
begin
|
|
360
|
+
if Rails.env == 'development'
|
|
361
|
+
return JSON.parse(data)
|
|
362
|
+
else
|
|
363
|
+
begin
|
|
364
|
+
return JSON.parse(encryptor.decrypt_and_verify(CGI::unescape(data)))
|
|
365
|
+
rescue ActiveSupport::MessageVerifier::InvalidSignature => ex
|
|
366
|
+
Rails.logger.fatal('Error Decrypting')
|
|
367
|
+
return rescue_return
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
rescue JSON::ParserError => ex
|
|
371
|
+
Rails.logger.fatal('Error Parsing')
|
|
372
|
+
return rescue_return
|
|
301
373
|
end
|
|
374
|
+
end
|
|
302
375
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
rateplans = {}
|
|
376
|
+
def encrypt_data(data: nil)
|
|
377
|
+
return data if data.blank?
|
|
306
378
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
379
|
+
if Rails.env == 'development'
|
|
380
|
+
return data.to_json
|
|
381
|
+
else
|
|
382
|
+
return encryptor.encrypt_and_sign(data.to_json)
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
#### END AppInstance Temporary Persistance Methods ####
|
|
310
386
|
|
|
311
|
-
|
|
312
|
-
|
|
387
|
+
### START Resque Helping Methods ####
|
|
388
|
+
def api_limit(start: true, time: API_LIMIT_TIMEOUT.to_i)
|
|
389
|
+
if start
|
|
390
|
+
Redis.current.setex("APILimits:#{self.id}", time, true)
|
|
391
|
+
else
|
|
392
|
+
Redis.current.del("APILimits:#{self.id}")
|
|
393
|
+
end
|
|
394
|
+
end
|
|
313
395
|
|
|
314
|
-
|
|
315
|
-
|
|
396
|
+
def api_limit?
|
|
397
|
+
return Redis.current.get("APILimits:#{self.id}").to_bool
|
|
398
|
+
end
|
|
316
399
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
product["productRatePlans"] = rateplans
|
|
400
|
+
def queue_paused?
|
|
401
|
+
return Redis.current.get("resque:PauseQueue:#{self.id}").to_bool
|
|
402
|
+
end
|
|
321
403
|
|
|
322
|
-
|
|
404
|
+
def queue_pause(time: nil)
|
|
405
|
+
if time.present?
|
|
406
|
+
raise "Time must be fixnum of seconds." if time.class != Fixnum
|
|
407
|
+
Redis.current.setex("resque:PauseQueue:#{self.id}", time, true)
|
|
408
|
+
else
|
|
409
|
+
Redis.current.set("resque:PauseQueue:#{self.id}", true)
|
|
323
410
|
end
|
|
324
411
|
end
|
|
325
412
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if defined?(Redis.current)
|
|
329
|
-
Redis.current.keys("Catalog:#{self.id}:*").each do |key|
|
|
330
|
-
Redis.current.del(key.to_s)
|
|
331
|
-
end
|
|
413
|
+
def queue_start
|
|
414
|
+
Redis.current.del("resque:PauseQueue:#{self.id}")
|
|
332
415
|
end
|
|
333
|
-
|
|
334
|
-
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp}\', \'{}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp}\', \'{}\') where "id" = %{id}' % {:id => self.id})
|
|
416
|
+
### END Resque Helping Methods ####
|
|
335
417
|
|
|
336
|
-
|
|
337
|
-
self.
|
|
338
|
-
|
|
418
|
+
### START Catalog Helping Methods #####
|
|
419
|
+
def get_catalog(page_size: 5, zuora_login: self.login_lookup(type: "Zuora").first, entity_id: nil)
|
|
420
|
+
self.update_column(:catalog_update_attempt_at, Time.now.utc)
|
|
339
421
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
422
|
+
entity_reference = entity_id.blank? ? 'Default' : entity_id
|
|
423
|
+
Rails.logger.info("Fetch Catalog")
|
|
424
|
+
Rails.logger.info("Zuora Entity: #{entity_id.blank? ? 'default' : entity_id}")
|
|
343
425
|
|
|
344
|
-
|
|
345
|
-
@api_version = "v2"
|
|
346
|
-
@username = username
|
|
347
|
-
@password = password
|
|
348
|
-
@last_refresh = session["#{self.id}::last_refresh"]
|
|
426
|
+
login = zuora_login.client(entity_reference)
|
|
349
427
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
"mode" => ZuoraConnect.configuration.dev_mode_mode
|
|
354
|
-
}
|
|
428
|
+
old_logger = ActiveRecord::Base.logger
|
|
429
|
+
ActiveRecord::Base.logger = nil
|
|
430
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp}\', \'{}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp}\', \'{}\') where "id" = %{id}' % {:id => self.id})
|
|
355
431
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
432
|
+
response = {'nextPage' => login.rest_endpoint("catalog/products?pageSize=#{page_size}")}
|
|
433
|
+
while !response["nextPage"].blank?
|
|
434
|
+
url = login.rest_endpoint(response["nextPage"].split('/v1/').last)
|
|
435
|
+
Rails.logger.debug("Fetch Catalog URL #{url}")
|
|
436
|
+
output_json, response = login.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true)
|
|
437
|
+
Rails.logger.debug("Fetch Catalog Response Code #{response.code}")
|
|
362
438
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
439
|
+
if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
|
|
440
|
+
Rails.logger.error("Fetch Catalog DATA #{output_json.to_json}")
|
|
441
|
+
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}")
|
|
442
|
+
end
|
|
367
443
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
Rails.logger.info("[#{self.id}] REFRESHING - Session Empty")
|
|
372
|
-
self.refresh(session)
|
|
444
|
+
output_json["products"].each do |product|
|
|
445
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp, %s}\', \'%s\') where "id" = %s' % [product["id"], {"productId" => product["id"]}.to_json.gsub("'", "''"), self.id])
|
|
446
|
+
rateplans = {}
|
|
373
447
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
448
|
+
product["productRatePlans"].each do |rateplan|
|
|
449
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp, %s}\', \'%s\') where "id" = %s' % [rateplan["id"], {"productId" => product["id"], "productRatePlanId" => rateplan["id"]}.to_json.gsub("'", "''"), self.id])
|
|
450
|
+
charges = {}
|
|
377
451
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
self.refresh(session)
|
|
452
|
+
rateplan["productRatePlanCharges"].each do |charge|
|
|
453
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp, %s}\', \'%s\') where "id" = %s' % [charge["id"], {"productId" => product["id"], "productRatePlanId" => rateplan["id"], "productRatePlanChargeId" => charge["id"]}.to_json.gsub("'", "''"), self.id])
|
|
381
454
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
self.refresh(session)
|
|
455
|
+
charges[charge["id"]] = charge.merge({"productId" => product["id"], "productName" => product["name"], "productRatePlanId" => rateplan["id"], "productRatePlanName" => rateplan["name"] })
|
|
456
|
+
end
|
|
385
457
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
458
|
+
rateplan["productRatePlanCharges"] = charges
|
|
459
|
+
rateplans[rateplan["id"]] = rateplan.merge({"productId" => product["id"], "productName" => product["name"]})
|
|
460
|
+
end
|
|
461
|
+
product["productRatePlans"] = rateplans
|
|
389
462
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
build_task(session["#{self.id}::task_data"], session)
|
|
463
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp, %s}\', \'%s\') where "id" = %s' % [product["id"], product.to_json.gsub("'", "''"), self.id])
|
|
464
|
+
end
|
|
393
465
|
end
|
|
394
|
-
end
|
|
395
|
-
begin
|
|
396
|
-
I18n.locale = self.locale
|
|
397
|
-
rescue I18n::InvalidLocale => ex
|
|
398
|
-
Rails.logger.error("Invalid Locale: #{ex.message}")
|
|
399
|
-
end
|
|
400
|
-
Time.zone = self.timezone
|
|
401
|
-
Thread.current[:appinstance] = self
|
|
402
|
-
return self
|
|
403
|
-
end
|
|
404
466
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
session["#{self.id}::#{key}::#{entity_key}:session"] = login.client(entity_key).current_session
|
|
411
|
-
end
|
|
412
|
-
else
|
|
413
|
-
session["#{self.id}::#{key}:session"] = login.client.current_session
|
|
467
|
+
# Move from tmp to actual
|
|
468
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{%{entity}}\', "catalog" #> \'{tmp}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{%{entity}}\', "catalog_mapping" #> \'{tmp}\') where "id" = %{id}' % {:entity => entity_reference, :id => self.id})
|
|
469
|
+
if defined?(Redis.current)
|
|
470
|
+
Redis.current.keys("Catalog:#{self.id}:*").each do |key|
|
|
471
|
+
Redis.current.del(key.to_s)
|
|
414
472
|
end
|
|
415
473
|
end
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
session["#{self.id}::last_refresh"] = self.last_refresh
|
|
419
|
-
session["appInstance"] = self.id
|
|
420
|
-
return session
|
|
421
|
-
end
|
|
474
|
+
# Clear tmp holder
|
|
475
|
+
ActiveRecord::Base.connection.execute('UPDATE "public"."zuora_connect_app_instances" SET "catalog" = jsonb_set("catalog", \'{tmp}\', \'{}\'), "catalog_mapping" = jsonb_set("catalog_mapping", \'{tmp}\', \'{}\') where "id" = %{id}' % {:id => self.id})
|
|
422
476
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
self.refresh_oauth if self.oauth_expired?
|
|
427
|
-
return HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
|
|
428
|
-
else
|
|
429
|
-
return false
|
|
430
|
-
end
|
|
431
|
-
end
|
|
477
|
+
ActiveRecord::Base.logger = old_logger
|
|
478
|
+
self.update_column(:catalog_updated_at, Time.now.utc)
|
|
479
|
+
self.touch
|
|
432
480
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
#This can update an existing login
|
|
436
|
-
#This can add a new login
|
|
437
|
-
#This can change to another existing login
|
|
438
|
-
def update_logins(options)
|
|
439
|
-
#Refresh token if already expired
|
|
440
|
-
self.refresh_oauth if self.oauth_expired?
|
|
441
|
-
|
|
442
|
-
count ||= 0
|
|
443
|
-
response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/logins",:body => {:access_token => self.username}.merge(options))
|
|
444
|
-
if response.code == 200
|
|
445
|
-
return JSON.parse(response.body)
|
|
446
|
-
else
|
|
447
|
-
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
|
|
481
|
+
# DO NOT RETURN CATALOG. THIS IS NOT SCALABLE WITH LARGE CATALOGS. USE THE CATALOG_LOOKUP method provided
|
|
482
|
+
return true
|
|
448
483
|
end
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
else
|
|
453
|
-
raise
|
|
484
|
+
|
|
485
|
+
def catalog_outdated?(time: Time.now - 12.hours)
|
|
486
|
+
return self.catalog_updated_at.blank? || (self.catalog_updated_at < time)
|
|
454
487
|
end
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
self.refresh_oauth
|
|
459
|
-
end
|
|
460
|
-
retry
|
|
461
|
-
else
|
|
462
|
-
raise
|
|
488
|
+
|
|
489
|
+
def catalog_loaded?
|
|
490
|
+
return ActiveRecord::Base.connection.execute('SELECT id FROM "public"."zuora_connect_app_instances" WHERE "id" = %s AND catalog = \'{}\' LIMIT 1' % [self.id]).first.nil?
|
|
463
491
|
end
|
|
464
|
-
end
|
|
465
492
|
|
|
466
|
-
|
|
467
|
-
#
|
|
468
|
-
|
|
493
|
+
# Catalog lookup provides method to lookup zuora catalog efficiently.
|
|
494
|
+
# entity_id: If the using catalog json be field to store multiple entity product catalogs.
|
|
495
|
+
# object: The Object class desired to be returned. Available [:product, :rateplan, :charge]
|
|
496
|
+
# object_id: The id or id's of the object/objects to be returned.
|
|
497
|
+
# child_objects: Whether to include child objects of the object in question.
|
|
498
|
+
# cache: Store individual "1" object lookup in redis for caching.
|
|
499
|
+
def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)
|
|
500
|
+
entity_reference = entity_id.blank? ? 'Default' : entity_id
|
|
469
501
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
response_time = Time.now - start
|
|
502
|
+
if object_id.present? && ![Array, String].include?(object_id.class)
|
|
503
|
+
raise "Object Id can only be a string or an array of strings"
|
|
504
|
+
end
|
|
474
505
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
build_task(JSON.parse(response.body), session)
|
|
479
|
-
else
|
|
480
|
-
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
|
|
481
|
-
end
|
|
482
|
-
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError
|
|
483
|
-
if (count += 1) < 3
|
|
484
|
-
retry
|
|
485
|
-
else
|
|
486
|
-
raise
|
|
487
|
-
end
|
|
488
|
-
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
|
|
489
|
-
if (count += 1) < 3
|
|
490
|
-
if ex.code == 401
|
|
491
|
-
self.refresh_oauth
|
|
506
|
+
if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
|
|
507
|
+
stub_catalog = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}"))
|
|
508
|
+
object_hierarchy = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Hierarchy"))
|
|
492
509
|
end
|
|
493
|
-
retry
|
|
494
|
-
else
|
|
495
|
-
raise
|
|
496
|
-
end
|
|
497
|
-
end
|
|
498
510
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
if
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
if defined?(object_hierarchy)
|
|
512
|
+
object_hierarchy ||= (JSON.parse(ActiveRecord::Base.connection.execute('SELECT catalog_mapping #> \'{%s}\' AS item FROM "public"."zuora_connect_app_instances" WHERE "id" = %s LIMIT 1' % [entity_reference, self.id]).first["item"] || "{}") [object_id] || {"productId" => "SAFTEY", "productRatePlanId" => "SAFTEY", "productRatePlanChargeId" => "SAFTEY"})
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
case object
|
|
516
|
+
when :product
|
|
517
|
+
if object_id.nil?
|
|
518
|
+
string =
|
|
519
|
+
"SELECT "\
|
|
520
|
+
"json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
|
|
521
|
+
"FROM "\
|
|
522
|
+
"\"public\".\"zuora_connect_app_instances\", "\
|
|
523
|
+
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
|
|
524
|
+
"WHERE "\
|
|
525
|
+
"\"id\" = %s" % [entity_reference, self.id]
|
|
526
|
+
else
|
|
527
|
+
if object_id.class == String
|
|
528
|
+
string =
|
|
529
|
+
"SELECT "\
|
|
530
|
+
"(catalog #> '{%s, %s}') #{child_objects ? '' : '- \'productRatePlans\''} AS item "\
|
|
531
|
+
"FROM "\
|
|
532
|
+
"\"public\".\"zuora_connect_app_instances\" "\
|
|
533
|
+
"WHERE "\
|
|
534
|
+
"\"id\" = %s" % [entity_reference, object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id, self.id]
|
|
535
|
+
elsif object_id.class == Array
|
|
536
|
+
string =
|
|
537
|
+
"SELECT "\
|
|
538
|
+
"json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
|
|
539
|
+
"FROM "\
|
|
540
|
+
"\"public\".\"zuora_connect_app_instances\", "\
|
|
541
|
+
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
|
|
542
|
+
"WHERE "\
|
|
543
|
+
"\"product_id\" IN (\'%s\') AND "\
|
|
544
|
+
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
|
|
513
545
|
end
|
|
514
546
|
end
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
547
|
+
|
|
548
|
+
when :rateplan
|
|
549
|
+
if object_id.nil?
|
|
550
|
+
string =
|
|
551
|
+
"SELECT "\
|
|
552
|
+
"json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
|
|
553
|
+
"FROM "\
|
|
554
|
+
"\"public\".\"zuora_connect_app_instances\", "\
|
|
555
|
+
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
556
|
+
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
|
|
557
|
+
"WHERE "\
|
|
558
|
+
"\"id\" = %s" % [entity_reference, self.id]
|
|
559
|
+
else
|
|
560
|
+
if object_id.class == String
|
|
561
|
+
string =
|
|
562
|
+
"SELECT "\
|
|
563
|
+
"(catalog #> '{%s, %s, productRatePlans, %s}') #{child_objects ? '' : '- \'productRatePlanCharges\''} AS item "\
|
|
564
|
+
"FROM "\
|
|
565
|
+
"\"public\".\"zuora_connect_app_instances\" "\
|
|
566
|
+
"WHERE "\
|
|
567
|
+
"\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id, self.id]
|
|
568
|
+
elsif object_id.class == Array
|
|
569
|
+
string =
|
|
570
|
+
"SELECT "\
|
|
571
|
+
"json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
|
|
572
|
+
"FROM "\
|
|
573
|
+
"\"public\".\"zuora_connect_app_instances\", "\
|
|
574
|
+
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
575
|
+
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
|
|
576
|
+
"WHERE "\
|
|
577
|
+
"\"rateplan_id\" IN (\'%s\') AND "\
|
|
578
|
+
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
when :charge
|
|
583
|
+
if object_id.nil?
|
|
584
|
+
string =
|
|
585
|
+
"SELECT "\
|
|
586
|
+
"json_object_agg(charge_id, charge) as item "\
|
|
587
|
+
"FROM "\
|
|
588
|
+
"\"public\".\"zuora_connect_app_instances\", "\
|
|
589
|
+
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
590
|
+
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
|
|
591
|
+
"jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
|
|
592
|
+
"WHERE "\
|
|
593
|
+
"\"id\" = %s" % [entity_reference, self.id]
|
|
594
|
+
else
|
|
595
|
+
if object_id.class == String
|
|
596
|
+
string =
|
|
597
|
+
"SELECT "\
|
|
598
|
+
"catalog #> '{%s, %s, productRatePlans, %s, productRatePlanCharges, %s}' AS item "\
|
|
599
|
+
"FROM "\
|
|
600
|
+
"\"public\".\"zuora_connect_app_instances\" "\
|
|
601
|
+
"WHERE "\
|
|
602
|
+
"\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_hierarchy['productRatePlanId'], object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id , self.id]
|
|
603
|
+
|
|
604
|
+
elsif object_id.class == Array
|
|
605
|
+
string =
|
|
606
|
+
"SELECT "\
|
|
607
|
+
"json_object_agg(charge_id, charge) AS item "\
|
|
608
|
+
"FROM "\
|
|
609
|
+
"\"public\".\"zuora_connect_app_instances\", "\
|
|
610
|
+
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
|
|
611
|
+
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
|
|
612
|
+
"jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
|
|
613
|
+
"WHERE "\
|
|
614
|
+
"\"charge_id\" IN (\'%s\') AND "\
|
|
615
|
+
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
|
|
616
|
+
end
|
|
520
617
|
end
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
self.locale = v["local"]
|
|
618
|
+
else
|
|
619
|
+
raise "Available objects include [:product, :rateplan, :charge]"
|
|
524
620
|
end
|
|
621
|
+
|
|
622
|
+
stub_catalog ||= JSON.parse(ActiveRecord::Base.connection.execute(string).first["item"] || "{}")
|
|
623
|
+
|
|
624
|
+
if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
|
|
625
|
+
Redis.current.set("Catalog:#{self.id}:#{object_id}:Hierarchy", encrypt_data(data: object_hierarchy))
|
|
626
|
+
Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog)) if cache
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
return stub_catalog
|
|
525
630
|
end
|
|
526
|
-
|
|
527
|
-
end
|
|
631
|
+
### END Catalog Helping Methods #####
|
|
528
632
|
|
|
529
|
-
|
|
633
|
+
### START S3 Helping Methods #####
|
|
634
|
+
def s3_client
|
|
635
|
+
require 'aws-sdk-s3'
|
|
636
|
+
if ZuoraConnect.configuration.mode == "Development"
|
|
637
|
+
@s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region,access_key_id: ZuoraConnect.configuration.dev_mode_access_key_id,secret_access_key: ZuoraConnect.configuration.dev_mode_secret_access_key)
|
|
638
|
+
else
|
|
639
|
+
@s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region)
|
|
640
|
+
end
|
|
641
|
+
end
|
|
530
642
|
|
|
531
|
-
|
|
643
|
+
def upload_to_s3(local_file,s3_path = nil)
|
|
644
|
+
s3_path = local_file.split("/").last if s3_path.nil?
|
|
645
|
+
obj = self.s3_client.bucket(ZuoraConnect.configuration.s3_bucket_name).object("#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{s3_path}}")
|
|
646
|
+
obj.upload_file(local_file, :server_side_encryption => 'AES256')
|
|
647
|
+
end
|
|
532
648
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
649
|
+
def get_s3_file_url(key)
|
|
650
|
+
require 'aws-sdk-s3'
|
|
651
|
+
signer = Aws::S3::Presigner.new(client: self.s3_client)
|
|
652
|
+
url = signer.presigned_url(:get_object, bucket: ZuoraConnect.configuration.s3_bucket_name, key: "#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{key}")
|
|
653
|
+
end
|
|
654
|
+
### END S3 Helping Methods #####
|
|
538
655
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
656
|
+
### START Aggregate Grouping Helping Methods ####
|
|
657
|
+
def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)
|
|
658
|
+
self.update_functions
|
|
659
|
+
#Broke function into two parts to ensure transaction size was small enough
|
|
660
|
+
ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Table\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
|
|
661
|
+
ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Index\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)]) if index_table
|
|
662
|
+
end
|
|
544
663
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
if ZuoraConnect.configuration.mode == "Development"
|
|
548
|
-
@s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region,access_key_id: ZuoraConnect.configuration.dev_mode_access_key_id,secret_access_key: ZuoraConnect.configuration.dev_mode_secret_access_key)
|
|
549
|
-
else
|
|
550
|
-
@s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region)
|
|
664
|
+
def self.update_functions
|
|
665
|
+
ActiveRecord::Base.connection.execute(File.read("#{Gem.loaded_specs["zuora_connect"].gem_dir}/app/views/sql/refresh_aggregate_table.txt"))
|
|
551
666
|
end
|
|
667
|
+
### END Aggregate Grouping Helping Methods #####
|
|
668
|
+
|
|
669
|
+
# Overide this method to avoid the new session call for api requests that use the before filter authenticate_app_api_request.
|
|
670
|
+
# This can be usefull for apps that dont need connect metadata call, or credentials, to operate for api requests
|
|
671
|
+
def new_session_for_api_requests(params: {})
|
|
672
|
+
return true
|
|
552
673
|
end
|
|
553
674
|
|
|
554
|
-
|
|
555
|
-
|
|
675
|
+
# Overide this method to avoid the new session call for ui requests that use the before filter authenticate_connect_app_request.
|
|
676
|
+
# This can be usefull for apps that dont need connect metadata call, or credentials, to operate for ui requests
|
|
677
|
+
def new_session_for_ui_requests(params: {})
|
|
678
|
+
return true
|
|
556
679
|
end
|
|
557
680
|
|
|
558
|
-
def
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token",:body => params)
|
|
568
|
-
response_time = Time.now - start
|
|
569
|
-
Rails.logger.info("[#{self.id}] REFRESHING - OAuth in #{response_time.round(2).to_s}")
|
|
681
|
+
def reload_attributes(selected_attributes)
|
|
682
|
+
raise "Attibutes must be array" if selected_attributes.class != Array
|
|
683
|
+
value_attributes = self.class.unscoped.where(:id=>id).select(selected_attributes).first.attributes
|
|
684
|
+
value_attributes.each do |key, value|
|
|
685
|
+
next if key == "id" && value.blank?
|
|
686
|
+
self.send(:write_attribute, key, value)
|
|
687
|
+
end
|
|
688
|
+
return self
|
|
689
|
+
end
|
|
570
690
|
|
|
571
|
-
|
|
572
|
-
|
|
691
|
+
def instance_failure(failure)
|
|
692
|
+
raise failure
|
|
693
|
+
end
|
|
573
694
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
self.oauth_expires_at = Time.at(response_body["created_at"].to_i) + response_body["expires_in"].seconds
|
|
577
|
-
self.save(:validate => false)
|
|
578
|
-
else
|
|
579
|
-
Rails.logger.fatal("REFRESHING - OAuth Failed - Code #{response.code}")
|
|
580
|
-
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Refreshing Access Token", response.body, response.code)
|
|
581
|
-
end
|
|
695
|
+
def send_email
|
|
696
|
+
end
|
|
582
697
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
raise
|
|
588
|
-
end
|
|
589
|
-
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
|
|
590
|
-
if (count += 1) < 3
|
|
591
|
-
Rails.logger.info("REFRESHING - OAuth Failed - Retrying(#{count})")
|
|
592
|
-
self.reload
|
|
593
|
-
sleep(5)
|
|
594
|
-
retry
|
|
595
|
-
else
|
|
596
|
-
Rails.logger.fatal("REFRESHING - OAuth Failed")
|
|
597
|
-
raise
|
|
698
|
+
def login_lookup(type: "Zuora")
|
|
699
|
+
results = []
|
|
700
|
+
self.logins.each do |name, login|
|
|
701
|
+
results << login if login.tenant_type == type
|
|
598
702
|
end
|
|
703
|
+
return results
|
|
599
704
|
end
|
|
600
705
|
|
|
601
|
-
def
|
|
602
|
-
(
|
|
706
|
+
def self.decrypt_response(resp)
|
|
707
|
+
OpenSSL::PKey::RSA.new(ZuoraConnect.configuration.private_key).private_decrypt(resp)
|
|
603
708
|
end
|
|
604
709
|
|
|
605
710
|
def attr_builder(field,val)
|
|
@@ -616,15 +721,7 @@ module ZuoraConnect
|
|
|
616
721
|
super
|
|
617
722
|
end
|
|
618
723
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
end
|
|
622
|
-
|
|
623
|
-
def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)
|
|
624
|
-
self.update_functions
|
|
625
|
-
#Broke function into two parts to ensure transaction size was small enough
|
|
626
|
-
ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Table\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
|
|
627
|
-
ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Index\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)]) if index_table
|
|
628
|
-
end
|
|
724
|
+
method_hook :refresh, :updateOption, :update_logins, :before => :check_oauth_state
|
|
725
|
+
method_hook :new_session, :refresh, :build_task, :after => :apartment_switch
|
|
629
726
|
end
|
|
630
727
|
end
|