zuora_connect_oauth_alpha 2

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 (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