zuora_connect 2.1.1 → 3.0.0k

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0342c5ac7396b4e8ba75aff8dbab5d579fa6d0ad04a421b7d1d809ab3abbd90a
4
- data.tar.gz: 70f7277a93fec8e481f63e56f85e466b5e0b4d1307e2413e4f8da9497e980be2
3
+ metadata.gz: bad84d0fc35d26f517f83b2cc38e5d1569dafc4253c6c12a474f69aa9d9fee5e
4
+ data.tar.gz: 0ad1de0856425104abb6288662ca1481b097dfb3af696c40d1e8d5a1d018a0af
5
5
  SHA512:
6
- metadata.gz: eb6497a343dde2b4a3edfe470c8e5a44eb37ff99b75ed434138c280e8ac25bf36806d58fbb477772182596917f1c37c0e237bd55e88f646e149743477fba2f15
7
- data.tar.gz: f0b1f8b6f647f204ce8c4bfed7a8e77b49f3aada0faf6a0c31fc47675f5bfca67feb464692006bcb490977a577d1d5a3feab53886815f4c6472023e9ade6823b
6
+ metadata.gz: c00dcd65d70b84242e2c298f1d663d98259083c53e4232cf00c477b32fb32d480a49a39bf982a5682807ee0ae828905cd6dfeb1f548bf6e1a501dd4aca58c63e
7
+ data.tar.gz: 91fe7a9815f2340879cc0a30ef405b48960d44b54f1a020f193c5f029448b19ddb513944ce424fde25764cc0b5bf9a25ae25f3b70d124d715f69e4304ebf36d3
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
15
15
  rdoc.rdoc_files.include('lib/**/*.rb')
16
16
  end
17
17
 
18
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
18
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
19
19
  load 'rails/tasks/engine.rake'
20
20
 
21
21
 
@@ -1,23 +1,20 @@
1
1
  module ZuoraConnect
2
2
  class StaticController < ApplicationController
3
- before_action :authenticate_connect_app_request, :except => [:metrics, :health, :initialize_app]
4
- before_action :clear_connect_app_session, :only => [:metrics, :health, :initialize_app]
5
- after_action :persist_connect_app_session, :except => [:metrics, :health, :initialize_app]
6
-
7
- skip_before_action :verify_authenticity_token, :only => [:initialize_app]
8
-
9
- def metrics
10
- type = params[:type].present? ? params[:type] : "versions"
11
- render json: ZuoraConnect::AppInstance.get_metrics(type).to_json, status: 200
12
- end
3
+ before_action :authenticate_connect_app_request, :except => [:health, :initialize_app, :provision, :instance_user]
4
+ before_action :clear_connect_app_session, :only => [:health, :initialize_app, :provision, :instance_user]
5
+ after_action :persist_connect_app_session, :except => [:health, :initialize_app, :provision, :instance_user]
6
+
7
+ skip_before_action :verify_authenticity_token, :only => [:initialize_app, :provision]
8
+ http_basic_authenticate_with name: ENV['PROVISION_USER'], password: ENV['PROVISION_SECRET'], :only => [:provision, :instance_user]
9
+
13
10
 
14
11
  def health
15
12
  if params[:error].present?
16
- begin
13
+ begin
17
14
  raise ZuoraConnect::Exceptions::Error.new('This is an error')
18
15
  rescue => ex
19
16
  case params[:error]
20
- when 'Log'
17
+ when 'Log'
21
18
  Rails.logger.error("Error in Health", ex)
22
19
  when 'Exception'
23
20
  raise
@@ -34,11 +31,13 @@ module ZuoraConnect
34
31
  def initialize_app
35
32
  begin
36
33
  authenticate_connect_app_request
37
- @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
38
- render json: {
39
- message: "Success",
40
- status: 200
41
- }, status: 200
34
+ unless performed?
35
+ @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
36
+ render json: {
37
+ message: 'Success',
38
+ status: 200
39
+ }, status: 200
40
+ end
42
41
  rescue => ex
43
42
  Rails.logger.error("Failed to Initialize application", ex)
44
43
  if performed?
@@ -52,6 +51,74 @@ module ZuoraConnect
52
51
  end
53
52
  end
