module ZuoraConnect
class AppInstanceBase < ActiveRecord::Base
default_scope {select(ZuoraConnect::AppInstance.column_names.delete_if {|x| ["catalog_mapping", "catalog"].include?(x) }) }
after_initialize :init
self.table_name = "zuora_connect_app_instances"
attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version
def init
@options = Hash.new
@logins = Hash.new
@api_version = "v2"
self.attr_builder("timezone", ZuoraConnect.configuration.default_time_zone)
self.attr_builder("locale", ZuoraConnect.configuration.default_locale)
PaperTrail.whodunnit = "Backend" if defined?(PaperTrail)
Apartment::Tenant.switch!(self.id) if self.persisted?
if(ActiveRecord::Migrator.needs_migration?)
Apartment::Migrator.migrate(self.id)
end
Thread.current[:appinstance] = self
end
def data_lookup(session: {})
if defined?(PaperTrail)
PaperTrail.whodunnit = session["#{self.id}::user::email"].present? ? session["#{self.id}::user::email"] : nil if session.present?
end
if defined?(Redis.current)
cached_instance = Redis.current.get("AppInstance:#{self.id}")
if cached_instance.blank?
Rails.logger.info('Cached AppInstance Missing')
return {}
else
Rails.logger.info('Cached AppInstance Found')
return decrypt_data(data: cached_instance, rescue_return: {})
end
else
return session
end
end
def cache_app_instance
if defined?(Redis.current)
Redis.current.set("AppInstance:#{self.id}", encrypt_data(data: self.save_data))
Redis.current.expire("AppInstance:#{self.id}", 60.minutes.to_i)
Redis.current.del("Deleted:#{self.id}")
end
end
def decrypt_data(data: nil, rescue_return: nil)
return data if data.blank?
begin
if Rails.env == 'development'
return JSON.parse(data)
else
begin
return JSON.parse(encryptor.decrypt_and_verify(CGI::unescape(data)))
rescue ActiveSupport::MessageVerifier::InvalidSignature => ex
Rails.logger.fatal('Error Decrypting')
return rescue_return
end
end
rescue JSON::ParserError => ex
Rails.logger.fatal('Error Parsing')
return rescue_return
end
end
def encrypt_data(data: nil)
return data if data.blank?
if Rails.env == 'development'
return data.to_json
else
return encryptor.encrypt_and_sign(data.to_json)
end
end
def encryptor
# Default values for Rails 4 apps
key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
key_generator = ActiveSupport::KeyGenerator.new(Rails.application.secrets.secret_key_base, iterations: key_iter_num)
secret, sign_secret = [key_generator.generate_key(salt), key_generator.generate_key(signed_salt)]
return ActiveSupport::MessageEncryptor.new(secret, sign_secret)
end
def api_limit(start: true, time: 2.minutes.to_i)
if start
Redis.current.set("APILimits:#{self.id}", true)
Redis.current.expire("APILimits:#{self.id}", time)
else
Redis.current.del("APILimits:#{self.id}")
end
end
def api_limit?
return Redis.current.get("APILimits:#{self.id}").to_bool
end
def queue_paused?
return Redis.current.get("resque:PauseQueue:#{self.id}").to_bool
end
def queue_pause(time: nil)
if time.present?
raise "Time must be fixnum of seconds." if time.class != Fixnum
Redis.current.set("resque:PauseQueue:#{self.id}", true)
Redis.current.expire("resque:PauseQueue:#{self.id}", time)
else
Redis.current.set("resque:PauseQueue:#{self.id}", true)
end
end
def queue_start
Redis.current.del("resque:PauseQueue:#{self.id}")
end
def catalog_outdated?(time: Time.now - 12.hours)
return self.catalog_updated_at.blank? || (self.catalog_updated_at < time)
end
def catalog_loaded?
return ActiveRecord::Base.connection.execute('SELECT id FROM "public"."zuora_connect_app_instances" WHERE "id" = %s AND catalog = \'{}\' LIMIT 1' % [self.id]).first.nil?
end
# Catalog lookup provides method to lookup zuora catalog efficiently.
# entity_id: If the using catalog json be field to store multiple entity product catalogs.
# object: The Object class desired to be returned. Available [:product, :rateplan, :charge]
# object_id: The id or id's of the object/objects to be returned.
# child_objects: Whether to include child objects of the object in question.
# cache: Store individual "1" object lookup in redis for caching.
def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)
entity_reference = entity_id.blank? ? 'Default' : entity_id
if object_id.present? && ![Array, String].include?(object_id.class)
raise "Object Id can only be a string or an array of strings"
end
if defined?(Redis.current) && object_id.present? && object_id.class == String
stub_catalog = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}"))
object_hierarchy = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Hierarchy"))
end
if defined?(object_hierarchy)
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"})
end
case object
when :product
if object_id.blank?
string =
"SELECT "\
"json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\", "\
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
"WHERE "\
"\"id\" = %s" % [entity_reference, self.id]
else
if object_id.class == String
string =
"SELECT "\
"(catalog #> '{%s, %s}') #{child_objects ? '' : '- \'productRatePlans\''} AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\" "\
"WHERE "\
"\"id\" = %s" % [entity_reference, object_id, self.id]
elsif object_id.class == Array
string =
"SELECT "\
"json_object_agg(product_id, product #{child_objects ? '' : '- \'productRatePlans\''}) AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\", "\
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product) "\
"WHERE "\
"\"product_id\" IN (\'%s\') AND "\
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
end
end
when :rateplan
if object_id.blank?
string =
"SELECT "\
"json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\", "\
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
"WHERE "\
"\"id\" = %s" % [entity_reference, self.id]
else
if object_id.class == String
string =
"SELECT "\
"(catalog #> '{%s, %s, productRatePlans, %s}') #{child_objects ? '' : '- \'productRatePlanCharges\''} AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\" "\
"WHERE "\
"\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_id, self.id]
elsif object_id.class == Array
string =
"SELECT "\
"json_object_agg(rateplan_id, rateplan #{child_objects ? '' : '- \'productRatePlanCharges\''}) AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\", "\
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan) "\
"WHERE "\
"\"rateplan_id\" IN (\'%s\') AND "\
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
end
end
when :charge
if object_id.blank?
string =
"SELECT "\
"json_object_agg(charge_id, charge) as item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\", "\
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
"jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
"WHERE "\
"\"id\" = %s" % [entity_reference, self.id]
else
if object_id.class == String
string =
"SELECT "\
"catalog #> '{%s, %s, productRatePlans, %s, productRatePlanCharges, %s}' AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\" "\
"WHERE "\
"\"id\" = %s" % [entity_reference, object_hierarchy['productId'], object_hierarchy['productRatePlanId'], object_id, self.id]
elsif object_id.class == Array
string =
"SELECT "\
"json_object_agg(charge_id, charge) AS item "\
"FROM "\
"\"public\".\"zuora_connect_app_instances\", "\
"jsonb_each((\"public\".\"zuora_connect_app_instances\".\"catalog\" #> '{%s}' )) AS e(product_id, product), "\
"jsonb_each(product #> '{productRatePlans}') AS ee(rateplan_id, rateplan), "\
"jsonb_each(rateplan #> '{productRatePlanCharges}') AS eee(charge_id, charge) "\
"WHERE "\
"\"charge_id\" IN (\'%s\') AND "\
"\"id\" = %s" % [entity_reference, object_id.join("\',\'"), self.id]
end
end
else
raise "Available objects include [:product, :rateplan, :charge]"
end
stub_catalog ||= JSON.parse(ActiveRecord::Base.connection.execute(string).first["item"] || "{}")
if defined?(Redis.current) && object_id.present? && object_id.class == String
Redis.current.set("Catalog:#{self.id}:#{object_id}:Hierarchy", encrypt_data(data: object_hierarchy))
Redis.current.set("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}", encrypt_data(data: stub_catalog)) if cache
end
return stub_catalog
end
def instance_failure(failure)
raise failure
end
def login_lookup(type: "Zuora")
results = []
self.logins.each do |name, login|
results << login if login.tenant_type == type
end
return results
end
def get_catalog(page_size: 5, zuora_login: self.login_lookup(type: "Zuora").first, entity_id: nil)
entity_reference = entity_id.blank? ? 'Default' : entity_id
Rails.logger.info("Fetch Catalog")
Rails.logger.info("Zuora Entity: #{entity_id.blank? ? 'default' : entity_id}")
login = zuora_login.client(entity_reference)
old_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
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})
response = {'nextPage' => login.rest_endpoint("catalog/products?pageSize=#{page_size}")}
while !response["nextPage"].blank?
url = login.rest_endpoint(response["nextPage"].split('/v1/').last)
Rails.logger.debug("Fetch Catalog URL #{url}")
output_json, response = login.rest_call(:debug => false, :url => url, :errors => [ZuoraAPI::Exceptions::ZuoraAPISessionError], :timeout_retry => true)
Rails.logger.debug("Fetch Catalog Response Code #{response.code}")
if !output_json['success'] =~ (/(true|t|yes|y|1)$/i) || output_json['success'].class != TrueClass
Rails.logger.error("Fetch Catalog DATA #{output_json.to_json}")
raise ZuoraAPI::Exceptions::ZuoraAPIError.new("Error Getting Catalog: #{output_json}")
end
output_json["products"].each do |product|
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])
rateplans = {}
product["productRatePlans"].each do |rateplan|
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])
charges = {}
rateplan["productRatePlanCharges"].each do |charge|
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])
charges[charge["id"]] = charge.merge({"productId" => product["id"], "productName" => product["name"], "productRatePlanId" => rateplan["id"], "productRatePlanName" => rateplan["name"] })
end
rateplan["productRatePlanCharges"] = charges
rateplans[rateplan["id"]] = rateplan.merge({"productId" => product["id"], "productName" => product["name"]})
end
product["productRatePlans"] = rateplans
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])
end
end
# Move from tmp to actual
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})
if defined?(Redis.current)
Redis.current.keys("Catalog:#{self.id}:*").each do |key|
Redis.current.del(key.to_s)
end
end
# Clear tmp holder
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})
ActiveRecord::Base.logger = old_logger
self.update_column(:catalog_updated_at, Time.now.utc)
self.touch
# DO NOT RETURN CATALOG. THIS IS NOT SCALABLE WITH LARGE CATALOGS. USE THE CATALOG_LOOKUP method provided
return true
end
def new_session(session: self.data_lookup, username: self.access_token, password: self.refresh_token)
@api_version = "v2"
@username = username
@password = password
@last_refresh = session["#{self.id}::last_refresh"]
## DEV MODE TASK DATA MOCKUP
if ZuoraConnect.configuration.mode != "Production"
mock_task_data = {
"options": ZuoraConnect.configuration.dev_mode_options,
"mode": ZuoraConnect.configuration.dev_mode_mode
}
ZuoraConnect.configuration.dev_mode_logins.each do |k,v|
v = v.merge({"entities": [] }) if !v.keys.include?("entities")
mock_task_data[k] = v
end
build_task(mock_task_data, session)
else
if session.nil? || (!session.nil? && self.id != session["appInstance"].to_i) || session["#{self.id}::task_data"].blank? || ( session["#{self.id}::last_refresh"].blank? || session["#{self.id}::last_refresh"].to_i < ZuoraConnect.configuration.timeout.ago.to_i )
Rails.logger.info("[#{self.id}] REFRESHING - Session Nil") if session.nil?
Rails.logger.info("[#{self.id}] REFRESHING - AppInstance ID(#{self.id}) does not match session id(#{session["appInstance"].to_i})") if (!session.nil? && self.id != session["appInstance"].to_i)
Rails.logger.info("[#{self.id}] REFRESHING - Task Data Blank") if session["#{self.id}::task_data"].blank?
Rails.logger.info("[#{self.id}] REFRESHING - No Time on Cookie") if session["#{self.id}::last_refresh"].blank?
Rails.logger.info("[#{self.id}] REFRESHING - Session Old") if (session["#{self.id}::last_refresh"].blank? || session["#{self.id}::last_refresh"].to_i < ZuoraConnect.configuration.timeout.ago.to_i )
self.refresh(session)
else
Rails.logger.info("[#{self.id}] REBUILDING")
build_task(session["#{self.id}::task_data"], session)
end
end
begin
I18n.locale = self.locale
rescue I18n::InvalidLocale => ex
Rails.logger.error("Invalid Locale: #{ex.message}")
end
Time.zone = self.timezone
Thread.current[:appinstance] = self
return self
end
def save_data(session = Hash.new)
self.logins.each do |key, login|
if login.tenant_type == "Zuora"
if login.available_entities.size > 1 && Rails.application.config.session_store != ActionDispatch::Session::CookieStore
login.available_entities.each do |entity_key|
session["#{self.id}::#{key}::#{entity_key}:session"] = login.client(entity_key).current_session
end
else
session["#{self.id}::#{key}:session"] = login.client.current_session
end
end
end
session["#{self.id}::task_data"] = self.task_data
session["#{self.id}::last_refresh"] = self.last_refresh
session["appInstance"] = self.id
return session
end
def updateOption(optionId, value)
if self.access_token && self.refresh_token
#Refresh token if already expired
self.refresh_oauth if self.oauth_expired?
return HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/application_options/#{optionId}/edit?value=#{value}",:body => {:access_token => self.username})
else
return false
end
end
#Example
#{"name": "ftp_login_14","username": "ftplogin7","tenant_type": "Custom","password": "test2","url": "www.ftp.com","custom_data": { "path": "/var/usr/test"}}
#This can update an existing login
#This can add a new login
#This can change to another existing login
def update_logins(options)
#Refresh token if already expired
self.refresh_oauth if self.oauth_expired?
count ||= 0
response = HTTParty.post(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}/logins",:body => {:access_token => self.username}.merge(options))
if response.code == 200
return JSON.parse(response.body)
else
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
end
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError
if (count += 1) < 3
retry
else
raise
end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
if (count += 1) < 3
if ex.code == 401
self.refresh_oauth
end
retry
else
raise
end
end
def refresh(session = nil)
#Refresh token if already expired
self.refresh_oauth if self.oauth_expired?
count ||= 0
start = Time.now
Rails.logger.info("[#{self.id}] REFRESHING - Get Task Info")
response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
response_time = Time.now - start
Rails.logger.info("[#{self.id}] REFRESHING - Connect Request Time #{response_time.round(2).to_s}")
if response.code == 200
@last_refresh = Time.now.to_i
build_task(JSON.parse(response.body), session)
else
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
end
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError
if (count += 1) < 3
retry
else
raise
end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
if (count += 1) < 3
if ex.code == 401
self.refresh_oauth
end
retry
else
raise
end
end
def build_task(task_data, session)
@task_data = task_data
@mode = @task_data["mode"]
@task_data.each do |k,v|
if k.match(/^(.*)_login$/)
tmp = ZuoraConnect::Login.new(v)
if !session.nil? && v["tenant_type"] == "Zuora"
if tmp.entities.size > 0
tmp.entities.each do |value|
entity_id = value["id"]
tmp.client(entity_id).current_session = session["#{self.id}::#{k}::#{entity_id}:session"] if !session.nil? && v["tenant_type"] == "Zuora" && session["#{self.id}::#{k}::#{entity_id}:session"]
end
else
tmp.client.current_session = session["#{self.id}::#{k}:session"] if !session.nil? && v["tenant_type"] == "Zuora" && session["#{self.id}::#{k}:session"]
end
end
@logins[k] = tmp
self.attr_builder(k, @logins[k])
elsif k == "options"
v.each do |opt|
@options[opt["config_name"]] = opt
end
elsif k == "user_settings"
self.timezone = v["timezone"]
self.locale = v["local"]
end
end
Thread.current[:appinstance] = self
end
def send_email
end
def upload_to_s3(local_file,s3_path = nil)
s3_path = local_file.split("/").last if s3_path.nil?
obj = self.s3_client.bucket(ZuoraConnect.configuration.s3_bucket_name).object("#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{s3_path}}")
obj.upload_file(local_file, :server_side_encryption => 'AES256')
end
def get_s3_file_url(key)
require 'aws-sdk-s3'
signer = Aws::S3::Presigner.new(client: self.s3_client)
url = signer.presigned_url(:get_object, bucket: ZuoraConnect.configuration.s3_bucket_name, key: "#{ZuoraConnect.configuration.s3_folder_name}/#{self.id.to_s}/#{key}")
end
def s3_client
require 'aws-sdk-s3'
if ZuoraConnect.configuration.mode == "Development"
@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)
else
@s3_client ||= Aws::S3::Resource.new(region: ZuoraConnect.configuration.aws_region)
end
end
def self.decrypt_response(resp)
OpenSSL::PKey::RSA.new(ZuoraConnect.configuration.private_key).private_decrypt(resp)
end
def refresh_oauth
count ||= 0
start = Time.now
Rails.logger.info("[#{self.id}] REFRESHING - OAuth")
params = {
:grant_type => "refresh_token",
:redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
:refresh_token => self.refresh_token
}
response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token",:body => params)
response_time = Time.now - start
Rails.logger.info("[#{self.id}] REFRESHING - OAuth in #{response_time.round(2).to_s}")
if response.code == 200
response_body = JSON.parse(response.body)
self.refresh_token = response_body["refresh_token"]
self.access_token = response_body["access_token"]
self.oauth_expires_at = Time.at(response_body["created_at"].to_i) + response_body["expires_in"].seconds
self.save(:validate => false)
else
Rails.logger.fatal("REFRESHING - OAuth Failed - Code #{response.code}")
raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Refreshing Access Token", response.body, response.code)
end
rescue Net::ReadTimeout, Net::OpenTimeout, Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNREFUSED, SocketError => ex
if (count += 1) < 3
retry
else
raise
end
rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
if (count += 1) < 3
Rails.logger.info("REFRESHING - OAuth Failed - Retrying(#{count})")
self.reload
sleep(5)
retry
else
Rails.logger.fatal("REFRESHING - OAuth Failed")
raise
end
end
def oauth_expired?
(self.oauth_expires_at < Time.now)
end
def attr_builder(field,val)
singleton_class.class_eval { attr_accessor "#{field}" }
send("#{field}=", val)
end
def method_missing(method_sym, *arguments, &block)
if method_sym.to_s.include?("login")
Rails.logger.fatal("Method Missing #{method_sym}")
Rails.logger.fatal("Instance Data: #{self.task_data}")
Rails.logger.fatal("Instance Logins: #{self.logins}")
end
super
end
def self.update_functions
ActiveRecord::Base.connection.execute(File.read("#{Gem.loaded_specs["zuora_connect"].gem_dir}/app/views/sql/refresh_aggregate_table.txt"))
end
def self.refresh_aggregate_table(aggregate_name: 'all_tasks_processing', table_name: 'tasks', where_clause: "where status in ('Processing', 'Queued')", index_table: true)
self.update_functions
#Broke function into two parts to ensure transaction size was small enough
ActiveRecord::Base.connection.execute('SELECT "shared_extensions".refresh_aggregate_table(\'%s\', \'%s\', %s, \'Table\');' % [aggregate_name, table_name, ActiveRecord::Base.connection.quote(where_clause)])
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
end
end
end