class Appsignal::Hooks::ActionCableHook

@!visibility private

def dependencies_present?

def dependencies_present?
  defined?(::ActiveSupport::Notifications::Instrumenter) &&
    defined?(::ActionCable)
end

def install

def install
  require "appsignal/integrations/action_cable"
  ActionCable::Channel::Base.prepend Appsignal::Integrations::ActionCableIntegration
  install_subscribe_callback
  install_unsubscribe_callback
end

def install_subscribe_callback

def install_subscribe_callback
  ActionCable::Channel::Base.set_callback :subscribe, :around,
    :prepend => true do |channel, inner|
    # The request is only the original websocket request
    connection = channel.connection
    # #env is not available on the Rails ConnectionStub class used in the
    # Rails app test suite. If we call `#env` it causes an error to occur
    # in apps' test suites.
    env = connection.respond_to?(:env) ? connection.env : {}
    request = ActionDispatch::Request.new(env)
    request_id = request.request_id || SecureRandom.uuid
    env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
    transaction =
      Appsignal::Transaction.create(Appsignal::Transaction::ACTION_CABLE)
    begin
      Appsignal.instrument "subscribed.action_cable" do
        inner.call
      end
    rescue Exception => exception # rubocop:disable Lint/RescueException
      transaction.set_error(exception)
      raise exception
    ensure
      transaction.set_action_if_nil("#{channel.class}#subscribed")
      transaction.set_metadata("path", request.path)
      transaction.set_metadata("method", "websocket")
      transaction.add_params_if_nil { request.params }
      transaction.add_headers_if_nil { request.env }
      transaction.add_session_data { request.session.to_h if request.respond_to? :session }
      transaction.add_tags(:request_id => request_id) if request_id
      Appsignal::Transaction.complete_current!
    end
  end
end

def install_unsubscribe_callback

def install_unsubscribe_callback
  ActionCable::Channel::Base.set_callback :unsubscribe, :around,
    :prepend => true do |channel, inner|
    # The request is only the original websocket request
    connection = channel.connection
    # #env is not available on the Rails ConnectionStub class used in the
    # Rails app test suite. If we call `#env` it causes an error to occur
    # in apps' test suites.
    env = connection.respond_to?(:env) ? connection.env : {}
    request = ActionDispatch::Request.new(env)
    request_id = request.request_id || SecureRandom.uuid
    env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||= request_id
    transaction =
      Appsignal::Transaction.create(Appsignal::Transaction::ACTION_CABLE)
    begin
      Appsignal.instrument "unsubscribed.action_cable" do
        inner.call
      end
    rescue Exception => exception # rubocop:disable Lint/RescueException
      transaction.set_error(exception)
      raise exception
    ensure
      transaction.set_action_if_nil("#{channel.class}#unsubscribed")
      transaction.set_metadata("path", request.path)
      transaction.set_metadata("method", "websocket")
      transaction.add_params_if_nil { request.params }
      transaction.add_headers_if_nil { request.env }
      transaction.add_session_data { request.session.to_h if request.respond_to? :session }
      transaction.add_tags(:request_id => request_id) if request_id
      Appsignal::Transaction.complete_current!
    end
  end
end