require 'base64' module Jets::Controller::Rack class Adapter extend Memoist # Returns back API Gateway response hash structure def self.process(event, context, meth) adapter = new(event, context, meth) adapter.process end def initialize(event, context, meth) @event, @context, @meth = event, context, meth end # 1. Convert API Gateway event event to Rack env # 2. Process using full Rack middleware stack # 3. Convert back to API gateway response structure payload def process status, headers, body = Jets.application.call(env) convert_to_api_gateway(status, headers, body) end def env Env.new(@event, @context, adapter: true).convert # convert to Rack env end memoize :env # Transform the structure to AWS_PROXY compatible structure # http://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format def convert_to_api_gateway(status, headers, body) base64 = headers["x-jets-base64"] == 'yes' body = body.respond_to?(:read) ? body.read : body body = Base64.encode64(body) if base64 resp = { "statusCode" => status, "headers" => headers, "body" => body, "isBase64Encoded" => base64, } adjust_for_elb(resp) resp end # Note: ELB is not officially support. This is just in case users wish to manually # connect ELBs to the functions created by Jets. def adjust_for_elb(resp) return resp unless from_elb? # ELB requires statusCode to be an Integer whereas API Gateway requires statusCode to be a String status = resp["statusCode"] = resp["statusCode"].to_i # ELB also requires statusDescription attribute status_desc = Rack::Utils::HTTP_STATUS_CODES[status] status_desc = status_desc.nil? ? status.to_s : "#{status} #{status_desc}" resp["statusDescription"] = status_desc resp end def from_elb? # NOTE: @event["requestContext"]["elb"] is set when the request is coming from an elb # Can set JETS_ELB=1 for local testing @event["requestContext"] && @event["requestContext"]["elb"] || ENV['JETS_ELB'] end # Called from Jets::Controller::Base.process. Example: # # adapter.rack_vars( # 'jets.controller' => self, # 'lambda.context' => context, # 'lambda.event' => event, # 'lambda.meth' => meth, # ) # # Passes a these special variables so we have access to them in the middleware. # The controller instance is called in the Main middleware. # The lambda.* info is used by the Rack::Local middleware to create a mimiced # controller for the local server. # def rack_vars(vars) env.merge!(vars) end end end