zuora_connect_oauth_alpha 2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +38 -0
  4. data/app/assets/javascripts/zuora_connect/api/v1/app_instance.js +2 -0
  5. data/app/assets/javascripts/zuora_connect/application.js +13 -0
  6. data/app/assets/stylesheets/zuora_connect/api/v1/app_instance.css +4 -0
  7. data/app/assets/stylesheets/zuora_connect/application.css +15 -0
  8. data/app/controllers/zuora_connect/admin/tenant_controller.rb +11 -0
  9. data/app/controllers/zuora_connect/api/v1/app_instance_controller.rb +37 -0
  10. data/app/controllers/zuora_connect/application_controller.rb +8 -0
  11. data/app/controllers/zuora_connect/static_controller.rb +26 -0
  12. data/app/helpers/zuora_connect/api/v1/app_instance_helper.rb +4 -0
  13. data/app/helpers/zuora_connect/application_helper.rb +5 -0
  14. data/app/models/zuora_connect/app_instance.rb +5 -0
  15. data/app/models/zuora_connect/app_instance_base.rb +734 -0
  16. data/app/models/zuora_connect/login.rb +37 -0
  17. data/app/views/layouts/zuora_connect/application.html.erb +14 -0
  18. data/app/views/sql/refresh_aggregate_table.txt +84 -0
  19. data/app/views/zuora_connect/static/invalid_app_instance_error.html.erb +65 -0
  20. data/app/views/zuora_connect/static/session_error.html.erb +63 -0
  21. data/config/initializers/apartment.rb +95 -0
  22. data/config/initializers/object_method_hooks.rb +27 -0
  23. data/config/initializers/redis.rb +10 -0
  24. data/config/initializers/resque.rb +5 -0
  25. data/config/initializers/to_bool.rb +24 -0
  26. data/config/routes.rb +12 -0
  27. data/db/migrate/20100718151733_create_connect_app_instances.rb +9 -0
  28. data/db/migrate/20101024162319_add_tokens_to_app_instance.rb +6 -0
  29. data/db/migrate/20101024220705_add_token_to_app_instance.rb +5 -0
  30. data/db/migrate/20110131211919_add_sessions_table.rb +13 -0
  31. data/db/migrate/20110411200303_add_expiration_to_app_instance.rb +5 -0
  32. data/db/migrate/20110413191512_add_new_api_token.rb +5 -0
  33. data/db/migrate/20110503003602_add_catalog_data_to_app_instance.rb +6 -0
  34. data/db/migrate/20110503003603_add_catalog_mappings_to_app_instance.rb +5 -0
  35. data/db/migrate/20110503003604_catalog_default.rb +5 -0
  36. data/db/migrate/20180301052853_add_catalog_attempted_at.rb +5 -0
  37. data/lib/resque/additions.rb +53 -0
  38. data/lib/resque/dynamic_queues.rb +131 -0
  39. data/lib/resque/self_lookup.rb +19 -0
  40. data/lib/tasks/zuora_connect_tasks.rake +24 -0
  41. data/lib/zuora_connect_oauth_alpha.rb +38 -0
  42. data/lib/zuora_connect_oauth_alpha/configuration.rb +40 -0
  43. data/lib/zuora_connect_oauth_alpha/controllers/helpers.rb +165 -0
  44. data/lib/zuora_connect_oauth_alpha/engine.rb +30 -0
  45. data/lib/zuora_connect_oauth_alpha/exceptions.rb +50 -0
  46. data/lib/zuora_connect_oauth_alpha/railtie.rb +35 -0
  47. data/lib/zuora_connect_oauth_alpha/version.rb +4 -0
  48. data/lib/zuora_connect_oauth_alpha/views/helpers.rb +9 -0
  49. data/test/controllers/zuora_connect/api/v1/app_instance_controller_test.rb +13 -0
  50. data/test/dummy/README.rdoc +28 -0
  51. data/test/dummy/Rakefile +6 -0
  52. data/test/dummy/app/assets/javascripts/application.js +13 -0
  53. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  54. data/test/dummy/app/controllers/application_controller.rb +5 -0
  55. data/test/dummy/app/helpers/application_helper.rb +2 -0
  56. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  57. data/test/dummy/bin/bundle +3 -0
  58. data/test/dummy/bin/rails +4 -0
  59. data/test/dummy/bin/rake +4 -0
  60. data/test/dummy/bin/setup +29 -0
  61. data/test/dummy/config.ru +4 -0
  62. data/test/dummy/config/application.rb +26 -0
  63. data/test/dummy/config/boot.rb +5 -0
  64. data/test/dummy/config/database.yml +25 -0
  65. data/test/dummy/config/environment.rb +5 -0
  66. data/test/dummy/config/environments/development.rb +41 -0
  67. data/test/dummy/config/environments/production.rb +79 -0
  68. data/test/dummy/config/environments/test.rb +42 -0
  69. data/test/dummy/config/initializers/assets.rb +11 -0
  70. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  71. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  72. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  73. data/test/dummy/config/initializers/inflections.rb +16 -0
  74. data/test/dummy/config/initializers/mime_types.rb +4 -0
  75. data/test/dummy/config/initializers/session_store.rb +3 -0
  76. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  77. data/test/dummy/config/locales/en.yml +23 -0
  78. data/test/dummy/config/routes.rb +4 -0
  79. data/test/dummy/config/secrets.yml +22 -0
  80. data/test/dummy/public/404.html +67 -0
  81. data/test/dummy/public/422.html +67 -0
  82. data/test/dummy/public/500.html +66 -0
  83. data/test/dummy/public/favicon.ico +0 -0
  84. data/test/fixtures/zuora_connect/app_instances.yml +11 -0
  85. data/test/integration/navigation_test.rb +8 -0
  86. data/test/lib/generators/zuora_connect/datatable_generator_test.rb +16 -0
  87. data/test/models/zuora_connect/app_instance_test.rb +9 -0
  88. data/test/test_helper.rb +21 -0
  89. data/test/zuora_connect_test.rb +7 -0
  90. metadata +361 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2690030bcc5cae84498de1732142c9290aa988ae2ab862a901a387cb9aa0cb71
