Re-add session errors (preparation for merge) (#395)

Raise helpful errors from Hanami::Action#flash and Hanami::Action#session when sessions are not enabled.
This commit is contained in:
Tim Riley 2022-10-17 21:20:32 +11:00 committed by GitHub
parent 24ee339323
commit e47fe2484e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 159 additions and 15 deletions

View File

@ -24,6 +24,7 @@ require_relative "action/mime"
require_relative "action/rack/file"
require_relative "action/request"
require_relative "action/response"
require_relative "action/error"
module Hanami
# An HTTP endpoint
@ -318,13 +319,14 @@ module Hanami
halted = catch :halt do
params = self.class.params_class.new(env)
request = build_request(env, params)
request = build_request(env: env, params: params, sessions_enabled: sessions_enabled?)
response = build_response(
request: request,
config: config,
content_type: Mime.calculate_content_type_with_charset(config, request, config.accepted_mime_types),
env: env,
headers: config.default_headers
headers: config.default_headers,
sessions_enabled: sessions_enabled?
)
enforce_accepted_mime_types(request)
@ -430,10 +432,17 @@ module Hanami
nil
end
# @see Session#sessions_enabled?
# @since 2.0.0
# @api private
def build_request(env, params)
Request.new(env, params)
def sessions_enabled?
false
end
# @since 2.0.0
# @api private
def build_request(**options)
Request.new(**options)
end
# @since 2.0.0

View File

@ -96,6 +96,7 @@ module Hanami
# @api private
def self.included(action)
unless Hanami.respond_to?(:env?) && Hanami.env?(:test)
action.include Hanami::Action::Session
action.class_eval do
before :set_csrf_token, :verify_csrf_token
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module Hanami
class Action
# @since 2.0.0
class Error < ::StandardError
end
# Missing session error
#
# This error is raised when `session` or `flash` is accessed/set on request/response objects
# in actions which do not include `Hanami::Action::Session`.
#
# @since 2.0.0
#
# @see Hanami::Action::Session
# @see Hanami::Action::Request#session
# @see Hanami::Action::Response#session
# @see Hanami::Action::Response#flash
class MissingSessionError < Error
def initialize(session_method)
super(<<~TEXT)
Sessions are not enabled. To use `#{session_method}`:
Configure sessions in your Hanami app, e.g.
module MyApp
class App < Hanami::App
# See Rack::Session::Cookie for options
config.sessions = :cookie, {**cookie_session_options}
end
end
Or include session support directly in your action class:
include Hanami::Action::Session
TEXT
end
end
end
end

View File

@ -16,9 +16,11 @@ module Hanami
class Request < ::Rack::Request
attr_reader :params
def initialize(env, params)
def initialize(env:, params:, sessions_enabled: false)
super(env)
@params = params
@sessions_enabled = sessions_enabled
end
def id
@ -36,6 +38,14 @@ module Hanami
accept != Action::DEFAULT_ACCEPT
end
def session
unless @sessions_enabled
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#session")
end
super
end
# @since 0.1.0
# @api private
def accept

View File

@ -45,7 +45,7 @@ module Hanami
# @since 2.0.0
# @api private
def initialize(request:, config:, content_type: nil, env: {}, headers: {}, view_options: nil) # rubocop:disable Metrics/ParameterLists
def initialize(request:, config:, content_type: nil, env: {}, headers: {}, view_options: nil, sessions_enabled: false) # rubocop:disable Layout/LineLength, Metrics/ParameterLists
super([], 200, headers.dup)
set_header(Action::CONTENT_TYPE, content_type)
@ -56,6 +56,7 @@ module Hanami
@env = env
@view_options = view_options || DEFAULT_VIEW_OPTIONS
@sessions_enabled = sessions_enabled
@sending_file = false
end
@ -102,6 +103,10 @@ module Hanami
# @since 2.0.0
# @api public
def session
unless @sessions_enabled
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Response#session")
end
env[Action::RACK_SESSION] ||= {}
end
@ -114,6 +119,10 @@ module Hanami
# @since 2.0.0
# @api public
def flash
unless @sessions_enabled
raise Hanami::Action::MissingSessionError.new("Hanami::Action::Response#flash")
end
@flash ||= Flash.new(session[Flash::KEY])
end
@ -179,7 +188,8 @@ module Hanami
def request_id
env.fetch(Action::REQUEST_ID) do
# FIXME: raise a meaningful error, by inviting devs to include Hanami::Action::Session
raise "Can't find request ID"
# raise "Can't find request ID"
raise Hanami::Action::MissingSessionError.new("request_id")
end
end

View File

@ -18,6 +18,10 @@ module Hanami
private
def sessions_enabled?
true
end
# Finalize the response
#
# @return [void]

View File

@ -41,4 +41,13 @@ RSpec.describe "Flash application" do
expect(last_response.body).to match(/flash_map: \[\[:hello, "world"\]\]/)
end
end
context "when sessions not enabled" do
it "raises Hanami::Action::MissingSessionError" do
expect { get "/disabled" }.to raise_error(
Hanami::Action::MissingSessionError,
/Hanami::Action::Response#flash/
)
end
end
end

View File

@ -12,6 +12,7 @@ RSpec.describe "HTTP sessions" do
get "/", to: Dashboard::Index.new
post "/login", to: Sessions::Create.new
delete "/logout", to: Sessions::Destroy.new
get "/disabled", to: Sessions::Disabled.new
end
end
@ -52,6 +53,13 @@ RSpec.describe "HTTP sessions" do
get "/"
expect(response.status).to be(401)
end
context "when sessions not enabled" do
it "raises Hanami::Action::MissingSessionError" do
expected = Hanami::Action::MissingSessionError
expect { get "/disabled" }.to raise_error(expected, /Hanami::Action::Response#session/)
end
end
end
RSpec.describe "HTTP Standalone Sessions" do

View File

@ -347,15 +347,21 @@ class YieldAfterBlockAction < AfterBlockAction
end
end
class MissingSessionAction < Hanami::Action
def handle(*)
session
class MissingRequestSessionAction < Hanami::Action
def handle(req, _)
req.session[:user_id]
end
end
class MissingFlashAction < Hanami::Action
def handle(*)
flash
class MissingResponseSessionAction < Hanami::Action
def handle(_, res)
res.session[:user_id] = 23
end
end
class MissingResponseFlashAction < Hanami::Action
def handle(_, res)
res.flash[:error] = "ouch"
end
end
@ -854,6 +860,12 @@ module Sessions
res.session[:user_id] = nil
end
end
class Disabled < Hanami::Action
def handle(*, res)
res.session[:user_id] = 23
end
end
end
class StandaloneSession < Hanami::Action
@ -1803,6 +1815,12 @@ module Flash
res.body = "flash_map: #{res.flash.map { |type, message| [type, message] }}"
end
end
class Disabled < Hanami::Action
def handle(_, res)
res.flash[:error] = "ouch"
end
end
end
end
@ -1817,6 +1835,7 @@ module Flash
get "/each_redirect", to: Flash::Controllers::Home::EachRedirect.new
get "/map", to: Flash::Controllers::Home::Map.new
get "/each", to: Flash::Controllers::Home::Each.new
get "/disabled", to: Flash::Controllers::Home::Disabled.new
end
@middleware = Rack::Builder.new do

View File

@ -63,7 +63,10 @@ RSpec.describe Hanami::Action::Request do
context "non-standard port" do
it "gets host and port" do
request = described_class.new(Rack::MockRequest.env_for("http://example.com:81/foo?q=bar", {}), {})
request = described_class.new(
env: Rack::MockRequest.env_for("http://example.com:81/foo?q=bar", {}),
params: {}
)
expect(request.host_with_port).to eq("example.com:81")
end
end
@ -155,6 +158,6 @@ RSpec.describe Hanami::Action::Request do
def build_request(attributes = {})
url = attributes.delete("url") || "http://example.com/foo?q=bar"
env = Rack::MockRequest.env_for(url, attributes)
described_class.new(env, {})
described_class.new(env: env, params: {})
end
end

View File

@ -77,6 +77,36 @@ RSpec.describe Hanami::Action do
expect(response.body).to eq([])
end
end
context "when setting res.session with sessions disabled" do
it "raises an informative exception" do
expected = Hanami::Action::MissingSessionError
expect { MissingResponseSessionAction.new.call({}) }.to raise_error(
expected,
/Hanami::Action::Response#session/
)
end
end
context "when setting res.flash with sessions disabled" do
it "raises an informative exception" do
expected = Hanami::Action::MissingSessionError
expect { MissingResponseFlashAction.new.call({}) }.to raise_error(
expected,
/Hanami::Action::Response#flash/
)
end
end
context "when accessing req.session with sessions disabled" do
it "raises an informative exception" do
expected = Hanami::Action::MissingSessionError
expect { MissingRequestSessionAction.new.call({}) }.to raise_error(
expected,
/Hanami::Action::Request#session/
)
end
end
end
describe "request" do