lookbook
lookbook copied to clipboard
Configuring ActionCable `allowed_request_origins`
Describe the bug
Configuring Lookbook's ActionCable (e.g. to add allowed_request_origins
) is counter-intuitive and difficult to debug.
To Reproduce
Steps to reproduce the behavior:
- Add something like the following to an initializer:
Rails.application.configure do config.action_cable.allowed_request_origins ||= [] config.action_cable.allowed_request_origins << ExampleApp::Config.default_origin config.action_cable.allowed_request_origins << %r{\Ahttps?://localhost(:\d+)?\z} end
- Using one of the origins configured in step one, load Lookbook and view the logs. They will confusingly claim that the origin is not allowed. E.g:
Request origin not allowed: https://username-name-asldkfjasl-3000.preview.app.github.dev
- Notice that the websocket path is
/lookbook/cable
, which helps me realize thatLookbook::Cable
has its own configuration: https://github.com/ViewComponent/lookbook/blob/764cf8f2b44ee559a52fcd69cde1c48967d4803b/lib/lookbook/cable/cable.rb#L44-L51 - Replace the above configuration with something like the following:
Lookbook.after_initialize do if (config = Lookbook.engine.websocket&.server&.config) config.allowed_request_origins ||= [] config.allowed_request_origins << ExampleApp::Config.default_origin config.allowed_request_origins << %r{\Ahttps?://localhost(:\d+)?\z} end end
- Reload and see the same error message!?!
- After a lot of debugging, realize that
engine.websocket.full_mount_path
is now/cable
, and instead ofLookbook::Cable
andLookbook::Connection
it is nowActionCable::Server::Base
andActionCable::Connection::Base
. - After some more debugging, realize that accessing
Lookbook.engine.websocket
from an initializer calls the following method: https://github.com/ViewComponent/lookbook/blob/764cf8f2b44ee559a52fcd69cde1c48967d4803b/lib/lookbook/engine.rb#L119-L121 This is problematic:-
mount_path
comes fromconfig/routes.rb
but that loads after the initializers -
@_websocket
memoizesLookbook::Cable
with an emptyengine_mount_path
-
Workaround
Place the following line at the bottom of config/routes.rb
:
ActiveSupport::Notifications.instrument "routes_loaded.application"
And place the following into an initializer:
ActiveSupport::Notifications.subscribe "routes_loaded.application" do
Lookbook.engine.instance_variable_set(:@_websocket, nil)
if (config = Lookbook.engine.websocket&.server&.config)
config.allowed_request_origins ||= []
config.allowed_request_origins << ExampleApp::Config.default_origin
config.allowed_request_origins << %r{\Ahttps?://localhost(:\d+)?\z}
end
end
Is there a better approach?
Expected behavior
A simple method for configuring Lookbook's ActionCable::Server::Configuration
, documented in the Lookbook documentation. Or, if one exists and is documented elsewhere already, a link to that documentation.
Version numbers
Please complete the following information:
- Lookbook: 2.0.2
- ViewComponent: 3.0.0
- Rails: 6.1.7.3
- Ruby: 3.0.6
Additional context
I've never configured ActionCable before, so it's entirely possible I simply overlooked a much simpler approach that was documented and I couldn't find it.
To debug the issue, I used rdbg
and something like the following:
module LookbookLoggedViewAssigns
def view_assigns
if defined?(@engine) && @engine
logger.info {
"Lookbook engine: %p" % [{
mount_path: @engine.mount_path,
cable_mount_path: @engine.websocket.full_mount_path,
config: @engine.websocket.server.config.allowed_request_origins,
}]
}
end
super
end
end
module LookbookLoggedActionCable
def allow_request_origin?
logger.info({
class: self.class,
server_class: server.class,
config: server.config,
mount_path: Lookbook::Engine.mount_path,
cable_mount_path: Lookbook::Engine.websocket.full_mount_path,
engine_cable_mount_path: Lookbook.engine.websocket.full_mount_path,
}.inspect)
super
end
end
ActiveSupport.on_load(:action_controller_base) do
prepend LookbookLoggedViewAssigns
end
ActiveSupport.on_load(:action_cable_connection) do
prepend LoookbookLoggedActionCable
end