54
53
 
54
+ def provision
55
+ create_new_instance
56
+ unless performed?
57
+ render json: {
58
+ status: 200,
59
+ message: 'Success',
60
+ app_instance_id: @appinstance.id
61
+ }, status: 200
62
+ end
63
+ rescue StandardError => e
64
+ message = 'Failed to provision new instance'
65
+ if performed?
66
+ Rails.logger.error("#{message}: #{performed?}", e)
67
+ else
68
+ Rails.logger.error(message, e)
69
+ render json: {
70
+ status: 500,
71
+ message: message
72
+ }, status: 500
73
+ end
74
+ end
75
+
76
+ def instance_user
77
+ ZuoraConnect::AppInstance.read_master_db do
78
+ ZuoraConnect.logger.with_fields = {} if ZuoraConnect.logger.is_a?(Ougai::Logger)
79
+ Rails.logger.with_fields = {} if Rails.logger.is_a?(Ougai::Logger)
80
+
81
+ if defined?(ElasticAPM) && ElasticAPM.running? && ElasticAPM.respond_to?(:set_label)
82
+ ElasticAPM.set_label(:trace_id, request.uuid)
83
+ end
84
+
85
+ unless params[:id].present?
86
+ render json: {
87
+ status: 400,
88
+ message: 'No app instance id provided'
89
+ }, status: :bad_request
90
+ return
91
+ end
92
+
93
+ @appinstance = ZuoraConnect::AppInstance.find(params[:id]).new_session
94
+ end
95
+
96
+ zuora_client = @appinstance.send(ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION).client
97
+ client_describe, = zuora_client.rest_call(
98
+ url: zuora_client.rest_endpoint('genesis/user/info').gsub('v1/', ''),
99
+ session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic
100
+ )
101
+
102
+ render json: {
103
+ status: 200,
104
+ message: 'Success',
105
+ user_id: client_describe['coreUserId'],
106
+ username: client_describe['username'],
107
+ email: client_describe['workEmail']
108
+ }, status: 200
109
+ rescue ActiveRecord::RecordNotFound
110
+ render json: {
111
+ status: 400,
112
+ message: 'No app instance found'
113
+ }, status: :bad_request
114
+ rescue StandardError => e
115
+ Rails.logger.error('Error occurred getting user details', e)
116
+ render json: {
117
+ status: 500,
118
+ message: 'Failed to get user details'
119
+ }, status: 500
120
+ end
121
+
55
122
  private
56
123
 
57
124
  def clear_connect_app_session
@@ -11,8 +11,7 @@ module ZuoraConnect
11
11
  before_destroy :prune_data
12
12
 
13
13
  self.table_name = "zuora_connect_app_instances"
14
- attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version, :drop_message, :new_session_message, :connect_user, :logitems
15
- @@telegraf_host = nil
14
+ attr_accessor :options, :mode, :logins, :task_data, :last_refresh, :username, :password, :s3_client, :api_version, :drop_message, :new_session_message, :connect_user, :logitems, :user_timezone
16
15
  REFRESH_TIMEOUT = 2.minute #Used to determine how long to wait on current refresh call before executing another
17
16
  INSTANCE_REFRESH_WINDOW = 1.hours #Used to set how how long till app starts attempting to refresh cached task connect data
18
17
  INSTANCE_REDIS_CACHE_PERIOD = 24.hours #Used to determine how long to cached task data will live for
@@ -21,6 +20,12 @@ module ZuoraConnect
21
20
  HOLDING_PATTERN_SLEEP = 5.seconds
22
21
  CONNECT_APPLICATION_ID = 0
23
22
  CONNECT_COMMUNICATION_SLEEP = Rails.env.test? ? 0.seconds : 5.seconds
23
+ CATALOG_LOOKUP_PAGE_SIZE = 10_000
24
+ CATALOG_LOOKUP_CACHE_TIME_KEY = 'CatalogCachedAt'
25
+ CATALOG_LOOKUP_TTL = 60.seconds
26
+ CATALOG_LOOKUP_CACHE_RESULT_KEY = 'CatalogCache'
27
+ TIMEZONE_LOG_RATE_LIMIT_KEY = 'TimezoneLoggedAt'
28
+ TIMEZONE_LOG_PERIOD = 4.hours
24
29
  IGNORED_LOCALS = ['fr', 'ja', 'es', 'zh', 'de']
