zuora_connect 0 → 2.1.1

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/hallway_wrapper/after.js +22 -3
  3. data/app/controllers/zuora_connect/static_controller.rb +27 -22
  4. data/app/helpers/zuora_connect/application_helper.rb +10 -0
  5. data/app/models/zuora_connect/app_instance_base.rb +419 -136
  6. data/app/models/zuora_connect/login.rb +1 -0
  7. data/app/models/zuora_connect/telegraf.rb +11 -7
  8. data/app/models/zuora_connect/zuora_user.rb +7 -0
  9. data/app/views/sql/refresh_aggregate_table.txt +3 -2
  10. data/app/views/zuora_connect/static/error_handled.html.erb +76 -0
  11. data/app/views/zuora_connect/static/error_handled.js.erb +1 -0
  12. data/app/views/zuora_connect/static/error_unhandled.erb +85 -0
  13. data/app/views/zuora_connect/static/error_unhandled.js.erb +1 -0
  14. data/app/views/zuora_connect/static/launch.html.erb +71 -76
  15. data/config/initializers/patches.rb +9 -0
  16. data/config/initializers/redis.rb +24 -5
  17. data/config/initializers/resque.rb +13 -1
  18. data/config/routes.rb +0 -2
  19. data/db/migrate/20100718151733_create_connect_app_instances.rb +1 -1
  20. data/db/migrate/20101024162319_add_tokens_to_app_instance.rb +1 -1
  21. data/db/migrate/20101024220705_add_token_to_app_instance.rb +1 -1
  22. data/db/migrate/20110131211919_add_sessions_table.rb +1 -1
  23. data/db/migrate/20110411200303_add_expiration_to_app_instance.rb +1 -1
  24. data/db/migrate/20110413191512_add_new_api_token.rb +1 -1
  25. data/db/migrate/20110503003602_add_catalog_data_to_app_instance.rb +1 -1
  26. data/db/migrate/20110503003603_add_catalog_mappings_to_app_instance.rb +1 -1
  27. data/db/migrate/20110503003604_catalog_default.rb +1 -1
  28. data/db/migrate/20180301052853_add_catalog_attempted_at.rb +1 -1
  29. data/db/migrate/20181206162339_add_fields_to_instance.rb +1 -1
  30. data/db/migrate/20190520232221_add_zuora_user_table_and_alter_app_instance_id_table.rb +18 -0
  31. data/db/migrate/20190520232222_add_unique_index.rb +6 -0
  32. data/db/migrate/20190520232223_add_provisioning_fields.rb +6 -0
  33. data/db/migrate/20190520232224_add_environment_fields.rb +13 -0
  34. data/lib/logging/connect_formatter.rb +44 -0
  35. data/lib/metrics/net.rb +2 -2
  36. data/lib/middleware/bad_multipart_form_data_sanitizer.rb +21 -0
  37. data/lib/middleware/json_parse_errors.rb +22 -0
  38. data/lib/middleware/metrics_middleware.rb +5 -2
  39. data/lib/middleware/request_id_middleware.rb +17 -0
  40. data/lib/resque/dynamic_queues.rb +34 -12
  41. data/lib/resque/plugins/app_instance_job.rb +77 -0
  42. data/lib/resque/plugins/custom_logger.rb +10 -24
  43. data/lib/zuora_connect.rb +116 -5
  44. data/lib/zuora_connect/configuration.rb +4 -3
  45. data/lib/zuora_connect/controllers/helpers.rb +483 -176
  46. data/lib/zuora_connect/engine.rb +8 -5
  47. data/lib/zuora_connect/exceptions.rb +4 -2
  48. data/lib/zuora_connect/railtie.rb +50 -19
  49. data/lib/zuora_connect/version.rb +2 -2
  50. metadata +98 -60
  51. data/app/views/zuora_connect/static/invalid_app_instance_error.html.erb +0 -65
  52. data/app/views/zuora_connect/static/invalid_launch_request.html +0 -65
  53. data/app/views/zuora_connect/static/session_error.html.erb +0 -63
  54. data/config/initializers/elastic_apm.rb +0 -25
  55. data/lib/zuora_connect/views/helpers.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b69457446868cd3b97d81d5753a967f002d58dfc750c2a4cae9b880a590c741
4
- data.tar.gz: e27a81b4e9f30562ff67632d57c3b9e798e76677b5b4ee7c2698473a349bb111
3
+ metadata.gz: 0342c5ac7396b4e8ba75aff8dbab5d579fa6d0ad04a421b7d1d809ab3abbd90a
4
+ data.tar.gz: 70f7277a93fec8e481f63e56f85e466b5e0b4d1307e2413e4f8da9497e980be2
5
5
  SHA512:
6
- metadata.gz: cb57173d9c5ef615f65d42a1fb6034f47c9878840063d3d243b774ed036e3b30f82a86943b1cfccf9a6534d69849a47468a51b1978e6720a012320025120e9ae
7
- data.tar.gz: 53cad727a3c349daf72ee6f3b77c6dfebfe658f41470feb2bc259c4cd5dda994f28f00291d8fa6b14e2feda38df2acc033fef79d8875c5a8c0e6acbef083581b
6
+ metadata.gz: eb6497a343dde2b4a3edfe470c8e5a44eb37ff99b75ed434138c280e8ac25bf36806d58fbb477772182596917f1c37c0e237bd55e88f646e149743477fba2f15
7
+ data.tar.gz: f0b1f8b6f647f204ce8c4bfed7a8e77b49f3aada0faf6a0c31fc47675f5bfca67feb464692006bcb490977a577d1d5a3feab53886815f4c6472023e9ade6823b
@@ -4,12 +4,31 @@ window.define = previousDefine;
4
4
  if (isHallway()) {
5
5
  $( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
6
6
  if ( jqxhr.status === 401) {
7
- window.location.href = '/apps/newlogin.do?retURL=' + window.location.pathname;
7
+ fetch("https://" + window.location.host + "/apps/v1/navigation").then(response => {
8
+ if (response.status === 401) {
9
+ deleteAllCookies();
10
+ window.location.href = '/apps/newlogin.do?retURL=' + window.location.pathname;
11
+ }
12
+ });
8
13
  }
9
14
  });
10
15
  }
11
16
 
12
17
  function isHallway() {
13
- var regex = new RegExp("^/services/");
14
- return window.location.pathname.match(regex);
18
+ var regex = new RegExp("(?<=\\/)services\\/.*");
19
+ if (regex.test(window.location.pathname)) {
20
+ return window.location.pathname.match(regex)[0]
21
+ }
22
+ return null;
23
+ }
24
+
25
+ function deleteAllCookies() {
26
+ var cookies = document.cookie.split(";");
27
+
28
+ for (var i = 0; i < cookies.length; i++) {
29
+ var cookie = cookies[i];
30
+ var eqPos = cookie.indexOf("=");
31
+ var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
32
+ document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/";
33
+ }
15
34
  }
@@ -1,31 +1,30 @@
1
1
  module ZuoraConnect
2
2
  class StaticController < ApplicationController
