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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +38 -0
- data/app/assets/javascripts/zuora_connect/api/v1/app_instance.js +2 -0
- data/app/assets/javascripts/zuora_connect/application.js +13 -0
- data/app/assets/stylesheets/zuora_connect/api/v1/app_instance.css +4 -0
- data/app/assets/stylesheets/zuora_connect/application.css +15 -0
- data/app/controllers/zuora_connect/admin/tenant_controller.rb +11 -0
- data/app/controllers/zuora_connect/api/v1/app_instance_controller.rb +37 -0
- data/app/controllers/zuora_connect/application_controller.rb +8 -0
- data/app/controllers/zuora_connect/static_controller.rb +26 -0
- data/app/helpers/zuora_connect/api/v1/app_instance_helper.rb +4 -0
- data/app/helpers/zuora_connect/application_helper.rb +5 -0
- data/app/models/zuora_connect/app_instance.rb +5 -0
- data/app/models/zuora_connect/app_instance_base.rb +734 -0
- data/app/models/zuora_connect/login.rb +37 -0
- data/app/views/layouts/zuora_connect/application.html.erb +14 -0
- data/app/views/sql/refresh_aggregate_table.txt +84 -0
- data/app/views/zuora_connect/static/invalid_app_instance_error.html.erb +65 -0
- data/app/views/zuora_connect/static/session_error.html.erb +63 -0
- data/config/initializers/apartment.rb +95 -0
- data/config/initializers/object_method_hooks.rb +27 -0
- data/config/initializers/redis.rb +10 -0
- data/config/initializers/resque.rb +5 -0
- data/config/initializers/to_bool.rb +24 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20100718151733_create_connect_app_instances.rb +9 -0
- data/db/migrate/20101024162319_add_tokens_to_app_instance.rb +6 -0
- data/db/migrate/20101024220705_add_token_to_app_instance.rb +5 -0
- data/db/migrate/20110131211919_add_sessions_table.rb +13 -0
- data/db/migrate/20110411200303_add_expiration_to_app_instance.rb +5 -0
- data/db/migrate/20110413191512_add_new_api_token.rb +5 -0
- data/db/migrate/20110503003602_add_catalog_data_to_app_instance.rb +6 -0
- data/db/migrate/20110503003603_add_catalog_mappings_to_app_instance.rb +5 -0
- data/db/migrate/20110503003604_catalog_default.rb +5 -0
- data/db/migrate/20180301052853_add_catalog_attempted_at.rb +5 -0
- data/lib/resque/additions.rb +53 -0
- data/lib/resque/dynamic_queues.rb +131 -0
- data/lib/resque/self_lookup.rb +19 -0
- data/lib/tasks/zuora_connect_tasks.rake +24 -0
- data/lib/zuora_connect_oauth_alpha.rb +38 -0
- data/lib/zuora_connect_oauth_alpha/configuration.rb +40 -0
- data/lib/zuora_connect_oauth_alpha/controllers/helpers.rb +165 -0
- data/lib/zuora_connect_oauth_alpha/engine.rb +30 -0
- data/lib/zuora_connect_oauth_alpha/exceptions.rb +50 -0
- data/lib/zuora_connect_oauth_alpha/railtie.rb +35 -0
- data/lib/zuora_connect_oauth_alpha/version.rb +4 -0
- data/lib/zuora_connect_oauth_alpha/views/helpers.rb +9 -0
- data/test/controllers/zuora_connect/api/v1/app_instance_controller_test.rb +13 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/fixtures/zuora_connect/app_instances.yml +11 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/lib/generators/zuora_connect/datatable_generator_test.rb +16 -0
- data/test/models/zuora_connect/app_instance_test.rb +9 -0
- data/test/test_helper.rb +21 -0
- data/test/zuora_connect_test.rb +7 -0
- 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,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,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,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,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,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
|