zuora_connect 1.5.30 → 1.5.32
Sign up to get free protection for your applications and to get access to all the features.
- 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
|