3
- before_action :authenticate_connect_app_request, :except => [:metrics, :health, :session_error, :invalid_app_instance_error, :initialize_app]
4
- before_action :clear_connect_app_session, :only => [:metrics, :health, :session_error, :invalid_app_instance_error, :initialize_app]
5
- after_action :persist_connect_app_session, :except => [:metrics, :health, :session_error, :invalid_app_instance_error, :initialize_app]
3
+ before_action :authenticate_connect_app_request, :except => [:metrics, :health, :initialize_app]
4
+ before_action :clear_connect_app_session, :only => [:metrics, :health, :initialize_app]
5
+ after_action :persist_connect_app_session, :except => [:metrics, :health, :initialize_app]
6
6
 
7
7
  skip_before_action :verify_authenticity_token, :only => [:initialize_app]
8
8
 
9
- def session_error
10
- respond_to do |format|
11
- format.html
12
- format.json { render json: { message: "Session Error", status: 500 }, status: 500 }
13
- end
14
- end
15
-
16
- def invalid_app_instance_error
17
- respond_to do |format|
18
- format.html
19
- format.json {render json: { message: "Invalid App Instance", status: 500 }, status: 500 }
20
- end
21
- end
22
-
23
9
  def metrics
24
10
  type = params[:type].present? ? params[:type] : "versions"
25
11
  render json: ZuoraConnect::AppInstance.get_metrics(type).to_json, status: 200
26
12
  end
27
13
 
28
14
  def health
15
+ if params[:error].present?
16
+ begin
17
+ raise ZuoraConnect::Exceptions::Error.new('This is an error')
18
+ rescue => ex
19
+ case params[:error]
20
+ when 'Log'
21
+ Rails.logger.error("Error in Health", ex)
22
+ when 'Exception'
23
+ raise
24
+ end
25
+ end
26
+ end
27
+
29
28
  render json: {
30
29
  message: "Alive",
31
30
  status: 200
@@ -35,15 +34,21 @@ module ZuoraConnect
35
34
  def initialize_app
36
35
  begin
37
36
  authenticate_connect_app_request
37
+ @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
38
38
  render json: {
39
39
  message: "Success",
40
40
  status: 200
41
41
  }, status: 200
42
- rescue
43
- render json: {
44
- message: "Failure initializing app instance",
45
- status: 500
46
- }, status: 500
42
+ rescue => ex
43
+ Rails.logger.error("Failed to Initialize application", ex)
44
+ if performed?
45
+ Rails.logger.error("Failed to Initialize application #{performed?}", ex)
46
+ else
47
+ render json: {
48
+ message: "Failure initializing app instance",
49
+ status: 500
50
+ }, status: 500
51
+ end
47
52
  end
48
53
  end
49
54
 
@@ -1,5 +1,15 @@
1
1
  module ZuoraConnect
2
2
  module ApplicationHelper
3
+ def is_app_admin?
4
+ return @appinstance.blank? ? false : session["#{@appinstance.id}::admin"]
5
+ end
3
6
 
7
+ def zuora_user
8
+ return @zuora_user
9
+ end
10
+
11
+ def connect_meta_tags
12
+ "<meta name=\"z-hallway-prefix\" content=\"#{ Thread.current[:isHallway] }\">".html_safe
13
+ end
4
14
  end
5
15
  end
@@ -1,9 +1,13 @@
1
1
  module ZuoraConnect
2
2
  require "uri"
3
+ require 'aws-sigv4'
4
+ require 'aws-sdk-s3'
5
+ require 'aws-sdk-ses'
6
+ require 'aws-sdk-kms'
3
7
  class AppInstanceBase < ActiveRecord::Base
4
8
  default_scope {select(ZuoraConnect::AppInstance.column_names.delete_if {|x| ["catalog_mapping", "catalog"].include?(x) }) }
5
9
  after_initialize :init
6
- after_create :initialize_redis_placeholder
10
+ after_create :initialize_redis_placeholders
7
11
  before_destroy :prune_data
8
12
 
9
13
  self.table_name = "zuora_connect_app_instances"
@@ -15,7 +19,19 @@ module ZuoraConnect
15
19
  API_LIMIT_TIMEOUT = 2.minutes #Used to set the default for expiring timeout when api rate limiting is in effect
16
20
  BLANK_OBJECT_ID_LOOKUP = 'BlankValueSupplied'
17
21
  HOLDING_PATTERN_SLEEP = 5.seconds
18
- CONNECT_COMMUNICATION_SLEEP= 5.seconds
22
+ CONNECT_APPLICATION_ID = 0
23
+ CONNECT_COMMUNICATION_SLEEP = Rails.env.test? ? 0.seconds : 5.seconds
24
+ IGNORED_LOCALS = ['fr', 'ja', 'es', 'zh', 'de']
25
+ INTERNAL_HOSTS = []
26
+ LOGIN_TENANT_DESTINATION = 'target_login'
27
+ AWS_AUTH_ERRORS = [
28
+ Aws::Sigv4::Errors::MissingCredentialsError,
29
+ Aws::Errors::MissingCredentialsError,
30
+ Aws::S3::Errors::AccessDenied,
31
+ Aws::SES::Errors::AccessDenied,
32
+ Aws::KMS::Errors::AccessDeniedException
33
+ ].freeze
34
+ AWS_AUTH_ERRORS_MSG = "AWS Auth Errors".freeze
19
35
 
20
36
  def init
21
37
  self.connect_user = 'Nobody'
@@ -27,46 +43,73 @@ module ZuoraConnect
27
43
  self.attr_builder("timezone", ZuoraConnect.configuration.default_time_zone)
28
44
  self.attr_builder("locale", ZuoraConnect.configuration.default_locale)
29
45
  PaperTrail.whodunnit = "Backend" if defined?(PaperTrail)
30
- if defined?(ElasticAPM)
46
+ if defined?(ElasticAPM) && ElasticAPM.running?
31
47
  ElasticAPM.set_user("Backend")
32
- ElasticAPM.set_tag(:app_instance, self.id)
48
+ if ElasticAPM.respond_to?(:set_label)
49
+ ElasticAPM.set_label(:app_instance, self.id)
50
+ else
51
+ ElasticAPM.set_label(:app_instance, self.id)
52
+ end
33
53
  end
34
54
 
35
55
  if INSTANCE_REFRESH_WINDOW > INSTANCE_REDIS_CACHE_PERIOD
36
56
  raise "The instance refresh window cannot be greater than the instance cache period"
37
57
  end
38
58
  self.apartment_switch(nil, false)
59
+
60
+ if ZuoraConnect.logger.is_a?(Ougai::Logger)
61
+ ZuoraConnect.logger.with_fields.merge!(default_ougai_items)
62
+ end
63
+ if Rails.logger.is_a?(Ougai::Logger)
64
+ Rails.logger.with_fields.merge!(default_ougai_items)
65
+ end
39
66
  end
40
67
 
41
- def initialize_redis_placeholder
68
+ def initialize_redis_placeholders
42
69
  if defined?(Redis.current)
43
- Redis.current.zrem("AppInstance:Deleted", id)
44
- Redis.current.zadd("APILimits", 9999999999, "placeholder")
45
- Redis.current.zadd("InstanceRefreshing", 9999999999, "placeholder")
70
+ unless Redis.current.zscore("AppInstance:Deleted", "placeholder").present? # O(1)
71
+ Redis.current.zadd("AppInstance:Deleted", 9_999_999_999, "placeholder") # O(log(N))
72
+ end
73
+ if self.id.present?
74
+ if Redis.current.zscore("AppInstance:Deleted", self.id).present? # O(1)
75
+ Redis.current.zrem("AppInstance:Deleted", self.id) # O(log(N))
76
+ end
77
+ end
78
+ unless Redis.current.zscore("APILimits", "placeholder").present? # O(1)
79
+ Redis.current.zadd("APILimits", 9_999_999_999, "placeholder") # O(log(N))
80
+ end
81
+ unless Redis.current.zscore("InstanceRefreshing", "placeholder").present? # O(1)
82
+ Redis.current.zadd("InstanceRefreshing", 9_999_999_999, "placeholder") # O(log(N))
83
+ end
46
84
  end
47
85
  if defined?(Resque.redis)
48
- Resque.redis.zadd("PauseQueue", 9999999999, "placeholder")
86
+ unless Resque.redis.zscore("PauseQueue", "placeholder").present? # O(1)
87
+ Resque.redis.zadd("PauseQueue", 9_999_999_999, "placeholder") # O(log(N))
88
+ end
49
89
  end
90
+ true
50
91
  end
51
92
 
52
- def prune_data(id: self.id)
93
+ def prune_data
53
94
  if defined?(Redis.current)
54
- Redis.current.zadd("AppInstance:Deleted", Time.now.to_i, id)
55
- Redis.current.del("AppInstance:#{id}")
56
- Redis.current.zrem("APILimits", id)
57
- Redis.current.zrem("InstanceRefreshing", id)
95
+ Redis.current.zadd("AppInstance:Deleted", Time.now.to_i, self.id)
96
+ Redis.current.del("AppInstance:#{self.id}")
97
+ Redis.current.zrem("APILimits", self.id)
98
+ Redis.current.zrem("InstanceRefreshing", self.id)
58
99
  end
59
100
  if defined?(Resque.redis)
60
- Resque.redis.zrem("PauseQueue", id)
101
+ Resque.redis.zrange("PauseQueue", 0, -1).each do |key|
102
+ Resque.redis.zrem("PauseQueue", key) if key.split("__").first.to_i == self.id
103
+ end
61
104
  end
62
105
  return true
63
- end
106
+ end
64
107
 
65
108
  def apartment_switch(method = nil, migrate = false)
66
109
  switch_count ||= 0
67
110
  if self.persisted?
68
111
  begin
69
- Apartment::Tenant.switch!(self.id)
112
+ Apartment::Tenant.switch!(self.id)
70
113
  rescue Apartment::TenantNotFound => ex
71
114
  sleep(2)
72
115
  begin
@@ -74,9 +117,9 @@ module ZuoraConnect
74
117
  rescue Apartment::TenantExists => ex
75
118
  end
76
119
  if (switch_count += 1) < 2
77
- retry
120
+ retry
78
121
  else
79
- raise
122
+ raise
80
123
  end
81
124
  end
82
125
  if migrate && ActiveRecord::Migrator.needs_migration?
@@ -86,19 +129,24 @@ module ZuoraConnect
86
129
  Thread.current[:appinstance] = self
87
130
  end
88
131
 
89
- def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token, holding_pattern: false, log_level: Logger::DEBUG)
132
+ def default_ougai_items
133
+ return {app_instance_id: self.id, tenant_ids: self.zuora_tenant_ids, organization: self.organizations, environment: self.environment}
134
+ end
135
+
136
+ def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token, holding_pattern: false, **args)
90
137
  self.api_version = "v2"
