Class: Retailer::BestBuyUrlDiscovery
- Inherits:
-
Object
- Object
- Retailer::BestBuyUrlDiscovery
- Defined in:
- app/services/retailer/best_buy_url_discovery.rb
Overview
Discovers bestbuy.ca product URLs for WarmlyYours catalog items via Best Buy's
own public JSON API — no Oxylabs spend, no HTML scraping, no search-results
page to mis-resolve.
Best Buy Canada serves an unauthenticated JSON API that answers from a plain
datacenter IP (unlike the HTML site, which is bot-gated):
/api/v2/json/search?query=warmlyyours → { products: [{ sku, productUrl }] }
/api/v2/json/product/{sku} → { sku, modelNumber, productUrl }
sku is Best Buy's own product id and the trailing segment of every PDP URL,
and it is what we store in CatalogItem#third_party_part_number. modelNumber
is OUR manufacturer SKU, which gives a second, equally hard identity for items
whose part number was never captured.
== Matching
Two exact-equality matches, in order — never a fuzzy title match, because a
wrong match here writes a wrong price into MAP reporting (the failure mode
PR #1480 removed the search fallback over):
third_party_part_number== Best Buysku- our item SKU == Best Buy
modelNumber
An item matching neither is genuinely not in Best Buy's catalogue — it should
be reported as a listing problem, not guessed at, so #discover_url returns nil.
Constant Summary collapse
- BASE_URL =
'https://www.bestbuy.ca'- SEARCH_ENDPOINT =
"#{BASE_URL}/api/v2/json/search".freeze
- PRODUCT_ENDPOINT =
"#{BASE_URL}/api/v2/json/product".freeze
- BRAND_QUERY =
Brand query. Best Buy returns the full WarmlyYours assortment (65 products
as of 2026-08-08), so one page covers it. 'warmlyyours'- PAGE_SIZE =
100- USER_AGENT =
The API rejects an obviously-scripted client; a normal desktop UA is served
the same public JSON a browser gets. 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ' \ '(KHTML, like Gecko) Chrome/128.0 Safari/537.36'
- TIMEOUT =
20- MAX_DETAIL_FETCHES =
Ceiling on per-product detail calls in one run. At ~10s each this bounds the
model-number fallback to a couple of minutes even if Best Buy's assortment
grows or every part number goes missing at once. 25
Instance Method Summary collapse
-
#discover_url(catalog_item) ⇒ String?
Absolute PDP URL, or nil when Best Buy doesn't list it.
-
#initialize(logger: Rails.logger) ⇒ BestBuyUrlDiscovery
constructor
A new instance of BestBuyUrlDiscovery.
Constructor Details
#initialize(logger: Rails.logger) ⇒ BestBuyUrlDiscovery
Returns a new instance of BestBuyUrlDiscovery.
56 57 58 |
# File 'app/services/retailer/best_buy_url_discovery.rb', line 56 def initialize(logger: Rails.logger) @logger = logger end |
Instance Method Details
#discover_url(catalog_item) ⇒ String?
Returns absolute PDP URL, or nil when Best Buy doesn't list it.
62 63 64 65 66 |
# File 'app/services/retailer/best_buy_url_discovery.rb', line 62 def discover_url(catalog_item) part_number = catalog_item.third_party_part_number.to_s.strip matched = by_sku[part_number].presence if part_number.present? matched || by_model_number[normalize(catalog_item.sku)].presence end |