Class: Menard::PartnerPortalUploader
- Inherits:
-
Object
- Object
- Menard::PartnerPortalUploader
- Defined in:
- app/services/menard/partner_portal_uploader.rb
Overview
Uploads an inventory spreadsheet to the Menard partner portal via browser automation.
Uses playwright-ruby-client to log in, navigate to the inventory upload page,
attach the XLSX file, and submit the form.
The Menard portal is a JavaScript SPA — the login form is rendered dynamically
by main.bundle.js, not present in the initial HTML. Chromium must fully execute
JS in headless mode, which requires --no-sandbox and --disable-dev-shm-usage
when running from background processes (Sidekiq) or on Linux servers.
Credentials are read from Heatwave::Configuration (encrypted credentials):
menard:
portal_username:
portal_password:
Defined Under Namespace
Classes: Result
Constant Summary collapse
- PORTAL_BASE_URL =
URL for portal base.
'https://partners.menard-inc.com/partners'- LOGIN_URL =
URL for login.
"#{PORTAL_BASE_URL}/login.html".freeze
- INVENTORY_URL =
URL for inventory.
"#{PORTAL_BASE_URL}/viewInventoryUpdates.html".freeze
- NAVIGATION_TIMEOUT =
Timeout for navigation.
60_000- SPA_RENDER_TIMEOUT =
Timeout for spa render.
45_000
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(file_path:, headless: true) ⇒ PartnerPortalUploader
constructor
A new instance of PartnerPortalUploader.
Constructor Details
#initialize(file_path:, headless: true) ⇒ PartnerPortalUploader
Returns a new instance of PartnerPortalUploader.
37 38 39 40 41 |
# File 'app/services/menard/partner_portal_uploader.rb', line 37 def initialize(file_path:, headless: true) @file_path = file_path @headless = headless validate! end |
Instance Method Details
#call ⇒ Object
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 |
# File 'app/services/menard/partner_portal_uploader.rb', line 43 def call require 'playwright' log('Starting browser automation', level: :info) PlaywrightRuntime.with_browser(launch_options: ) do |browser| log("Browser ready (headless=#{@headless}, remote=#{PlaywrightRuntime.remote?})", level: :info) active_page = nil page = nil begin context = browser.new_context( userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' \ '(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' ) page = context.new_page attach_diagnostics(page) active_page = login(page) upload_inventory(active_page) rescue Playwright::Error, Playwright::TimeoutError => e dump_page_html(active_page || page, 'timeout_failure') log("Playwright error: #{e.class} — #{e.}", level: :error) Result.new(uploaded: false, message: "Browser automation failed: #{e.}") rescue StandardError => e dump_page_html(active_page || page, 'unexpected_failure') log("#{e.class}: #{e.}\n#{e.backtrace&.first(5)&.join("\n")}", level: :error) Result.new(uploaded: false, message: "Upload failed: #{e.}") end end rescue StandardError => e log("Fatal: #{e.class}: #{e.}", level: :error) Result.new(uploaded: false, message: "Upload failed: #{e.}") end |