91
138
  self.username = username
92
139
  self.password = password
93
140
  self.last_refresh = session["#{self.id}::last_refresh"]
94
141
  self.connect_user = session["#{self.id}::user::email"] if session["#{self.id}::user::email"].present?
95
142
  PaperTrail.whodunnit = self.connect_user if defined?(PaperTrail)
96
- ElasticAPM.set_user(self.connect_user) if defined?(ElasticAPM)
143
+ ElasticAPM.set_user(self.connect_user) if defined?(ElasticAPM) && ElasticAPM.running?
97
144
  recoverable_session = false
98
145
 
99
146
  ## DEV MODE TASK DATA MOCKUP
100
147
  if ZuoraConnect.configuration.mode != "Production"
101
148
  mock_task_data = {
149
+ "id" => ZuoraConnect.configuration.dev_mode_appinstance,
102
150
  "mode" => ZuoraConnect.configuration.dev_mode_mode
103
151
  }
104
152
 
@@ -115,31 +163,32 @@ module ZuoraConnect
115
163
  end
116
164
 
117
165
  self.build_task(task_data: mock_task_data, session: session)
166
+ self.last_refresh = Time.now.to_i
118
167
  else
119
168
  time_expire = (session["#{self.id}::last_refresh"] || Time.now).to_i - INSTANCE_REFRESH_WINDOW.ago.to_i
120
169
 
121
170
  if session.empty?
122
171
  self.new_session_message = "REFRESHING - Session Empty"
123
- Rails.logger.add(log_level, self.new_session_message)
172
+ ZuoraConnect.logger.debug(self.new_session_message)
124
173
  raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
125
174
  self.refresh(session: session)
126
175
 
127
176
  elsif (self.id != session["appInstance"].to_i)
128
177
  self.new_session_message = "REFRESHING - AppInstance ID(#{self.id}) does not match session id(#{session["appInstance"].to_i})"
129
- Rails.logger.add(log_level, self.new_session_message)
178
+ ZuoraConnect.logger.debug(self.new_session_message)
130
179
  raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
131
180
  self.refresh(session: session)
132
181
 
133
182
  elsif session["#{self.id}::task_data"].blank?
134
183
  self.new_session_message = "REFRESHING - Task Data Blank"
135
- Rails.logger.add(log_level, self.new_session_message)
184
+ ZuoraConnect.logger.debug(self.new_session_message)
136
185
  raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
137
186
  self.refresh(session: session)
138
187
 
139
188
  elsif session["#{self.id}::last_refresh"].blank?
140
189
  self.new_session_message = "REFRESHING - No Time on Cookie"
141
190
  recoverable_session = true
142
- Rails.logger.add(log_level, self.new_session_message)
191
+ ZuoraConnect.logger.debug(self.new_session_message)
143
192
  raise ZuoraConnect::Exceptions::HoldingPattern if holding_pattern && !self.mark_for_refresh
144
193
  self.refresh(session: session)
145
194
 
@@ -147,7 +196,7 @@ module ZuoraConnect
147
196
  elsif (session["#{self.id}::last_refresh"].to_i < INSTANCE_REFRESH_WINDOW.ago.to_i) && self.mark_for_refresh
148
197
  self.new_session_message = "REFRESHING - Session Old by #{time_expire.abs} second"
149
198
  recoverable_session = true
150
- Rails.logger.add(log_level, self.new_session_message)
199
+ ZuoraConnect.logger.debug(self.new_session_message)
151
200
  self.refresh(session: session)
