Class: Menard::PartnerPortalUploader

Inherits:
Object
  • Object
show all
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 =
'https://partners.menard-inc.com/partners'
LOGIN_URL =
"#{PORTAL_BASE_URL}/login.html".freeze
INVENTORY_URL =
"#{PORTAL_BASE_URL}/viewInventoryUpdates.html".freeze
60_000
SPA_RENDER_TIMEOUT =
45_000

Instance Method Summary collapse

Constructor Details

#initialize(file_path:, headless: true) ⇒ PartnerPortalUploader

Returns a new instance of PartnerPortalUploader.



31
32
33
34
35
# File 'app/services/menard/partner_portal_uploader.rb', line 31

def initialize(file_path:, headless: true)
  @file_path = file_path
  @headless = headless
  validate!
end

Instance Method Details

#callObject



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
# File 'app/services/menard/partner_portal_uploader.rb', line 37

def call
  require 'playwright'
  log('Starting browser automation', level: :info)

  Playwright.create(playwright_cli_executable_path: PlaywrightRuntime.cli_path) do |playwright|
    browser = playwright.chromium.launch(**launch_options)
    log("Browser launched (headless=#{@headless})", level: :info)

    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)

    begin
      active_page = (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.message}", level: :error)
      Result.new(uploaded: false, message: "Browser automation failed: #{e.message}")
    rescue StandardError => e
      dump_page_html(active_page || page, 'unexpected_failure')
      log("#{e.class}: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}", level: :error)
      Result.new(uploaded: false, message: "Upload failed: #{e.message}")
    end
  ensure
    browser&.close
  end
rescue StandardError => e
  log("Fatal: #{e.class}: #{e.message}", level: :error)
  Result.new(uploaded: false, message: "Upload failed: #{e.message}")
end