Class: Transport::PlaywrightPortalConnection
- Inherits:
-
Object
- Object
- Transport::PlaywrightPortalConnection
- Defined in:
- app/services/transport/playwright_portal_connection.rb
Overview
Browser-based transport for partner portals that require form-based file uploads.
Uses Playwright to automate login, navigation, and file upload in a headless browser.
This is the transport equivalent of SFTP or HTTP API connections, but for
JavaScript SPAs that lack a programmatic API (e.g., Menard partner portal).
The Menard portal is a PingOne SSO JavaScript SPA — the login form is rendered
dynamically by main.bundle.js, not present in the initial HTML.
Constant Summary collapse
- NAVIGATION_TIMEOUT =
60_000- SPA_RENDER_TIMEOUT =
45_000
Instance Attribute Summary collapse
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
Instance Method Summary collapse
-
#initialize(options = {}) ⇒ PlaywrightPortalConnection
constructor
A new instance of PlaywrightPortalConnection.
-
#send_file(file_path, remote_filepath = nil) ⇒ Hash
Uploads a file to the portal.
Constructor Details
#initialize(options = {}) ⇒ PlaywrightPortalConnection
Returns a new instance of PlaywrightPortalConnection.
19 20 21 22 23 24 25 |
# File 'app/services/transport/playwright_portal_connection.rb', line 19 def initialize( = {}) @options = @headless = .fetch(:headless, true) @logger = [:logger] || Rails.logger @portal_base_url = [:portal_base_url] @credentials = [:credentials] || {} end |
Instance Attribute Details
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
17 18 19 |
# File 'app/services/transport/playwright_portal_connection.rb', line 17 def logger @logger end |
Instance Method Details
#send_file(file_path, remote_filepath = nil) ⇒ Hash
Uploads a file to the portal. Returns a result hash compatible with other transports.
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 |
# File 'app/services/transport/playwright_portal_connection.rb', line 31 def send_file(file_path, remote_filepath = nil) raise ArgumentError, "File not found: #{file_path}" unless File.exist?(file_path) logger.info("[PlaywrightPortal] Starting upload of #{File.basename(file_path)}") Playwright.create(playwright_cli_executable_path: playwright_cli_path) do |playwright| browser = playwright.chromium.launch(**) 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 = login(page) upload_file(active_page, file_path) rescue Playwright::Error, Playwright::TimeoutError => e dump_page_html(active_page || page, 'timeout_failure') logger.error("[PlaywrightPortal] #{e.class}: #{e.}") { success: false, message: "Browser automation failed: #{e.}" } rescue StandardError => e dump_page_html(active_page || page, 'unexpected_failure') logger.error("[PlaywrightPortal] #{e.class}: #{e.}") { success: false, message: "Upload failed: #{e.}" } end ensure browser&.close end rescue StandardError => e logger.error("[PlaywrightPortal] Fatal: #{e.class}: #{e.}") { success: false, message: "Upload failed: #{e.}" } end |