152
201
 
153
202
  else
@@ -156,82 +205,194 @@ module ZuoraConnect
156
205
  else
157
206
  self.new_session_message = "REBUILDING - Expires in #{time_expire} seconds"
158
207
  end
159
- Rails.logger.add(log_level, self.new_session_message)
208
+ ZuoraConnect.logger.debug(self.new_session_message, self.default_ougai_items)
160
209
  self.build_task(task_data: session["#{self.id}::task_data"], session: session)
161
210
  end
162
- end
211
+ end
163
212
  return self
164
213
  rescue ZuoraConnect::Exceptions::HoldingPattern => ex
165
214
  while self.marked_for_refresh?
166
- Rails.logger.info("Holding - Expires in #{self.reset_mark_expires_at}. '#{self.new_session_message}'")
215
+ ZuoraConnect.logger.info("Holding - Expires in #{self.reset_mark_expires_at}. '#{self.new_session_message}'", self.default_ougai_items)
167
216
  sleep(HOLDING_PATTERN_SLEEP)
168
217
  end
169
218
  self.reload_attributes([:refresh_token, :oauth_expires_at, :access_token])
170
219
  session = self.data_lookup(session: session)
171
220
  retry
172
- rescue => ex
221
+ rescue ZuoraConnect::Exceptions::MissMatch => ex
222
+ self.delete_app_instance
223
+ session = {}
224
+ ZuoraConnect.logger.error(ex, self.default_ougai_items.merge({app_instance_id_new: self.task_data['id']}))
225
+ retry
226
+ rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex
227
+ raise
228
+ rescue => ex
173
229
  if recoverable_session
174
- Rails.logger.error("REBUILDING - Using backup expired cache")
230
+ ZuoraConnect.logger.warn("REBUILDING - Using backup expired cache", ex, self.default_ougai_items)
175
231
  self.build_task(task_data: session["#{self.id}::task_data"], session: session)
176
232
  return self
177
233
  else
234
+ ZuoraConnect.logger.error("Failed new session", ex, self.default_ougai_items)
178
235
  raise
179
236
  end
180
237
  ensure
181
238
  begin
182
239
  I18n.locale = self.locale
183
240
  rescue I18n::InvalidLocale => ex
184
- Rails.logger.debug("Invalid Locale: #{ex.message}")
241
+ ZuoraConnect.logger.error(ex) if !IGNORED_LOCALS.include?(ex.locale.to_s.downcase)
185
242
  end
186
243
  Time.zone = self.timezone
187
- tenants = self.task_data.dig('tenant_ids') || []
188
- organizations = self.task_data.dig('organizations') || []
189
- if defined?(ElasticAPM)
190
- ElasticAPM.set_tag(:tenant_id, tenants.first)
191
- ElasticAPM.set_tag(:organization, organizations.first)
192
- end
193
- self.logitem(item: {tenant_ids: tenants, organization: organizations})
194
- self.update_column(:name, self.task_data.dig('name')) if ZuoraConnect::AppInstance.column_names.include?('name') && self.task_data.dig('name') != self.name
244
+ if self.task_data.present?
245
+ tenants = self.task_data.fetch('tenant_ids', [])
246
+ organizations = self.task_data.fetch('organizations', [])
247
+ if defined?(ElasticAPM) && ElasticAPM.running?
248
+ if ElasticAPM.respond_to?(:set_label)
249
+ ElasticAPM.set_label(:tenant_id, tenants.first)
250
+ ElasticAPM.set_label(:organization, organizations.first)
251
+ else
252
+ ElasticAPM.set_label(:tenant_id, tenants.first)
253
+ ElasticAPM.set_label(:organization, organizations.first)
254
+ end
255
+ end
256
+
257
+ params = {
258
+ name: self.task_data.dig('name'),
259
+ zuora_entity_ids: (self.task_data.dig(LOGIN_TENANT_DESTINATION,'entities') || []).map{|e| e['id']}.uniq,
260
+ zuora_tenant_ids: tenants.map(&:to_s).uniq,
261
+ organizations: organizations
262
+ }
263
+ if self.methods.include?(LOGIN_TENANT_DESTINATION.to_sym)
264
+ client = self.send(LOGIN_TENANT_DESTINATION).client
265
+ if defined?(client.rest_domain)
266
+ ZuoraConnect::RequestIdMiddleware.zuora_rest_domain = client.rest_domain
267
+ params.merge!({zuora_domain: client.rest_domain, environment: client.environment })
268
+ end
269
+ end
270
+ params = params.reject{|k,v| !self.attributes.keys.member?(k.to_s) || self[k] == v}
271
+ self.update_columns(params) if params.present?
272
+ end
195
273
  end
196
274
 
197
- def refresh(session: {}, session_fallback: false)
275
+ def refresh(session: {})
198
276
  refresh_count ||= 0
199
- start = Time.now
200
- response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
201
- response_time = Time.now - start
277
+ skip_connect ||= false
278
+ begin
279
+ #Check how app was deployed
280
+ if self.id < 25000000 && !skip_connect
281
+ self.check_oauth_state
282
+ response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
283
+
284
+ if response.code == 200
285
+ begin
286
+ parsed_json = JSON.parse(response.body)
287
+ rescue JSON::ParserError => ex
288
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("JSON parse error", response.body, response.code)
289
+ end
202
290
 
203
- Rails.logger.debug("[#{self.id}] REFRESH TASK - Connect Task Info Request Time #{response_time.round(2).to_s}")
204
- if response.code == 200
205
- self.build_task(task_data: JSON.parse(response.body), session: session)
291
+ self.build_task(task_data: parsed_json, session: session)
292
+ if self.kms_key.present? && self.kms_key.match(/^arn:aws:.*/)
293
+ begin
294
+ self.zuora_logins = self.strip_cache_data(object: parsed_json.dup, keys: ['applications', 'tokens', 'user_settings'])
295
+ self.save(:validate => false)
296
+ rescue Aws::KMS::Errors::ValidationException, *AWS_AUTH_ERRORS => ex
297
+ Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
298
+ rescue => ex
299
+ Rails.logger.error(AWS_AUTH_ERRORS_MSG, ex)
300
+ end
301
+ end
302
+ else
303
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
304
+ end
305
+ else
306
+ self.build_task(task_data: self.zuora_logins, session: session)
307
+ end
206
308
  self.last_refresh = Time.now.to_i
207
309
  self.cache_app_instance
208
310
  self.reset_mark_for_refresh
209
- else
210
- Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed Code #{response.code}")
211
- raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
311
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
312
+ refresh_count += 1
313
+ if refresh_count < 3
314
+ sleep(10)
315
+ ZuoraConnect.logger.debug("REFRESH TASK - Connection Failure Retrying(#{refresh_count})", ex, self.default_ougai_items)
316
+ retry
317
+ else
318
+ ZuoraConnect.logger.fatal("REFRESH TASK - Connection Failed", ex)
319
+ raise
320
+ end
321
+ rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
322
+ refresh_count += 1
323
+ if refresh_count < 3
324
+ ZuoraConnect.logger.debug("REFRESH TASK - Communication Failure Retrying(#{refresh_count})", ex, self.default_ougai_items)
325
+ self.refresh_oauth if ex.code == 401
326
+ retry
327
+ else
328
+ ZuoraConnect.logger.fatal("REFRESH TASK - Communication Failed #{ex.code}", ex, self.default_ougai_items)
329
+ raise
330
+ end
212
331
  end