25
30
  INTERNAL_HOSTS = []
26
31
  LOGIN_TENANT_DESTINATION = 'target_login'
@@ -29,7 +34,7 @@ module ZuoraConnect
29
34
  Aws::Errors::MissingCredentialsError,
30
35
  Aws::S3::Errors::AccessDenied,
31
36
  Aws::SES::Errors::AccessDenied,
32
- Aws::KMS::Errors::AccessDeniedException
37
+ Aws::KMS::Errors::AccessDeniedException
33
38
  ].freeze
34
39
  AWS_AUTH_ERRORS_MSG = "AWS Auth Errors".freeze
35
40
 
@@ -147,7 +152,8 @@ module ZuoraConnect
147
152
  if ZuoraConnect.configuration.mode != "Production"
148
153
  mock_task_data = {
149
154
  "id" => ZuoraConnect.configuration.dev_mode_appinstance,
150
- "mode" => ZuoraConnect.configuration.dev_mode_mode
155
+ "mode" => ZuoraConnect.configuration.dev_mode_mode,
156
+ "name" => "Developer Instance"
151
157
  }
152
158
 
153
159
  case ZuoraConnect.configuration.dev_mode_options.class
@@ -163,6 +169,7 @@ module ZuoraConnect
163
169
  end
164
170
 
165
171
  self.build_task(task_data: mock_task_data, session: session)
172
+ self.set_backup_creds if !self['zuora_logins'].present?
166
173
  self.last_refresh = Time.now.to_i
167
174
  else
168
175
  time_expire = (session["#{self.id}::last_refresh"] || Time.now).to_i - INSTANCE_REFRESH_WINDOW.ago.to_i
@@ -240,7 +247,9 @@ module ZuoraConnect
240
247
  rescue I18n::InvalidLocale => ex
241
248
  ZuoraConnect.logger.error(ex) if !IGNORED_LOCALS.include?(ex.locale.to_s.downcase)
242
249
  end
243
- Time.zone = self.timezone
250
+
251
+ self.set_timezone
252
+
244
253
  if self.task_data.present?
245
254
  tenants = self.task_data.fetch('tenant_ids', [])
246
255
  organizations = self.task_data.fetch('organizations', [])
@@ -255,7 +264,7 @@ module ZuoraConnect
255
264
  end
256
265
 
