diff --git a/lib/hanami/lambda/env.rb b/lib/hanami/lambda/env.rb new file mode 100644 index 0000000..6e18105 --- /dev/null +++ b/lib/hanami/lambda/env.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "rack" + +module Hanami + module Lambda + class Env + def initialize(event:, headers:, context:) + @event = event + @headers = standardize_headers(headers) + @context = context + end + + def to_h + { + ::Rack::REQUEST_METHOD => @event["httpMethod"], + ::Rack::PATH_INFO => @event["path"] || "", + ::Rack::RACK_VERSION => ::Rack.release, + ::Rack::RACK_INPUT => StringIO.new(@event["body"] || ""), + ::Hanami::Lambda::LAMBDA_EVENT => @event, + ::Hanami::Lambda::LAMBDA_CONTEXT => @context + }.merge(@headers) + end + + private + + def standardize_headers(headers) + headers.transform_keys do |key| + if ::Rack::Headers::KNOWN_HEADERS[key.downcase.tr("_", "-")] + key.upcase.tr("-", "_") + else + "HTTP_#{key.upcase.tr('-', '_')}" + end + end + end + end + end +end diff --git a/lib/hanami/lambda/rack.rb b/lib/hanami/lambda/rack.rb index 9fe9ec7..7fc8f3b 100644 --- a/lib/hanami/lambda/rack.rb +++ b/lib/hanami/lambda/rack.rb @@ -41,19 +41,7 @@ def call(event:, context:) # # @since 0.1.0 def build_env(event, headers, context) - { - ::Rack::REQUEST_METHOD => event["httpMethod"], - ::Rack::PATH_INFO => event["path"] || "", - ::Rack::RACK_VERSION => ::Rack.release, - ::Rack::RACK_INPUT => StringIO.new(event["body"] || ""), - ::Hanami::Lambda::LAMBDA_EVENT => event, - ::Hanami::Lambda::LAMBDA_CONTEXT => context - }.tap do |env| - content_type = headers.delete("Content-Type") || - headers.delete("content-type") || - headers.delete("CONTENT_TYPE") - env["CONTENT_TYPE"] = content_type if content_type - end.merge(headers.transform_keys { |k| "HTTP_#{k.upcase.tr('-', '_')}" }) + Env.new(event: event, headers: headers, context: context).to_h end end end diff --git a/sig/hanami/lambda/env.rbs b/sig/hanami/lambda/env.rbs new file mode 100644 index 0000000..ca0adfb --- /dev/null +++ b/sig/hanami/lambda/env.rbs @@ -0,0 +1,23 @@ +module Hanami + module Lambda + class Env + @event: Hash[String, untyped] + + @headers: Hash[String, String] + + @context: Object + + def initialize: (event: Hash[String, untyped], headers: Hash[String, String], context: Object) -> void + + # Build a hash of the Rack environment + # + # @return [Hash] the hash of Rack environment + # + def to_h: () -> ::Hash[String, untyped] + + private + + def standardize_headers: (Hash[String, String]) -> Hash[String, String] + end + end +end diff --git a/sig/shims/rack.rbs b/sig/shims/rack.rbs new file mode 100644 index 0000000..d9b6b3d --- /dev/null +++ b/sig/shims/rack.rbs @@ -0,0 +1,5 @@ +module Rack + class Headers + KNOWN_HEADERS: Hash[String, String] + end +end diff --git a/spec/hanami/lambda/env_spec.rb b/spec/hanami/lambda/env_spec.rb new file mode 100644 index 0000000..2348889 --- /dev/null +++ b/spec/hanami/lambda/env_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +RSpec.describe Hanami::Lambda::Env do + subject(:env) { described_class.new(event: event, headers: headers, context: context) } + + let(:event) do + { + "httpMethod" => "GET", + "path" => "/", + "body" => "" + } + end + + let(:headers) do + { + "Content-Type" => "text/plain", + "X-Custom-Header" => "Custom Value" + } + end + + let(:context) { double(:context) } + + describe "#to_h" do + subject(:hash) { env.to_h } + + it { is_expected.to include(::Hanami::Lambda::LAMBDA_EVENT) } + it { is_expected.to include(::Hanami::Lambda::LAMBDA_CONTEXT) } + it { is_expected.to include("CONTENT_TYPE" => "text/plain") } + it { is_expected.to include("HTTP_X_CUSTOM_HEADER" => "Custom Value") } + end +end diff --git a/spec/hanami/lambda/rack_spec.rb b/spec/hanami/lambda/rack_spec.rb index dec496a..05bffaf 100644 --- a/spec/hanami/lambda/rack_spec.rb +++ b/spec/hanami/lambda/rack_spec.rb @@ -33,13 +33,4 @@ it { is_expected.to include(body: "Hello from Rack") } it { is_expected.to include(headers: {}) } end - - describe "#env" do - subject(:env) { rack.build_env(event, headers, context) } - - it { is_expected.to include(::Hanami::Lambda::LAMBDA_EVENT) } - it { is_expected.to include(::Hanami::Lambda::LAMBDA_CONTEXT) } - it { is_expected.to include("CONTENT_TYPE" => "text/plain") } - it { is_expected.to include("HTTP_X_CUSTOM_HEADER" => "Custom Value") } - end end