213
- rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS).concat(ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
214
- if (refresh_count += 1) < 3
215
- Rails.logger.info("[#{self.id}] REFRESH TASK - #{ex.class} Retrying(#{refresh_count})")
332
+ rescue => ex
333
+ if self['zuora_logins'].present?
334
+ ZuoraConnect.logger.warn("REFRESH TASK - Fallback to local encrypted store", ex, self.default_ougai_items)
335
+ skip_connect = true
216
336
  retry
217
- else
218
- Rails.logger.fatal("[#{self.id}] REFRESH TASK - #{ex.class} Failed #{refresh_count}x")
219
- raise
220
337
  end
221
- rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
222
- if (refresh_count += 1) < 3
223
- Rails.logger.info("[#{self.id}] REFRESH TASK - Failed Retrying(#{refresh_count})")
224
- if ex.code == 401
225
- self.refresh_oauth
338
+ raise
339
+ end
340
+
341
+ #### START KMS ENCRYPTION Methods ####
342
+ def zuora_logins=(val)
343
+ write_attribute(:zuora_logins, kms_encrypt(val.to_json))
344
+ end
345
+
346
+ def zuora_logins
347
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Zuora Logins is blank, cannot decrypt.") if super.blank?
348
+ return JSON.parse(kms_decrypt(super))
349
+ end
350
+
351
+ def kms_decrypt(value)
352
+ kms_tries ||= 0
353
+ kms_client = Aws::KMS::Client.new({region: Rails.application.secrets.aws['AWS_REGION'], credentials: self.aws_auth_client}.delete_if { |k, v| v.blank? })
354
+ resp = kms_client.decrypt({ciphertext_blob: [value].pack("H*") })
355
+ return resp.plaintext
356
+ rescue *AWS_AUTH_ERRORS => ex
357
+ if (kms_tries += 1) < 3
358
+ Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
359
+ retry
360
+ else
361
+ Rails.logger.error(AWS_AUTH_ERRORS_MSG, ex)
362
+ raise
226
363
  end
227
- retry
228
- else
229
- Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed #{refresh_count}x")
230
- raise
231
364
  end
232
- end
233
365
 
234
- #### START Metrics Mathods ####
366
+ def kms_encrypt(value)
367
+ kms_tries ||= 0
368
+ kms_client = Aws::KMS::Client.new({region: Rails.application.secrets.aws['AWS_REGION'], credentials: self.aws_auth_client}.delete_if {|k,v| v.blank? })
369
+
370
+ resp = kms_client.encrypt({key_id: kms_key, plaintext: value})
371
+ return resp.ciphertext_blob.unpack('H*').first
372
+ rescue *AWS_AUTH_ERRORS => ex
373
+ if (kms_tries += 1) < 3
374
+ Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
375
+ retry
376
+ else
377
+ Rails.logger.error(AWS_AUTH_ERRORS_MSG, ex)
378
+ raise
379
+ end
380
+ end
381
+
382
+ def kms_key
383
+ return ENV['AWS_KMS_ARN'] || Rails.application.secrets.dig(:aws,'AWS_KMS_ARN')
384
+ end
385
+
386
+ def aws_auth_client
387
+ if Rails.env.to_s == 'development'
388
+ return Aws::Credentials.new(Rails.application.secrets.aws['AWS_ACCESS_KEY_ID'], Rails.application.secrets.aws['AWS_SECRET_ACCESS_KEY'])
389
+ else
390
+ return nil
391
+ end
392
+ end
393
+ #### END KMS ENCRYPTION Methods ####
394
+
395
+ #### START Metrics Methods ####
235
396
  def logitem(item: {}, reset: false)
236
397
  self.logitems = {} if self.logitems.class != Hash
237
398
  if item.class == Hash
@@ -243,10 +404,45 @@ module ZuoraConnect
243
404
  def self.write_to_telegraf(*args)
244
405
  if ZuoraConnect.configuration.enable_metrics
245
406
  @@telegraf_host = ZuoraConnect::Telegraf.new() if @@telegraf_host == nil
407
+ unicorn_stats = self.unicorn_listener_stats() if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
408
+ @@telegraf_host.write(direction: 'Raindrops', tags: {}, values: unicorn_stats) unless unicorn_stats.blank?
246
409
  return @@telegraf_host.write(*args)
247
410
  end
248
411
  end
249
412
 
413
+ def self.unicorn_listener_stats ()
414
+ stats_hash = {}
415
+ stats_hash["total_active"] = 0
416
+ stats_hash["total_queued"] = 0
417
+
418
+ begin
419
+ tmp = Unicorn.listener_names
420
+ unix = tmp.grep(%r{\A/})
421
+ tcp = tmp.grep(/\A.+:\d+\z/)
422
+ tcp = nil if tcp.empty?
423
+ unix = nil if unix.empty?
424
+
425
+
426
+ Raindrops::Linux.tcp_listener_stats(tcp).each do |addr,stats|
427
+ stats_hash["active_#{addr}"] = stats.active
428
+ stats_hash["queued_#{addr}"] = stats.queued
429
+ stats_hash["total_active"] = stats.active + stats_hash["total_active"]
430
+ stats_hash["total_queued"] = stats.queued + stats_hash["total_queued"]
431
+ end if tcp
432
+
433
+ Raindrops::Linux.unix_listener_stats(unix).each do |addr,stats|
434
+ stats_hash["active_#{addr}"] = stats.active
435
+ stats_hash["queued_#{addr}"] = stats.queued
436
+ stats_hash["total_active"] = stats.active + stats_hash["total_active"]
437
+ stats_hash["total_queued"] = stats.queued + stats_hash["total_queued"]
438
+ end if unix
439
+ rescue IOError => ex
440
+ rescue => ex
441
+ ZuoraConnect.logger.error(ex)
442
+ end
443
+ return stats_hash
444
+ end
445
+
250
446
  def self.get_metrics(type)
251
447
  @data = {}
252
448
 
@@ -280,13 +476,18 @@ module ZuoraConnect
280
476
  end
281
477
  return @data
282
478
  end
283
- #### END Task Mathods ####
479
+ #### END Task Methods ####
284
480
 
285
- #### START Task Mathods ####
481
+ #### START Task Methods ####
286
482
  def build_task(task_data: {}, session: {})
287
483
  session = {} if session.blank?
288
484
  self.task_data = task_data
289
485
  self.mode = self.task_data["mode"]
486
+
487
+ if task_data['id'].to_s != self.id.to_s
488
+ raise ZuoraConnect::Exceptions::MissMatch.new("Wrong Instance Identifier/Lookup")
489
+ end
490
+
290
491
  self.task_data.each do |k,v|
291
492
  if k.match(/^(.*)_login$/)
292
493
  tmp = ZuoraConnect::Login.new(v)
@@ -301,7 +502,7 @@ module ZuoraConnect
301
502
  else
302
503
  tmp.client.current_session = session["#{self.id}::#{k}:current_session"] if session["#{self.id}::#{k}:current_session"]
303
504
  tmp.client.bearer_token = session["#{self.id}::#{k}:bearer_token"] if session["#{self.id}::#{k}:bearer_token"] && tmp.client.respond_to?(:bearer_token) ## need incase session id goes from basic to aouth in same redis store
304
- tmp.client.oauth_session_expires_at = session["#{self.id}::#{k}:oauth_session_expires_at"] if session["#{self.id}::#{k}:oauth_session_expires_at"] && tmp.client.respond_to?(:oauth_session_expires_at)
505
+ tmp.client.oauth_session_expires_at = session["#{self.id}::#{k}:oauth_session_expires_at"] if session["#{self.id}::#{k}:oauth_session_expires_at"] && tmp.client.respond_to?(:oauth_session_expires_at)
305
506
  end
306
507
  end
307
508
  self.logins[k] = tmp
@@ -315,9 +516,17 @@ module ZuoraConnect
315
516
  self.locale = v["local"]
316
517
  end
317
518
  end
519
+ rescue ZuoraConnect::Exceptions::MissMatch => ex
520
+ raise
521
+ rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex
522
+ raise
318
523
  rescue => ex
319
- Rails.logger.error("Task Data: #{task_data}") if task_data.present?
320
- Rails.logger.error("Task Session: #{session.to_h}") if session.present?
524
+ ZuoraConnect.logger.error("Build Task Error", ex)
525
+ ZuoraConnect.logger.error("Task Data: #{task_data}") if task_data.present?
526
+ if session.present?
527
+ ZuoraConnect.logger.error("Task Session: #{session.to_h}") if session.methods.include?(:to_h)
528
+ ZuoraConnect.logger.error("Task Session: #{session.to_hash}") if session.methods.include?(:to_hash)
529
+ end
321
530
  raise
322
531
  end
323
532
 
@@ -339,11 +548,11 @@ module ZuoraConnect
339
548
  end
340
549
  return parsed_json
341
550
  elsif response.code == 400
342
- raise ZuoraConnect::Exceptions::APIError.new(message: parsed_json['errors'].join(' '), response: response.body, code: response.code)
551
+ raise ZuoraConnect::Exceptions::APIError.new(message: parsed_json['errors'].join(' '), response: response)
343
552
  else
344
553
  raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
345
554
  end
346
- rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS).concat(ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
555
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
347
556
  if (update_login_count += 1) < 3
348
557
  retry
349
558
  else
@@ -360,6 +569,47 @@ module ZuoraConnect
360
569
  end
361
570
  end
362
571
 
572
+ def fetch_org_details(debug: false)
573
+ details_count ||= 0
574
+ self.refresh if !defined?(self.target_login)
575
+
576
+ response = HTTParty.get("#{ZuoraConnect.configuration.url}/api/#{self.api_version}/tenants/search?hostname=#{self.target_login.client.hostname}&node_id=#{self.zuora_entity_ids.first}")
577
+
578
+ if response.success?
579
+ parsed_json = JSON.parse(response.body)
580
+
581
+ #Set Org
582
+ if self.id >= 25000000 && parsed_json['organization'].present?
583
+ login_cache = self.zuora_logins
584
+ login_cache.delete('organization')
585
+ self.zuora_logins = login_cache.merge({'organizations' => [parsed_json['organization']]})
586
+ end
587
+ if defined?(ZuoraConnect::AppInstance::CONNECT_APPLICATION_ID)
588
+ downloads = parsed_json.fetch('downloads',[]).select{|a| a['applicationId'] == ZuoraConnect::AppInstance::CONNECT_APPLICATION_ID }.map { |h| h.slice('Name', 'provisionState') }
589
+ Rails.logger.info("Instance Downloads: #{downloads.to_s}") if debug
590
+ if downloads.size > 1
591
+ self.provision_status = 'MultipleProvisioningRecords'
592
+ self.provisioned_app = downloads.map {|d| d['Name']}.to_s
593
+ elsif downloads.size == 1
594
+ self.provision_status = downloads.first['provisionState']
595
+ self.provisioned_app = downloads.first['Name']
596
+ else
597
+ self.provision_status = 'NoProvisioningRecords'
598
+ self.provisioned_app = nil
599
+ end
600
+ end
601
+ self.save(:validate => false)
602
+
603
+ return parsed_json
604
+ end
605
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
606
+ if (details_count += 1) < 3
607
+ retry
608
+ else
609
+ raise
610
+ end
611
+ end
612
+
363
613
  def update_task(options)
364
614
  update_task_count ||= 0
365
615
  response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/update_task",:body => {:access_token => self.username}.merge(options))
@@ -367,11 +617,11 @@ module ZuoraConnect
367
617
  if response.code == 200
368
618
  return parsed_json
369
619
  elsif response.code == 400
370
- raise ZuoraConnect::Exceptions::APIError.new(message: parsed_json['errors'].join(' '), response: response.body, code: response.code)
620
+ raise ZuoraConnect::Exceptions::APIError.new(message: parsed_json['errors'].join(' '), response: response)
371
621
  else
372
622
  raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
373
623
  end
374
- rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS).concat(ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
624
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
375
625
  if (update_task_count += 1) < 3
376
626
  retry
377
627
  else
@@ -387,13 +637,13 @@ module ZuoraConnect
387
637
  raise
388
638
  end
389
639
  end
390
- #### END Task Mathods ####
640
+ #### END Task Methods ####
391
641
 
392
- #### START Connect OAUTH methods ####
393
- def check_oauth_state(method)
642
+ #### START Connect OAUTH Methods ####
643
+ def check_oauth_state(method=nil)
394
644
  #Refresh token if already expired
395
645
  if self.oauth_expired?
396
- Rails.logger.debug("[#{self.id}] Before '#{method}' method, Oauth expired")
646
+ ZuoraConnect.logger.debug("Before '#{method}' method, Oauth expired")
397
647
  self.refresh_oauth
398
648
  end
399
649
  end
@@ -403,16 +653,12 @@ module ZuoraConnect
403
653
  end
404
654
 
405
655
  def refresh_oauth
406
- refresh_oauth_count ||= 0
407
- start = Time.now
408
- params = {
409
- :grant_type => "refresh_token",
410
- :redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
411
- :refresh_token => self.refresh_token
412
- }
413
- response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token",:body => params)
414
- response_time = Time.now - start
415
- Rails.logger.info("[#{self.id}] REFRESH OAUTH - In #{response_time.round(2).to_s}")
656
+ refresh_oauth_count ||= 0
657
+ response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token", body: {
658
+ :grant_type => "refresh_token",
659
+ :redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
660
+ :refresh_token => self.refresh_token
661
+ })
416
662
 