257
266
  params = {
258
- name: self.task_data.dig('name'),
267
+ name: self.task_data.dig('name'),
259
268
  zuora_entity_ids: (self.task_data.dig(LOGIN_TENANT_DESTINATION,'entities') || []).map{|e| e['id']}.uniq,
260
269
  zuora_tenant_ids: tenants.map(&:to_s).uniq,
261
270
  organizations: organizations
@@ -264,7 +273,7 @@ module ZuoraConnect
264
273
  client = self.send(LOGIN_TENANT_DESTINATION).client
265
274
  if defined?(client.rest_domain)
266
275
  ZuoraConnect::RequestIdMiddleware.zuora_rest_domain = client.rest_domain
267
- params.merge!({zuora_domain: client.rest_domain, environment: client.environment })
276
+ params.merge!({zuora_domain: client.rest_domain, environment: client.environment })
268
277
  end
269
278
  end
270
279
  params = params.reject{|k,v| !self.attributes.keys.member?(k.to_s) || self[k] == v}
@@ -272,12 +281,77 @@ module ZuoraConnect
272
281
  end
273
282
  end
274
283
 
275
- def refresh(session: {})
284
+ def set_timezone(timezone: self.timezone, type: :default)
285
+ if timezone.blank?
286
+ timezone = self.timezone
287
+ end
288
+
289
+ if type == :default
290
+ Time.zone = timezone
291
+ elsif type == :user
292
+ begin
293
+ sql = <<-eos
294
+ SELECT zuora_users.zuora_identity_response FROM "#{self.id}".zuora_users ORDER BY zuora_users.updated_at DESC LIMIT 1;
295
+ eos
296
+ user = ActiveRecord::Base.connection.execute(sql).to_a.first
297
+
298
+ if user.present?
299
+ zuora_identity_response = JSON.parse(user.fetch('zuora_identity_response', '{}'))
300
+ self.user_timezone = zuora_identity_response.values.first&.dig('timeZone')
301
+ else
302
+ if (Redis.current.hget(TIMEZONE_LOG_RATE_LIMIT_KEY, self.id).to_i + TIMEZONE_LOG_PERIOD.to_i) <= Time.now.to_i
303
+ Rails.logger.error('Cannot find any user to set the timezone', app_instance_id: self.id)
304
+ Redis.current.hset(TIMEZONE_LOG_RATE_LIMIT_KEY, self.id, Time.now.to_i)
305
+ end
306
+ end
307
+ rescue => ex
308
+ Rails.logger.error('There is an error while getting timezone users', ex)
309
+ end
310
+
311
+ if self.user_timezone.present?
312
+ # connect instance which has a custom timezone
313
+ if !self.auto_deployed? && (
314
+ ActiveSupport::TimeZone[self.task_data.dig('user_settings', 'timezone') || '']&.utc_offset !=
315
+ ActiveSupport::TimeZone[self.user_timezone]&.utc_offset
316
+ )
317
+ if self.environment == 'Production' &&
318
+ (Redis.current.hget(TIMEZONE_LOG_RATE_LIMIT_KEY, self.id).to_i + TIMEZONE_LOG_PERIOD.to_i) <= Time.now.to_i
319
+ ZuoraConnect.logger.error(
320
+ "Instance and user timezones are different. User has '#{self.user_timezone}' and " \
321
+ "instance has '#{self.task_data.dig('user_settings', 'timezone')}'",
322
+ app_instance_id: self.id
323
+ )
324
+ Redis.current.hset(TIMEZONE_LOG_RATE_LIMIT_KEY, self.id, Time.now.to_i)
325
+ end
326
+ self.user_timezone = nil
327
+ Time.zone = timezone
328
+ else
329
+ begin
330
+ Time.zone = self.user_timezone
331
+ rescue ArgumentError
332
+ Rails.logger.error('Malformed user timezone', app_instance_id: self.id)
333
+ Time.zone = timezone
334
+ end
335
+ end
336
+ else
337
+ Time.zone = timezone
338
+ end
339
+ end
340
+ rescue => e
341
+ Rails.logger.error('Malformed timezone used', e, app_instance_id: self.id)
342
+ Time.zone = self.timezone
343
+ end
344
+
345
+ def auto_deployed?
346
+ self.id >= 25000000
347
+ end
348
+
349
+ def refresh(session: {})
276
350
  refresh_count ||= 0
277
351
  skip_connect ||= false
278
352
  begin
279
353
  #Check how app was deployed
280
- if self.id < 25000000 && !skip_connect
354
+ if !self.auto_deployed? && !skip_connect
281
355
  self.check_oauth_state
282
356
  response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
283
357
 
@@ -289,16 +363,8 @@ module ZuoraConnect
289
363
  end
290
364
 
291
365
  self.build_task(task_data: parsed_json, session: session)
292
- if self.kms_key.present? && self.kms_key.match(/^arn:aws:.*/)
293
- begin
294
- self.zuora_logins = self.strip_cache_data(object: parsed_json.dup, keys: ['applications', 'tokens', 'user_settings'])
295
- self.save(:validate => false)
296
- rescue Aws::KMS::Errors::ValidationException, *AWS_AUTH_ERRORS => ex
297
- Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
298
- rescue => ex
299
- Rails.logger.error(AWS_AUTH_ERRORS_MSG, ex)
300
- end
301
- end
366
+ self.set_backup_creds
367
+ self.save(validate: false) if self.changed?
302
368
  else
303
369
  raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
304
370
  end
@@ -338,9 +404,21 @@ module ZuoraConnect
338
404
  raise
339
405
  end
340
406
 
407
+ def aws_secrets
408
+ (Rails.application.secrets.aws || {}).transform_keys { |key| key.to_s }
409
+ end
410
+
341
411
  #### START KMS ENCRYPTION Methods ####
412
+ def set_backup_creds
413
+ if self.kms_key.present? && self.kms_key.match(/^arn:aws:.*/) && self.task_data.present?
414
+ self.zuora_logins = self.strip_cache_data(object: self.task_data.dup, keys: ['applications', 'tokens', 'user_settings'])
415
+ end
416
+ end
417
+
342
418
  def zuora_logins=(val)
343
419
  write_attribute(:zuora_logins, kms_encrypt(val.to_json))
420
+ rescue Aws::KMS::Errors::ValidationException, Aws::KMS::Errors::NotFoundException, *AWS_AUTH_ERRORS => ex
421
+ Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
344
422
  end
345
423
 
346
424
  def zuora_logins
@@ -350,7 +428,7 @@ module ZuoraConnect
350
428
 
351
429
  def kms_decrypt(value)
352
430
  kms_tries ||= 0
353
- kms_client = Aws::KMS::Client.new({region: Rails.application.secrets.aws['AWS_REGION'], credentials: self.aws_auth_client}.delete_if { |k, v| v.blank? })
431
+ kms_client = Aws::KMS::Client.new({region: aws_secrets['AWS_REGION'], credentials: self.aws_auth_client}.delete_if { |k, v| v.blank? })
354
432
  resp = kms_client.decrypt({ciphertext_blob: [value].pack("H*") })
355
433
  return resp.plaintext
356
434
  rescue *AWS_AUTH_ERRORS => ex
@@ -365,7 +443,7 @@ module ZuoraConnect
365
443
 
366
444
  def kms_encrypt(value)
367
445
  kms_tries ||= 0
368
- kms_client = Aws::KMS::Client.new({region: Rails.application.secrets.aws['AWS_REGION'], credentials: self.aws_auth_client}.delete_if {|k,v| v.blank? })
446
+ kms_client = Aws::KMS::Client.new({region: aws_secrets['AWS_REGION'], credentials: self.aws_auth_client}.delete_if {|k,v| v.blank? })
369
447
 
370
448
  resp = kms_client.encrypt({key_id: kms_key, plaintext: value})
371
449
  return resp.ciphertext_blob.unpack('H*').first
@@ -380,12 +458,12 @@ module ZuoraConnect
380
458
  end
381
459
 
382
460
  def kms_key
383
- return ENV['AWS_KMS_ARN'] || Rails.application.secrets.dig(:aws,'AWS_KMS_ARN')
461
+ return ENV['AWS_KMS_ARN'] || aws_secrets['AWS_KMS_ARN']
384
462
  end
385
463
 
386
464
  def aws_auth_client
387
465
  if Rails.env.to_s == 'development'
388
- return Aws::Credentials.new(Rails.application.secrets.aws['AWS_ACCESS_KEY_ID'], Rails.application.secrets.aws['AWS_SECRET_ACCESS_KEY'])
466
+ return Aws::Credentials.new(aws_secrets['AWS_ACCESS_KEY_ID'], aws_secrets['AWS_SECRET_ACCESS_KEY'])
389
467
  else
390
468
  return nil
391
469
  end
@@ -400,82 +478,6 @@ module ZuoraConnect
400
478
  end
401
479
  Thread.current[:appinstance] = self
402
480
  end
403
-
404
- def self.write_to_telegraf(*args)
405
- if ZuoraConnect.configuration.enable_metrics
406
- @@telegraf_host = ZuoraConnect::Telegraf.new() if @@telegraf_host == nil
407
- unicorn_stats = self.unicorn_listener_stats() if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
408
- @@telegraf_host.write(direction: 'Raindrops', tags: {}, values: unicorn_stats) unless unicorn_stats.blank?
409
- return @@telegraf_host.write(*args)
410
- end
411
- end
412
-
413
- def self.unicorn_listener_stats ()
414
- stats_hash = {}
415
- stats_hash["total_active"] = 0
416
- stats_hash["total_queued"] = 0
417
-
418
- begin
419
- tmp = Unicorn.listener_names
420
- unix = tmp.grep(%r{\A/})
421
- tcp = tmp.grep(/\A.+:\d+\z/)
422
- tcp = nil if tcp.empty?
423
- unix = nil if unix.empty?
424
-
425
-
426
- Raindrops::Linux.tcp_listener_stats(tcp).each do |addr,stats|
427
- stats_hash["active_#{addr}"] = stats.active
428
- stats_hash["queued_#{addr}"] = stats.queued
429
- stats_hash["total_active"] = stats.active + stats_hash["total_active"]
430
- stats_hash["total_queued"] = stats.queued + stats_hash["total_queued"]
431
- end if tcp
432
-
433
- Raindrops::Linux.unix_listener_stats(unix).each do |addr,stats|
434
- stats_hash["active_#{addr}"] = stats.active
435
- stats_hash["queued_#{addr}"] = stats.queued
436
- stats_hash["total_active"] = stats.active + stats_hash["total_active"]
437
- stats_hash["total_queued"] = stats.queued + stats_hash["total_queued"]
438
- end if unix
439
- rescue IOError => ex
440
- rescue => ex
441
- ZuoraConnect.logger.error(ex)
442
- end
443
- return stats_hash
444
- end
445
-
446
- def self.get_metrics(type)
447
- @data = {}
448
-
449
- if type == "versions"
450
- @data = {
451
- app_name: ZuoraConnect::Telegraf.app_name,
452
- url: "dummy",
453
- Version_Gem: ZuoraConnect::VERSION,
454
- Version_Zuora: ZuoraAPI::VERSION ,
455
- Version_Ruby: RUBY_VERSION,
456
- Version_Rails: Rails.version,
457
- hold: 1
458
- }
459
- elsif type == "stats"
460
- begin
461
- Resque.redis.ping
462
- @resque = Resque.info
463
- @data = {
464
- app_name: ZuoraConnect::Telegraf.app_name,
465
- url: "dummy",
466
- Resque:{
467
- Jobs_Finished: @resque[:processed] ,
468
- Jobs_Failed: @resque[:failed],
469
- Jobs_Pending: @resque[:pending],
470
- Workers_Active: @resque[:working],
471
- Workers_Total: @resque[:workers]
472
- }
473
- }
474
- rescue
475
- end
476
- end
477
- return @data
478
- end
479
481
  #### END Task Methods ####
480
482
 
481
483
  #### START Task Methods ####
@@ -518,7 +520,7 @@ module ZuoraConnect
518
520
  end
519
521
  rescue ZuoraConnect::Exceptions::MissMatch => ex
520
522
  raise
521
- rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex
523
+ rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex
522
524
  raise
523
525
  rescue => ex
524
526
  ZuoraConnect.logger.error("Build Task Error", ex)
@@ -574,12 +576,12 @@ module ZuoraConnect
574
576
  self.refresh if !defined?(self.target_login)
575
577
 
576
578
  response = HTTParty.get("#{ZuoraConnect.configuration.url}/api/#{self.api_version}/tenants/search?hostname=#{self.target_login.client.hostname}&node_id=#{self.zuora_entity_ids.first}")
577
-
579
+
578
580
  if response.success?
579
581
  parsed_json = JSON.parse(response.body)
580
-
582
+
581
583
  #Set Org
582
- if self.id >= 25000000 && parsed_json['organization'].present?
584
+ if self.auto_deployed? && parsed_json['organization'].present?
583
585
  login_cache = self.zuora_logins
584
586
  login_cache.delete('organization')
585
587
  self.zuora_logins = login_cache.merge({'organizations' => [parsed_json['organization']]})
@@ -599,7 +601,7 @@ module ZuoraConnect
599
601
  end
600
602
  end
601
603
  self.save(:validate => false)
602
-
604
+
603
605
  return parsed_json
604
606
  end
605
607
  rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
@@ -653,7 +655,7 @@ module ZuoraConnect
653
655
  end
654
656
 
655
657
  def refresh_oauth
656
- refresh_oauth_count ||= 0
658
+ refresh_oauth_count ||= 0
657
659
  response = HTTParty.post("#{ZuoraConnect.configuration.url}/oauth/token", body: {
658
660
  :grant_type => "refresh_token",
659
661
  :redirect_uri => ZuoraConnect.configuration.oauth_client_redirect_uri,
@@ -955,13 +957,81 @@ module ZuoraConnect
955
957
  # object_id: The id or id's of the object/objects to be returned.
956
958
  # child_objects: Whether to include child objects of the object in question.
957
959
  # cache: Store individual "1" object lookup in redis for caching.
958
- def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false)
960
+ def catalog_lookup(entity_id: nil, object: :product, object_id: nil, child_objects: false, cache: false, source: 'DB')
959
961
  entity_reference = entity_id.blank? ? 'Default' : entity_id
960
962
 
961
963
  if object_id.present? && ![Array, String].include?(object_id.class)
962
964
  raise "Object Id can only be a string or an array of strings"
963
965
  end
964
966
 
967
+ if source == 'API'
968
+ catalog_container = {}
969
+
970
+ if (Redis.current.hget(CATALOG_LOOKUP_CACHE_TIME_KEY, self.id).to_i + CATALOG_LOOKUP_TTL.to_i) > Time.now.to_i
971
+ begin
972
+ catalog_container = JSON.parse(Redis.current.hget(CATALOG_LOOKUP_CACHE_RESULT_KEY, self.id))
973
+ rescue JSON::ParserError => ex
974
+ Rails.logger.warn('Failed to parse catalog cache', ex)
975
+ end
976
+ else
977
+ zuora_login = self.login_lookup(type: 'Zuora').first
978
+ login = zuora_login.client(entity_reference)
979
+
980
+ response = {
981
+ 'nextPage' => login.rest_endpoint("catalog/products?pageSize=#{CATALOG_LOOKUP_PAGE_SIZE}")
982
+ }
983
+
984
+ while response['nextPage'].present?
985
+ url = login.rest_endpoint(response['nextPage'].split('/v1/').last)
986
+ output_json, response = login.rest_call(debug: false, url: url, timeout_retry: true)
987
+
988
+ case object
989
+ when :product
990
+ output_json.fetch('products', []).each do |product|
991
+ rate_plans = {}
992
+ product['productRatePlans'].each do |rate_plan|
993
+ charges = {}
994
+ rate_plan['productRatePlanCharges'].each do |charge|
995
+ charges[charge['id']] = charge.merge(
996
+ {
997
+ 'productId' => product['id'],
998
+ 'productName' => product['name'],
999
+ 'productRatePlanId' => rate_plan['id'],
1000
+ 'productRatePlanName' => rate_plan['name'],
1001
+ }
1002
+ )
1003
+ end
1004
+
1005
+ rate_plan['productRatePlanCharges'] = charges
1006
+ rate_plans[rate_plan['id']] = rate_plan.merge(
1007
+ {
1008
+ 'productId' => product['id'],
1009
+ 'productName' => product['name']
1010
+ }
1011
+ )
1012
+ end
1013
+
1014
+ product['productRatePlans'] = rate_plans
1015
+ catalog_container[product['id']] = product
1016
+ end
1017
+ else
1018
+ raise "Available objects include [:product]"
1019
+ end
1020
+ end
1021
+
1022
+ Redis.current.hset(CATALOG_LOOKUP_CACHE_RESULT_KEY, self.id, catalog_container.to_json)
1023
+ Redis.current.hset(CATALOG_LOOKUP_CACHE_TIME_KEY, self.id, Time.now.to_i)
1024
+ end
1025
+
1026
+ if object_id.nil?
1027
+ catalog_container.transform_values! { |v| v.except('productRatePlans') }
1028
+ elsif object_id.is_a?(String)
1029
+ catalog_container = catalog_container[object_id]
1030
+ end
1031
+
1032
+ return catalog_container || {}
1033
+ end
1034
+
965
1035
  if defined?(Redis.current) && object_id.present? && object_id.class == String && object_id.present?
966
1036
  stub_catalog = cache ? decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Children:#{child_objects}")) : nil
967
1037
  object_hierarchy = decrypt_data(data: Redis.current.get("Catalog:#{self.id}:#{object_id}:Hierarchy"))
@@ -1221,7 +1291,7 @@ module ZuoraConnect
1221
1291
 
1222
1292
  def self.without_sticking
1223
1293
  if self.connection.respond_to?(:without_sticking)
1224
- self.connection.without_sticking do
1294
+ self.connection.without_sticking do
1225
1295
  yield
1226
1296
  end
1227
1297
  else