Class: Catalog::PullAmazonCatalogsListingsData

Inherits:
BaseService
  • Object
show all
Defined in:
app/services/catalog/pull_amazon_catalogs_listings_data.rb

Overview

Service object: pull amazon catalogs listings data.

Defined Under Namespace

Classes: Result

Instance Attribute Summary

Attributes inherited from BaseService

#options

Instance Method Summary collapse

Methods inherited from BaseService

#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #tagged_logger

Constructor Details

This class inherits a constructor from BaseService

Instance Method Details

#process(options) ⇒ Result

Processes all Amazon catalogs' catalog items, attempting to pull their listing item data. Mark the catalog item as pending_onboarding if no corresponding listing item data found, also accept force_pricing_sync to:
use Catalog::UpdateCatalogItem

  • Store the procurement cost price to catalog item's amount
  • Store the list price to catalog item's retailer advertised price

Parameters:

  • options (Hash)

    processing options

Options Hash (options):

  • force_pricing_sync (Boolean)

    also sync procurement cost and list price onto the catalog item

  • catalog_ids (Array<Integer>)

    restrict processing to these Amazon catalog ids (defaults to all Amazon catalogs)

  • catalog_item_ids (Array<Integer>)

    restrict processing to these catalog item ids

  • limit (Integer, String)

    maximum number of catalog items to process

  • refresh_if_older_than (Time, DateTime, String)

    only process items whose amazon_info_datetime is missing or older than this time

Returns:



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'app/services/catalog/pull_amazon_catalogs_listings_data.rb', line 23

def process(options)
  options[:force_pricing_sync].to_b
  catalog_ids = options[:catalog_ids]
  catalog_item_ids = options[:catalog_item_ids]
  limit = options[:limit]&.to_i

  messages = []
  catalog_items_updated = []
  catalog_items_failed = []
  catalogs = Catalog.amazons
  catalogs = catalogs.where(id: catalog_ids) if catalog_ids.present?
  logger.info 'Amazon Pull Amazon Catalogs Listings Data processing start.'
  logger.debug("-- process starting")
  catalog_items = CatalogItem.amazons_with_asins.joins(:catalog).merge(catalogs)
  catalog_items = catalog_items.where(id: catalog_item_ids) if catalog_item_ids.present?
  catalog_items = catalog_items.where(state: %w[active])
  catalog_items = catalog_items.limit(limit) if limit.present?
  if options[:refresh_if_older_than].present?
    catalog_items = catalog_items.where(%{
      catalog_items.amazon_info_datetime IS NULL OR (catalog_items.amazon_info_datetime IS NOT NULL and catalog_items.amazon_info_datetime <= ?)
    }, options[:refresh_if_older_than])
  end

  total_records = catalog_items.count
  logger.info "Found #{total_records} catalog items to process for catalogs: #{catalog_ids&.join(', ') || 'all Amazon'}"

  # Amazon SP-API rate limits:
  # - Product Pricing API (Buy Box): 10 requests/sec burst, 0.5/sec restore
  # - Catalog Items API: 2 requests/sec burst, 2/sec restore
  # - Listings API: 5 requests/sec burst, 5/sec restore
  # To be safe, we use 0.6 seconds between each API call (under 2/sec)
  rate_limit_delay = 0.6.seconds

  index = 1
  catalog_items.find_each do |catalog_item|
    yield(at: index, total: total_records, message: "Processing catalog item #{catalog_item.id}") if block_given?
    logger.info "Processing catalog item #{catalog_item.id} for amazon data"
    action_success_counter = 0
    action_msgs = []

    begin
      %w[amazon_pull_catalog_information amazon_pull_listing_information amazon_pull_buy_box_status].each do |action|
        res = {}
        PaperTrail.request(whodunnit: 'Catalog::PullAmazonCatalogsListingsData') do
          res = catalog_item.send(action)
        end
        action_msgs << res[:message]
        action_success_counter += 1 if res[:status] == :success
        logger.info "Processed #{action} for catalog item #{catalog_item.id}, message: #{res[:message]}, success: #{res[:status]}, action_success_counter: #{action_success_counter}"

        # Rate limiting: sleep between API calls to avoid 429 errors
        sleep(rate_limit_delay)
      end
    rescue StandardError => e
      # Log the error but continue processing other items
      logger.error "Error processing catalog item #{catalog_item.id}: #{e.message}"
      logger.error e.backtrace.first(5).join("\n")
      action_msgs << "Error: #{e.message}"
    end

    if action_success_counter == 3
      catalog_items_updated << catalog_item
    else
      catalog_items_failed << catalog_item
    end
    messages += action_msgs
    index += 1
  end
  Result.new(all_catalog_items_listings_data_pulled: catalog_items_failed.empty?,
             messages:,
             catalog_items_updated:,
             catalog_items_failed:)
end