417
663
  if response.code == 200
418
664
  response_body = JSON.parse(response.body)
@@ -422,15 +668,15 @@ module ZuoraConnect
422
668
  self.oauth_expires_at = Time.at(response_body["created_at"].to_i) + response_body["expires_in"].seconds
423
669
  self.save(:validate => false)
424
670
  else
425
- Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed Code #{response.code}")
426
671
  raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Refreshing Access Token", response.body, response.code)
427
672
  end
428
- rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS).concat(ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
673
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
429
674
  if (refresh_oauth_count += 1) < 3
430
- Rails.logger.info("[#{self.id}] REFRESH OAUTH - #{ex.class} Retrying(#{refresh_oauth_count})")
675
+ sleep(CONNECT_COMMUNICATION_SLEEP)
676
+ ZuoraConnect.logger.debug("REFRESH OAUTH - Connection Failure Retrying(#{refresh_oauth_count})", ex, self.default_ougai_items)
431
677
  retry
432
678
  else
433
- Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - #{ex.class} Failed #{refresh_oauth_count}x")
679
+ Rails.logger.fatal("REFRESH OAUTH - Connection Failed", ex, self.default_ougai_items)
434
680
  raise
435
681
  end
436
682
  rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
@@ -441,14 +687,14 @@ module ZuoraConnect
441
687
  return if !self.oauth_expired?
442
688
 
443
689
  if (refresh_oauth_count += 1) < 3
444
- Rails.logger.info("[#{self.id}] REFRESH OAUTH - Failed Retrying(#{refresh_oauth_count})")
690
+ ZuoraConnect.logger.debug("REFRESH OAUTH - Communication Failure Retrying(#{refresh_oauth_count})", ex, self.default_ougai_items)
445
691
  retry
446
692
  else
447
- Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed #{refresh_oauth_count}x")
693
+ ZuoraConnect.logger.fatal("REFRESH OAUTH - Communication Failed #{ex.code}", ex, self.default_ougai_items)
448
694
  raise
449
695
  end
450
696
  end
451
- #### END Connect OAUTH methods ####
697
+ #### END Connect OAUTH Methods ####
452
698
 
453
699
  #### START AppInstance Temporary Persistance Methods ####
454
700
  def marked_for_refresh?
@@ -486,7 +732,7 @@ module ZuoraConnect
486
732
  begin
487
733
  redis_get_command ||= 0
488
734
  cached_instance = Redis.current.get("AppInstance:#{self.id}")
489
- rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS).concat(ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
735
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
490
736
  if (redis_get_command += 1) < 3
491
737
  retry
492
738
  else
@@ -494,10 +740,10 @@ module ZuoraConnect
494
740
  end
495
741
  end
496
742
  if cached_instance.blank?
497
- Rails.logger.debug("[#{self.id}] Cached AppInstance Missing")
743
+ ZuoraConnect.logger.debug("Cached AppInstance Missing", self.default_ougai_items)
498
744
  return session
499
745
  else
500
- Rails.logger.debug("[#{self.id}] Cached AppInstance Found")
746
+ ZuoraConnect.logger.debug("Cached AppInstance Found", self.default_ougai_items)
501
747
  return decrypt_data(data: cached_instance, rescue_return: session).merge(session)
502
748
  end
503
749
  else
@@ -505,11 +751,15 @@ module ZuoraConnect
505
751
  end
506
752
  end
507
753
 
754
+ def delete_app_instance
755
+ Redis.current.del("AppInstance:#{self.id}")
756
+ end
757
+
508
758
  def cache_app_instance
509
759
  if defined?(Redis.current)
510
760
  #Task data must be present and the last refresh cannot be old. We dont want to overwite new cache data with old
511
761
  if self.task_data.present? && (self.last_refresh.to_i > INSTANCE_REFRESH_WINDOW.ago.to_i)
512
- Rails.logger.debug("[#{self.id}] Caching AppInstance")
762
+ ZuoraConnect.logger.debug("Caching AppInstance", self.default_ougai_items)
513
763
  Redis.current.setex("AppInstance:#{self.id}", INSTANCE_REDIS_CACHE_PERIOD.to_i, self.encrypt_data(data: self.save_data))
514
764
  end
515
765
  end
@@ -536,12 +786,7 @@ module ZuoraConnect
536
786
 
537
787
  #Redis is not defined strip out old data
538
788
  if !defined?(Redis.current)
539
- session["#{self.id}::task_data"].delete('applications')
540
- session["#{self.id}::task_data"].delete('tenant_ids')
541
- session["#{self.id}::task_data"].delete('organizations')
542
- session["#{self.id}::task_data"].select {|k,v| k.include?('login') && v['tenant_type'] == 'Zuora'}.each do |login_key, login_data|
543
- session["#{self.id}::task_data"][login_key]['entities'] = (login_data.dig('entities') || []).map {|entity| entity.slice('id', 'tenantId')}
544
- end
789
+ strip_cache_data(object: session["#{self.id}::task_data"])
545
790
  end
546
791
 
547
792
  session["#{self.id}::last_refresh"] = self.last_refresh
@@ -549,6 +794,14 @@ module ZuoraConnect
549
794
  return session
550
795
  end
551
796
 
797
+ def strip_cache_data(object: {}, keys: ['applications', 'tokens','tenant_ids', 'organizations','user_settings'] )
798
+ keys.each {|key| object.delete(key) }
799
+ object.select {|k,v| k.include?('login') && v['tenant_type'] == 'Zuora'}.each do |login_key, login_data|
800
+ object[login_key]['entities'] = login_data.fetch('entities',[]).map {|entity| entity.slice('id', 'tenantId', 'entityId', 'displayName')}
801
+ end
802
+ return object
803
+ end
804
+
552
805
  def encryptor
553
806
  # Default values for Rails 4 apps
554
807
  key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
@@ -561,18 +814,19 @@ module ZuoraConnect
561
814
  def decrypt_data(data: nil, rescue_return: nil, log_fatal: true)
562
815
  return data if data.blank?
563
816
  if Rails.env == 'development'
564
- begin
817
+ begin
565
818
  return JSON.parse(data)
566
819
  rescue JSON::ParserError => ex
567
820
  return data
568
821
  end
569
822
  else
570
- begin
823
+ begin
571
824
  return JSON.parse(encryptor.decrypt_and_verify(CGI::unescape(data)))
572
825
  rescue ActiveSupport::MessageVerifier::InvalidSignature => ex
573
- Rails.logger.add(Logger::FATAL, 'Error Decrypting') if log_fatal
826
+ ZuoraConnect.logger.error("Error Decrypting", ex, self.default_ougai_items) if log_fatal
574
827
  return rescue_return
575
828
  rescue JSON::ParserError => ex
829
+ ZuoraConnect.logger.error("JSON Parse Error", ex, self.default_ougai_items) if log_fatal
576
830
  return encryptor.decrypt_and_verify(CGI::unescape(data))
577
831
  end
578
832
  end
@@ -622,7 +876,7 @@ module ZuoraConnect
622
876
  if paused_user == current_user || paused_user.blank?
623
877
  Resque.redis.zrem("PauseQueue", "#{self.id}__#{paused_user}")
624
878
  else
625
- raise "Can only unpause for user #{paused_user}."
879
+ raise "Can only unpause for user #{paused_user}."
626
880
  end
627
881
  end
628
882
  ### END Resque Helping Methods ####
@@ -632,10 +886,10 @@ module ZuoraConnect
632
886
  self.update_column(:catalog_update_attempt_at, Time.now.utc)
633
887
 
634
888
  entity_reference = entity_id.blank? ? 'Default' : entity_id
635
- Rails.logger.debug("Fetch Catalog")
636
- Rails.logger.debug("Zuora Entity: #{entity_id.blank? ? 'default' : entity_id}")
889
+ ZuoraConnect.logger.debug("Fetch Catalog")
890
+ ZuoraConnect.logger.debug("Zuora Entity: #{entity_id.blank? ? 'default' : entity_id}")
637
891
 
638
- login = zuora_login.client(entity_reference)
892
+ login = zuora_login.client(entity_id)
639
893
 
640
894
  old_logger = ActiveRecord::Base.logger
641
895
  ActiveRecord::Base.logger = nil
@@ -644,14 +898,8 @@ module ZuoraConnect
644
898
  response = {'nextPage' => login.rest_endpoint("catalog/products?pageSize=#{page_size}")}
645
899
  while !response["nextPage"].blank?
646
900
  url = login.rest_endpoint(response["nextPage"].split('/v1/').last)
647
- Rails.logger.debug("Fetch Catalog URL #{url}")
648
- output_json, response = login.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true)
649
- Rails.logger.debug("Fetch Catalog Response Code #{response.code}")
650
-
651
- if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
652
- Rails.logger.error("Fetch Catalog DATA #{output_json.to_json}")
653
- raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}")
654
- end
901
+ ZuoraConnect.logger.debug("Fetch Catalog URL #{url}")
902
+ output_json, response = login.rest_call(:debug => false, :url => url, :timeout_retry => true)
655
903
 