4
+ data.tar.gz: 1328b2fc3ae1d0b61f5d3bbc0abac9cb67adb5ea4f88839c0fd9a53920fd8cae
5
+ SHA512:
6
+ metadata.gz: d5b3f010c31d92e4f0e7ee2e208ca31b2f5b103de53e53a6364afb7602dc6d0894b551138a9cf729aa95ac78009315e0b1ff07fd286df5885763f722c9610db7
7
+ data.tar.gz: b0a2dc9acb401029083b2f69303958754c04c922366163b001a4e98a9ef05ee836e8bc3fc5c2684a5b0efdde30f0b0b2a43604876b87af44b4ebbdcf40d7804e
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Matthew Ingle
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+ require 'apartment'
9
+ Apartment.db_migrate_tenants = false
10
+ RDoc::Task.new(:rdoc) do |rdoc|
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = 'Connect'
13
+ rdoc.options << '--line-numbers'
14
+ rdoc.rdoc_files.include('README.rdoc')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
18
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
19
+ load 'rails/tasks/engine.rake'
20
+
21
+
22
+ load 'rails/tasks/statistics.rake'
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+
38
+ task default: :test
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,11 @@
1
+ require_dependency "zuora_connect/application_controller"
2
+
3
+ module ZuoraConnect
4
+ class Admin::TenantController < ApplicationController
5
+ before_filter :check_admin
6
+ def index
7
+
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ require_dependency "zuora_connect/application_controller"
2
+
3
+ module ZuoraConnect
4
+ class Api::V1::AppInstanceController < ApplicationController
5
+
6
+ def create
7
+ Apartment::Tenant.create(session['AppInstance'])
8
+ respond_to do |format|
9
+ format.json {render :json => "Created"}
10
+ end
11
+ end
12
+
13
+ def drop
14
+ instance_id = @appinstance.id
15
+ if session["#{instance_id}::destroy"] && ZuoraConnect::AppInstance.where(:id => instance_id).size != 0
16
+ ZuoraConnect::AppInstance.destroy(instance_id)
17
+ msg = Apartment::Tenant.drop(instance_id)
18
+ respond_to do |format|
19
+ message = Hash.new
20
+ message = {"error" => {:message => msg.error_message}} if msg.error_message != ""
21
+ message["message"] = msg.result_status == 1 ? "success" : "error"
22
+ format.json {render :json => message}
23
+ end
24
+ else
25
+ respond_to do |format|
26
+ format.json {render :json => { "message" => "Unauthorized"}}
27
+ end
28
+ end
29
+ end
30
+
31
+ def status
32
+
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ module ZuoraConnect
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ before_filter :authenticate_connect_app_request
5
+ after_filter :persist_connect_app_session
6
+
7
+ end
8
+ end
@@ -0,0 +1,26 @@
1
+ module ZuoraConnect
2
+ class StaticController < ApplicationController
3
+ before_filter :authenticate_connect_app_request, :except => [:health, :session_error, :invalid_app_instance_error]
4
+ after_filter :persist_connect_app_session, :except => [:health, :session_error, :invalid_app_instance_error]
5
+ def session_error
6
+ respond_to do |format|
7
+ format.html
8
+ format.json { render json: { message: "Session Error", status: 500 }, status: 500 }
9
+ end
10
+ end
11
+
12
+ def invalid_app_instance_error
13
+ respond_to do |format|
14
+ format.html
15
+ format.json {render json: { message: "Invalid App Instance", status: 500 }, status: 500 }
16
+ end
17
+ end
18
+
19
+ def health
20
+ render json: {
21
+ message: "Alive",
22
+ status: 200
23
+ }, status: 200
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,4 @@
1
+ module ZuoraConnect
2
+ module Api::V1::AppInstanceHelper
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module ZuoraConnect
2
+ module ApplicationHelper
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module ZuoraConnect
2
+ class AppInstance < ZuoraConnect::AppInstanceBase
3
+ default_scope {select(ZuoraConnect::AppInstance.column_names.delete_if {|x| ["catalog_mapping", "catalog"].include?(x) }) }
4
+ end
5
+ end
@@ -0,0 +1,734 @@
1
+ module ZuoraConnect
2
+ class AppInstanceBase < ActiveRecord::Base
3
+ default_scope {select(ZuoraConnect::AppInstance.column_names.delete_if {|x| ["catalog_mapping", "catalog"].include?(x) }) }
4
+ after_initialize :init
5
+ self.table_name = "zuora_connect_app_instances"
6
+ attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version
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
+
14
+ def init
15
+ @options = Hash.new
16
+ @logins = Hash.new
17
+ @api_version = "v2"
18
+ self.attr_builder("timezone", ZuoraConnect.configuration.default_time_zone)
19
+ self.attr_builder("locale", ZuoraConnect.configuration.default_locale)
20
+ PaperTrail.whodunnit = "Backend" if defined?(PaperTrail)
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
29
+ Apartment::Tenant.switch!(self.id) if self.persisted?
30
+ rescue Apartment::TenantNotFound => ex
31
+ Apartment::Tenant.create(self.id.to_s)
32
+ retry
33
+ end
34
+ if migrate && ActiveRecord::Migrator.needs_migration?
35
+ Apartment::Migrator.migrate(self.id)
36
+ end
37
+ Thread.current[:appinstance] = self
38
+ end
39
+
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
62
+ end
63
+
64
+ build_task(mock_task_data, session)
65
+ else
66
+ time_expire = (session["#{self.id}::last_refresh"] || Time.now).to_i - INSTANCE_REFRESH_WINDOW.ago.to_i
67
+
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)
72
+
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)
92
+ else
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")
97
+ end
98
+ build_task(session["#{self.id}::task_data"], session)
99
+ end
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
116
+ end
117
+
118
+ def refresh(session = nil)
119
+ refresh_count ||= 0
120
+
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
131
+ else
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)
134
+ end
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
139
+ else
140
+ Rails.logger.fatal("[#{self.id}] REFRESH TASK - #{ex.class} Failed #{refresh_count}x")
141
+ raise
142
+ end
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
150
+ else
151
+ Rails.logger.fatal("[#{self.id}] REFRESH TASK - Failed #{refresh_count}x")
152
+ raise
153
+ end
154
+ end
155
+
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.present? && 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).z_session_token = session["#{self.id}::#{k}::#{entity_id}:z_session_token"] if session["#{self.id}::#{k}::#{entity_id}:z_session_token"]
168
+ tmp.client(entity_id).bearer_token = session["#{self.id}::#{k}::#{entity_id}:bearer_token"] if session["#{self.id}::#{k}::#{entity_id}:bearer_token"]
169
+ tmp.client(entity_id).oauth_session_expires_at = session["#{self.id}::#{k}::#{entity_id}:oauth_session_expires_at"] if session["#{self.id}::#{k}::#{entity_id}:oauth_session_expires_at"]
170
+ end
171
+ else
172
+ tmp.client.z_session_token = session["#{self.id}::#{k}:z_session_token"] if session["#{self.id}::#{k}:z_session_token"]
173
+ tmp.client.bearer_token = session["#{self.id}::#{k}:bearer_token"] if session["#{self.id}::#{k}:bearer_token"]
174
+ tmp.client.oauth_session_expires_at = session["#{self.id}::#{k}:oauth_session_expires_at"] if session["#{self.id}::#{k}:oauth_session_expires_at"]
175
+ end
176
+ end
177
+ @logins[k] = tmp
178
+ self.attr_builder(k, @logins[k])
179
+ elsif k == "options"
180
+ v.each do |opt|
181
+ @options[opt["config_name"]] = opt
182
+ end
183
+ elsif k == "user_settings"
184
+ self.timezone = v["timezone"]
185
+ self.locale = v["local"]
186
+ end
187
+ end
188
+ end
189
+
190
+ def updateOption(optionId, value)
191
+ return HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
192
+ end
193
+
194
+ #This can update an existing login, add a new login, change to another existing login
195
+ #EXAMPLE: {"name": "ftp_login_14","username": "ftplogin7","tenant_type": "Custom","password": "test2","url": "www.ftp.com","custom_data": { "path": "/var/usr/test"}}
196
+ def update_logins(options)
197
+ update_login_count ||= 0
198
+
199
+ response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/logins",:body => {:access_token => self.username}.merge(options))
200
+ if response.code == 200
201
+ return JSON.parse(response.body)
202
+ else
203
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
204
+ end
205
+ rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError
206
+ if (update_login_count += 1) < 3
207
+ retry
208
+ else
209
+ raise
210
+ end
211
+ rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
212
+ if (update_login_count += 1) < 3
213
+ if ex.code == 401
214
+ self.refresh_oauth
215
+ end
216
+ retry
217
+ else
218
+ raise
219
+ end
220
+ end
221
+ #### END Task Mathods ####
222
+
223
+ #### START Connect OAUTH methods ####
224
+ def check_oauth_state(method)
225
+ #Refresh token if already expired
226
+ if self.oauth_expired?
227
+ Rails.logger.debug("[#{self.id}] Before '#{method}' method, Oauth expired")
228
+ self.refresh_oauth
229
+ end
230
+ end
231
+
232
+ def oauth_expired?
233
+ (self.oauth_expires_at < Time.now)
234
+ end
235
+
236
+ def refresh_oauth
237
+ refresh_oauth_count ||= 0
238
+
239
+ start = Time.now
240
+ params = {
241
+ :grant_type => "refresh_token",
242
+ :redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
243
+ :refresh_token => self.refresh_token
244
+ }
245
+ response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token",:body => params)
246
+ response_time = Time.now - start
247
+ Rails.logger.info("[#{self.id}] REFRESH OAUTH - In #{response_time.round(2).to_s}")
248
+
249
+ if response.code == 200
250
+ response_body = JSON.parse(response.body)
251
+
252
+ self.refresh_token = response_body["refresh_token"]
253
+ self.access_token = response_body["access_token"]
254
+ self.oauth_expires_at = Time.at(response_body["created_at"].to_i) + response_body["expires_in"].seconds
255
+ self.save(:validate => false)
256
+ else
257
+ Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed Code #{response.code}")
258
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Refreshing Access Token", response.body, response.code)
259
+ end
260
+ rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
261
+ if (refresh_oauth_count += 1) < 3
262
+ Rails.logger.info("[#{self.id}] REFRESH OAUTH - #{ex.class} Retrying(#{refresh_oauth_count})")
263
+ retry
264
+ else
265
+ Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - #{ex.class} Failed #{refresh_oauth_count}x")
266
+ raise
267
+ end
268
+ rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
269
+ sleep(5)
270
+ self.reload_attributes([:refresh_token, :oauth_expires_at, :access_token]) #Reload only the refresh token for retry
271
+
272
+ #After reload, if nolonger expired return
273
+ return if !self.oauth_expired?
274
+
275
+ if (refresh_oauth_count += 1) < 3
276
+ Rails.logger.info("[#{self.id}] REFRESH OAUTH - Failed Retrying(#{refresh_oauth_count})")
277
+ retry
278
+ else
279
+ Rails.logger.fatal("[#{self.id}] REFRESH OAUTH - Failed #{refresh_oauth_count}x")
280
+ raise
281
+ end
282
+ end
283
+ #### END Connect OAUTH methods ####
284
+
285
+ #### START AppInstance Temporary Persistance Methods ####
286
+ def marked_for_refresh?
287
+ return defined?(Redis.current) ? Redis.current.get("AppInstance:#{self.id}:Refreshing").to_bool : false
288
+ end
289
+
290
+ def reset_mark_for_refresh
291
+ Redis.current.del("AppInstance:#{self.id}:Refreshing") if defined?(Redis.current)
292
+ end
293
+
294
+ def reset_mark_refreshed_at
295
+ return defined?(Redis.current) ? REFRESH_TIMEOUT.to_i - Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
296
+ end
297
+
298
+ def reset_mark_expires_at
299
+ return defined?(Redis.current) ? Redis.current.ttl("AppInstance:#{self.id}:Refreshing") : 0
300
+ end
301
+
302
+ def mark_for_refresh
303
+ return defined?(Redis.current) ? Redis.current.set("AppInstance:#{self.id}:Refreshing", true, {:nx => true, :ex => REFRESH_TIMEOUT.to_i}) : true
304
+ end
305
+
306
+ def data_lookup(session: {})
307
+ if defined?(PaperTrail)
308
+ PaperTrail.whodunnit = session["#{self.id}::user::email"].present? ? session["#{self.id}::user::email"] : nil if session.present?
309
+ end
310
+ if defined?(Redis.current)
311
+ cached_instance = Redis.current.get("AppInstance:#{self.id}")
312
+ if cached_instance.blank?
313
+ Rails.logger.debug("[#{self.id}] Cached AppInstance Missing")
314
+ return session
315
+ else
316
+ Rails.logger.debug("[#{self.id}] Cached AppInstance Found")
317
+ return decrypt_data(data: cached_instance, rescue_return: session)
318
+ end
319
+ else
320
+ return session
321
+ end
322
+ end
323
+
324
+ def cache_app_instance
325
+ if defined?(Redis.current)
326
+ #Task data must be present and the last refresh cannot be old. We dont want to overwite new cache data with old
327
+ if self.task_data.present? && (self.last_refresh.to_i > INSTANCE_REFRESH_WINDOW.ago.to_i)
328
+ Rails.logger.info("[#{self.id}] Caching AppInstance")
329
+ Redis.current.setex("AppInstance:#{self.id}", INSTANCE_REDIS_CACHE_PERIOD.to_i, encrypt_data(data: self.save_data))
330
+ end
331
+ Redis.current.del("Deleted:#{self.id}")
332
+ end
333
+ end
334
+
335
+ def save_data(session = Hash.new)
336
+ self.logins.each do |key, login|
337
+ if login.tenant_type == "Zuora"
338
+ if login.available_entities.size > 1 && Rails.application.config.session_store != ActionDispatch::Session::CookieStore
339
+ login.available_entities.each do |entity_key|
340
+ session["#{self.id}::#{key}::#{entity_key}:z_session_token"] = login.client(entity_key).z_session_token if login.client.respond_to?(:z_session_token)
341
+ session["#{self.id}::#{key}::#{entity_key}:bearer_token"] = login.client(entity_key).bearer_token if login.client.respond_to?(:bearer_token)
342
+ session["#{self.id}::#{key}::#{entity_key}:oauth_session_expires_at"] = login.client(entity_key).oauth_session_expires_at if login.client.respond_to?(:oauth_session_expires_at)
343
+ end
344
+ else
345
+ session["#{self.id}::#{key}:z_session_token"] = login.client.z_session_token if login.client.respond_to?(:z_session_token)
346
+ session["#{self.id}::#{key}:bearer_token"] = login.client.bearer_token if login.client.respond_to?(:bearer_token)
347
+ session["#{self.id}::#{key}:oauth_session_expires_at"] = login.client.oauth_session_expires_at if login.client.respond_to?(:oauth_session_expires_at)
348
+ end
349
+ end
350
+ end
351
+ session["#{self.id}::task_data"] = self.task_data
352
+ session["#{self.id}::last_refresh"] = self.last_refresh
353
+ session["appInstance"] = self.id
354
+ return session
355
+ end
356
+
357
+ def encryptor
358
+ # Default values for Rails 4 apps
359
+ key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
360
+ key_generator = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base, iterations: key_iter_num)
361
+ secret, sign_secret = [key_generator.generate_key(salt), key_generator.generate_key(signed_salt)]
362
+ return ActiveSupport::MessageEncryptor.new(secret, sign_secret)
363
+ end
364
+
365
+ def decrypt_data(data: nil, rescue_return: nil)
366
+ return data if data.blank?
367
+ begin
368
+ if Rails.env == 'development'
369
+ return JSON.parse(data)
370
+ else
371
+ begin
372
+ return JSON.parse(encryptor.decrypt_and_verify(CGI::unescape(data)))
373
+ rescue ActiveSupport::MessageVerifier::InvalidSignature => ex
374
+ Rails.logger.fatal('Error Decrypting')
375
+ return rescue_return
376
+ end
377
+ end
378
+ rescue JSON::ParserError => ex
379
+ Rails.logger.fatal('Error Parsing')
380
+ return rescue_return
381
+ end
382
+ end
383
+
384
+ def encrypt_data(data: nil)
385
+ return data if data.blank?
386
+
387
+ if Rails.env == 'development'
388
+ return data.to_json
389
+ else
390
+ return encryptor.encrypt_and_sign(data.to_json)
391
+ end
392
+ end
393
+ #### END AppInstance Temporary Persistance Methods ####
394
+
395
+ ### START Resque Helping Methods ####
396
+ def api_limit(start: true, time: API_LIMIT_TIMEOUT.to_i)
397
+ if start
398
+ Redis.current.setex("APILimits:#{self.id}", time, true)
399
+ else
400
+ Redis.current.del("APILimits:#{self.id}")
401
+ end
402
+ end
403
+
404
+ def api_limit?
405
+ return Redis.current.get("APILimits:#{self.id}").to_bool
406
+ end
407
+
408
+ def queue_paused?
409
+ return Redis.current.get("resque:PauseQueue:#{self.id}").to_bool
410
+ end
411
+
412
+ def queue_pause(time: nil)
413
+ if time.present?
414
+ raise "Time must be fixnum of seconds." if time.class != Fixnum
415
+ Redis.current.setex("resque:PauseQueue:#{self.id}", time, true)
416
+ else
417
+ Redis.current.set("resque:PauseQueue:#{self.id}", true)
418
+ end
419
+ end
420
+
421
+ def queue_start
422
+ Redis.current.del("resque:PauseQueue:#{self.id}")
423
+ end
424
+ ### END Resque Helping Methods ####
425
+
426
+ ### START Catalog Helping Methods #####
427
+ def get_catalog(page_size: 5, zuora_login: self.login_lookup(type: "Zuora").first, entity_id: nil)
428
+ self.update_column(:catalog_update_attempt_at, Time.now.utc)
429
+
430
+ entity_reference = entity_id.blank? ? 'Default' : entity_id
431
+ Rails.logger.info("Fetch Catalog")
432
+ Rails.logger.info("Zuora Entity: #{entity_id.blank? ? 'default' : entity_id}")
433
+
434
+ login = zuora_login.client(entity_reference)
435
+
436
+ old_logger = ActiveRecord::Base.logger
437
+ ActiveRecord::Base.logger = nil
438
+ 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})
439
+
440
+ response = {'nextPage' => login.rest_endpoint("catalog/products?pageSize=#{page_size}")}
441
+ while !response["nextPage"].blank?
442
+ url = login.rest_endpoint(response["nextPage"].split('/v1/').last)
443
+ Rails.logger.debug("Fetch Catalog URL #{url}")
444
+ output_json, response = login.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true)
445
+ Rails.logger.debug("Fetch Catalog Response Code #{response.code}")
446
+
447
+ if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
448
+ Rails.logger.error("Fetch Catalog DATA #{output_json.to_json}")
449
+ raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}")
450
+ end
451
+
452
+ output_json["products"].each do |product|
453
+ 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])
454
+ rateplans = {}
455
+
456
+ product["productRatePlans"].each do |rateplan|
457
+ 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])
458
+ charges = {}
459
+
460
+ rateplan["productRatePlanCharges"].each do |charge|
461
+ 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])
462
+
463
+ charges[charge["id"]] = charge.merge({"productId" => product["id"], "productName" => product["name"], "productRatePlanId" => rateplan["id"], "productRatePlanName" => rateplan["name"] })
464
+ end
465
+
466
+ rateplan["productRatePlanCharges"] = charges
467
+ rateplans[rateplan["id"]] = rateplan.merge({"productId" => product["id"], "productName" => product["name"]})
468
+ end
469
+ product["productRatePlans"] = rateplans
470
+
471
+ 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])
472
+ end
473
+ end
474
+
475
+ # Move from tmp to actual
476
+ 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})
477
+ if defined?(Redis.current)
478
+ Redis.current.keys("Catalog:#{self.id}:*").each do |key|
479
+ Redis.current.del(key.to_s)
480
+ end
481
+ end
482
+ # Clear tmp holder
483
+ 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})
484
+
485
+ ActiveRecord::Base.logger = old_logger
486
+ self.update_column(:catalog_updated_at, Time.now.utc)
487
+ self.touch
488
+
489
+ # DO NOT RETURN CATALOG. THIS IS NOT SCALABLE WITH LARGE CATALOGS. USE THE CATALOG_LOOKUP method provided
490
+ return true
491
+ end
492
+
493
+ def catalog_outdated?(time: Time.now - 12.hours)
494
+ return self.catalog_updated_at.blank? || (self.catalog_updated_at < time)
495
+ end
496
+
497
+ def catalog_loaded?
498
+ return ActiveRecord::Base.connection.execute('SELECT id FROM "public"."zuora_connect_app_instances" WHERE "id" = %s AND catalog = \'{}\' LIMIT 1' % [self.id]).first.nil?
499
+ end
500
+
501
+ # Catalog lookup provides method to lookup zuora catalog efficiently.
502
+ # entity_id: If the using catalog json be field to store multiple entity product catalogs.
503
+ # object: The Object class desired to be returned. Available [:product, :rateplan, :charge]
504
+ # object_id: The id or id's of the object/objects to be returned.
505
+ # child_objects: Whether to include child objects of the object in question.
506
+ # cache: Store individual "1" object lookup in redis for caching.
507
+ def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)
508
+ entity_reference = entity_id.blank? ? 'Default' : entity_id
509
+
510
+ if object_id.present? && ![Array, String].include?(object_id.class)
511
+ raise "Object Id can only be a string or an array of strings"
512
+ end
513
+
514
+ if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
515
+ stub_catalog = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}"))
516
+ object_hierarchy = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Hierarchy"))
517
+ end
518
+
519
+ if defined?(object_hierarchy)
520
+ 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"})
521
+ end
522
+
523
+ case object
524
+ when :product
525
+ if object_id.nil?
526
+ string =
527
+ "SELECT "\
528
+ "json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
529
+ "FROM "\
530
+ "\"public\".\"zuora_connect_app_instances\", "\
531
+ "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
532
+ "WHERE "\
533
+ "\"id\" = %s" % [entity_reference, self.id]
534
+ else
535
+ if object_id.class == String
536
+ string =
537
+ "SELECT "\
538
+ "(catalog #> '{%s, %s}') #{child_objects ? '' : '- \'productRatePlans\''} AS item "\
539
+ "FROM "\
540
+ "\"public\".\"zuora_connect_app_instances\" "\
541
+ "WHERE "\
542
+ "\"id\" = %s" % [entity_reference, object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id, self.id]
543
+ elsif object_id.class == Array
544
+ string =
545
+ "SELECT "\
546
+ "json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
547
+ "FROM "\
548
+ "\"public\".\"zuora_connect_app_instances\", "\
549
+ "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
550
+ "WHERE "\
551
+ "\"product_id\" IN (\'%s\') AND "\
552
+ "\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
553
+ end
554
+ end
555
+
556
+ when :rateplan
557
+ if object_id.nil?
558
+ string =
559
+ "SELECT "\
560
+ "json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
561
+ "FROM "\
562
+ "\"public\".\"zuora_connect_app_instances\", "\
563
+ "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
564
+ "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
565
+ "WHERE "\
566
+ "\"id\" = %s" % [entity_reference, self.id]
567
+ else
568
+ if object_id.class == String
569
+ string =
570
+ "SELECT "\
571
+ "(catalog #> '{%s, %s, productRatePlans, %s}') #{child_objects ? '' : '- \'productRatePlanCharges\''} AS item "\
572
+ "FROM "\
573
+ "\"public\".\"zuora_connect_app_instances\" "\
574
+ "WHERE "\
575
+ "\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id, self.id]
576
+ elsif object_id.class == Array
577
+ string =
578
+ "SELECT "\
579
+ "json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
580
+ "FROM "\
581
+ "\"public\".\"zuora_connect_app_instances\", "\
582
+ "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
583
+ "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
584
+ "WHERE "\
585
+ "\"rateplan_id\" IN (\'%s\') AND "\
586
+ "\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
587
+ end
588
+ end
589
+
590
+ when :charge
591
+ if object_id.nil?
592
+ string =
593
+ "SELECT "\
594
+ "json_object_agg(charge_id, charge) as item "\
595
+ "FROM "\
596
+ "\"public\".\"zuora_connect_app_instances\", "\
597
+ "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
598
+ "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
599
+ "jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
600
+ "WHERE "\
601
+ "\"id\" = %s" % [entity_reference, self.id]
602
+ else
603
+ if object_id.class == String
604
+ string =
605
+ "SELECT "\
606
+ "catalog #> '{%s, %s, productRatePlans, %s, productRatePlanCharges, %s}' AS item "\
607
+ "FROM "\
608
+ "\"public\".\"zuora_connect_app_instances\" "\
609
+ "WHERE "\
610
+ "\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_hierarchy['productRatePlanId'], object_id.blank? ? BLANK_OBJECT_ID_LOOKUP : object_id , self.id]
611
+
612
+ elsif object_id.class == Array
613
+ string =
614
+ "SELECT "\
615
+ "json_object_agg(charge_id, charge) AS item "\
616
+ "FROM "\
617
+ "\"public\".\"zuora_connect_app_instances\", "\
618
+ "jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
619
+ "jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
620
+ "jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
621
+ "WHERE "\
622
+ "\"charge_id\" IN (\'%s\') AND "\
623
+ "\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
624
+ end
625
+ end
626
+ else
627
+ raise "Available objects include [:product, :rateplan, :charge]"
628
+ end
629
+
630
+ stub_catalog ||= JSON.parse(ActiveRecord::Base.connection.execute(string).first["item"] || "{}")
631
+
632
+ if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
633
+ Redis.current.set("Catalog:#{self.id}:#{object_id}:Hierarchy", encrypt_data(data: object_hierarchy))
634
+ Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog)) if cache
635
+ end
636
+ return stub_catalog
637
+ end
638
+ ### END Catalog Helping Methods #####
639
+
640
+ ### START S3 Helping Methods #####
641
+ def s3_client
642
+ require 'aws-sdk-s3'
643
+ if ZuoraConnect.configuration.mode == "Development"
644
+ @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)
645
+ else
646
+ @s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region)
647
+ end
648
+ end
649
+
650
+ def upload_to_s3(local_file,s3_path = nil)
651
+ s3_path = local_file.split("/").last if s3_path.nil?
652
+ obj = self.s3_client.bucket(ZuoraConnect.configuration.s3_bucket_name).object("#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{s3_path}}")
653
+ obj.upload_file(local_file, :server_side_encryption => 'AES256')
654
+ end
655
+
656
+ def get_s3_file_url(key)
657
+ require 'aws-sdk-s3'
658
+ signer = Aws::S3::Presigner.new(client: self.s3_client)
659
+ url = signer.presigned_url(:get_object, bucket: ZuoraConnect.configuration.s3_bucket_name, key: "#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{key}")
660
+ end
661
+ ### END S3 Helping Methods #####
662
+
663
+ ### START Aggregate Grouping Helping Methods ####
664
+ def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)
665
+ self.update_functions
666
+ #Broke function into two parts to ensure transaction size was small enough
667
+ ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Table\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
668
+ 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
669
+ end
670
+
671
+ def self.update_functions
672
+ ActiveRecord::Base.connection.execute(File.read("#{Gem.loaded_specs["zuora_connect"].gem_dir}/app/views/sql/refresh_aggregate_table.txt"))
673
+ end
674
+ ### END Aggregate Grouping Helping Methods #####
675
+
676
+ # Overide this method to avoid the new session call for api requests that use the before filter authenticate_app_api_request.
677
+ # This can be usefull for apps that dont need connect metadata call, or credentials, to operate for api requests
678
+ def new_session_for_api_requests(params: {})
679
+ return true
680
+ end
681
+
682
+ # Overide this method to avoid the new session call for ui requests that use the before filter authenticate_connect_app_request.
683
+ # This can be usefull for apps that dont need connect metadata call, or credentials, to operate for ui requests
684
+ def new_session_for_ui_requests(params: {})
685
+ return true
686
+ end
687
+
688
+ def reload_attributes(selected_attributes)
689
+ raise "Attibutes must be array" if selected_attributes.class != Array
690
+ value_attributes = self.class.unscoped.where(:id=>id).select(selected_attributes).first.attributes
691
+ value_attributes.each do |key, value|
692
+ next if key == "id" && value.blank?
693
+ self.send(:write_attribute, key, value)
694
+ end
695
+ return self
696
+ end
697
+
698
+ def instance_failure(failure)
699
+ raise failure
700
+ end
701
+
702
+ def send_email
703
+ end
704
+
705
+ def login_lookup(type: "Zuora")
706
+ results = []
707
+ self.logins.each do |name, login|
708
+ results << login if login.tenant_type == type
709
+ end
710
+ return results
711
+ end
712
+
713
+ def self.decrypt_response(resp)
714
+ OpenSSL::PKey::RSA.new(ZuoraConnect.configuration.private_key).private_decrypt(resp)
715
+ end
716
+
717
+ def attr_builder(field,val)
718
+ singleton_class.class_eval { attr_accessor "#{field}" }
719
+ send("#{field}=", val)
720
+ end
721
+
722
+ def method_missing(method_sym, *arguments, &block)
723
+ if method_sym.to_s.include?("login")
724
+ Rails.logger.fatal("Method Missing #{method_sym}")
725
+ Rails.logger.fatal("Instance Data: #{self.task_data}")
726
+ Rails.logger.fatal("Instance Logins: #{self.logins}")
727
+ end
728
+ super
729
+ end
730
+
731
+ method_hook :refresh, :updateOption, :update_logins, :before => :check_oauth_state
732
+ method_hook :new_session, :refresh, :build_task, :after => :apartment_switch
733
+ end
734
+ end