656
904
  output_json["products"].each do |product|
657
905
  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])
@@ -836,7 +1084,7 @@ module ZuoraConnect
836
1084
  if cache
837
1085
  Redis.current.sadd("Catalog:#{self.id}:Keys", ["Catalog:#{self.id}:#{object_id}:Hierarchy", "Catalog:#{self.id}:#{object_id}:Children:#{child_objects}"])
838
1086
  Redis.current.set("Catalog:#{self.id}:#{object_id}:Hierarchy", encrypt_data(data: object_hierarchy))
839
- Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog))
1087
+ Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog))
840
1088
  else
841
1089
  Redis.current.sadd("Catalog:#{self.id}:Keys", ["Catalog:#{self.id}:#{object_id}:Hierarchy"])
842
1090
  Redis.current.set("Catalog:#{self.id}:#{object_id}:Hierarchy", encrypt_data(data: object_hierarchy))
@@ -871,17 +1119,32 @@ module ZuoraConnect
871
1119
  ### END S3 Helping Methods #####
872
1120
 
873
1121
  ### START Aggregate Grouping Helping Methods ####
874
- def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)
1122
+ # Traverse entire database and run a query on table(table_name) with where clause(where_clause)
1123
+ # Data from each schema will be loaded into table(aggregate_name) into the public schema
1124
+ def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true, ignore_indexes: [])
875
1125
  self.update_functions
1126
+
1127
+ sql_result = ActiveRecord::Base.connection.execute <<-eos
1128
+ SELECT pid, relname, mode
1129
+ FROM pg_locks l
1130
+ JOIN pg_class t ON l.relation = t.oid AND t.relkind = 'r'
1131
+ WHERE t.relname = '#{aggregate_name}' AND l.mode ='AccessExclusiveLock';
1132
+ eos
1133
+ raise ZuoraConnect::Exceptions::Error.new("An existing lock detected while dropping table '#{aggregate_name}'") if sql_result.count > 0
1134
+
876
1135
  if index_table
877
- ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Index\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
878
- else
879
- ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'NO\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
1136
+ ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Index\', \'{%s}\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause), ignore_indexes.map { |index| "\"#{index}\"" }.join(',')])
1137
+ else
1138
+ ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'NO\',\'{}\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
880
1139
  end
881
1140
  end
882
1141
 
1142
+ # Load a psql script as a function in a transaction lock
883
1143
  def self.update_functions
884
- ActiveRecord::Base.connection.execute(File.read("#{Gem.loaded_specs["zuora_connect"].gem_dir}/app/views/sql/refresh_aggregate_table.txt"))
1144
+ ActiveRecord::Base.transaction do
1145
+ ActiveRecord::Base.connection.execute("SELECT pg_advisory_xact_lock(#{Zlib.crc32('refresh_aggregate_table')})")
1146
+ ActiveRecord::Base.connection.execute(File.read("#{Gem.loaded_specs["zuora_connect"].gem_dir}/app/views/sql/refresh_aggregate_table.txt"))
1147
+ end
885
1148
  end
886
1149
  ### END Aggregate Grouping Helping Methods #####
887
1150
 
@@ -904,13 +1167,15 @@ module ZuoraConnect
904
1167
  end
905
1168
 
906
1169
  def reload_attributes(selected_attributes)
907
- raise "Attibutes must be array" if selected_attributes.class != Array
908
- value_attributes = self.class.unscoped.where(:id=>id).select(selected_attributes).first.attributes
1170
+ raise "Attibutes must be array" unless selected_attributes.is_a?(Array)
1171
+ selected_attributes.push(:organizations, :environment, :zuora_tenant_ids)
1172
+ selected_attributes.uniq!
1173
+ value_attributes = self.class.unscoped.where(id: id).select(selected_attributes).first.attributes
909
1174
  value_attributes.each do |key, value|
910
1175
  next if key == "id" && value.blank?
911
1176
  self.send(:write_attribute, key, value)
912
1177
  end
913
- return self
1178
+ self
914
1179
  end
915
1180
 
916
1181
  def instance_failure(failure)
@@ -939,14 +1204,32 @@ module ZuoraConnect
939
1204
 
940
1205
  def method_missing(method_sym, *arguments, &block)
941
1206
  if method_sym.to_s.include?("login")
942
- Rails.logger.fatal("Method Missing #{method_sym}")
943
- Rails.logger.fatal("Instance Data: #{self.task_data}")
944
- Rails.logger.fatal("Instance Logins: #{self.logins}")
1207
+ ZuoraConnect.logger.fatal("Method Missing #{method_sym}. Instance Data: #{self.task_data} Instance Logins: #{self.logins}")
945
1208
  end
946
1209
  super
947
1210
  end
948
1211
 
949
- method_hook :refresh, :updateOption, :update_logins, :before => :check_oauth_state
1212
+ def self.read_master_db
1213
+ if self.connection.respond_to?(:stick_to_master!)
1214
+ self.connection.stick_to_master!(false)
1215
+ yield
1216
+ Makara::Context.release_all
1217
+ else
1218
+ yield
1219
+ end
1220
+ end
1221
+
1222
+ def self.without_sticking
1223
+ if self.connection.respond_to?(:without_sticking)
1224
+ self.connection.without_sticking do
1225
+ yield
1226
+ end
1227
+ else
1228
+ yield
1229
+ end
1230
+ end
1231
+
1232
+ method_hook :updateOption, :update_logins, :before => :check_oauth_state
950
1233
  method_hook :new_session, :refresh, :build_task, :after => :apartment_switch
951
1234
  end
952
- end
1235
+ end