Skip to content

Commit

Permalink
readme and example app
Browse files Browse the repository at this point in the history
  • Loading branch information
sorah committed Oct 17, 2022
1 parent 4e5a9f6 commit 96f5854
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 11 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@

# rspec failure tracking
.rspec_status

integration/template.json
.aws-sam/
samconfig.toml
13 changes: 13 additions & 0 deletions Dockerfile.integration
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM public.ecr.aws/lambda/ruby:2.7

ENV GEM_HOME=${LAMBDA_TASK_ROOT}
ENV BUNDLE_PATH=${LAMBDA_TASK_ROOT}/vendor/bundle
COPY integration/Gemfile* ${LAMBDA_TASK_ROOT}/
RUN bundle install

COPY lib ${LAMBDA_TASK_ROOT}/lib
COPY integration/* ${LAMBDA_TASK_ROOT}/

#RUN find ${LAMBDA_TASK_ROOT}

CMD ["main.Main.handle"]
47 changes: 36 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
# Apigatewayv2Rack
# apigatewayv2_rack: serve Rack app from AWS Lambda function via API Gateway V2 (HTTP API) or ALB (ELB v2) lambda target

Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/apigatewayv2_rack`. To experiment with that code, run `bin/console` for an interactive prompt.
Apigatewayv2Rack provides a method to convert a AWS Lambda invocation event from API Gateway V2 (HTTP API) or ALB lambda target (ELBv2) to a Rack request environment and a method to convert a Rack response tuple to a corresponding Lambda response object.

TODO: Delete this and the text above, and describe your gem
This gem also provides support for Lambda function URL as it uses the same schema with API Gateway V2.

## Installation
## Supported deployment and limitation

Install the gem and add to the application's Gemfile by executing:
- Supports Rack 2 and Rack 3 specification
- The following AWS Lambda invocation event schemas:
- [Amazon API Gateway HTTP API payload version 2.0](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html)
- [ALB lambda function target](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html)
- Multiple field lines are not supported on API Gateway schema except `set-cookie` header due to API Gateway's limitation
- `lambda.multi_value_headers.enabled` is recommended to be set for usage with ALB. <sup>[[doc](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#enable-multi-value-headers)]</sup>

$ bundle add apigatewayv2_rack
## Usage

If bundler is not being used to manage dependencies, install the gem by executing:
### Quick usage

$ gem install apigatewayv2_rack
```ruby
# Gemfile
gem 'apigatewayv2_rack'
```

## Usage
```ruby
# main.rb
require 'apigatewayv2_rack'
Main = Apigatewayv2Rack.handler_from_rack_config_file(File.join(__dir__, 'config.ru'))
```

And set lambda function handler to `main.Main.handle` then voila!

### Non-quick usage

```ruby
req = Apigatewayv2Rack::Request.new(event: event, context: context)
status, headers, body = rack_app.call(req.to_h)
resp = Apigatewayv2Rack::Response.new(status: status, headers: headers, body: body, elb: req.elb?, multivalued: req.multivalued?)
p resp.as_json
```

### Full example

TODO: Write usage instructions here
See [./Dockerfile.integration](./Dockerfile.integration) and [./integration](./integration).

## Development

Expand All @@ -26,7 +51,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/apigatewayv2_rack.
Bug reports and pull requests are welcome on GitHub at https://github.com/sorah/apigatewayv2_rack.

## License

Expand Down
6 changes: 6 additions & 0 deletions integration/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'
gem 'sinatra'
gem 'rack', '<3'
gem 'webrick'
#gem 'rackup'
#gem 'rack-session'
27 changes: 27 additions & 0 deletions integration/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
GEM
remote: https://rubygems.org/
specs:
mustermann (3.0.0)
ruby2_keywords (~> 0.0.1)
rack (2.2.4)
rack-protection (3.0.2)
rack
ruby2_keywords (0.0.5)
sinatra (3.0.2)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.0.2)
tilt (~> 2.0)
tilt (2.0.11)
webrick (1.7.0)

PLATFORMS
ruby

DEPENDENCIES
rack (< 3)
sinatra
webrick

BUNDLED WITH
2.4.0.dev
9 changes: 9 additions & 0 deletions integration/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
build:
jsonnet template.jsonnet > template.json
sam build

setup: build
sam deploy --guided

deploy: build
sam deploy
6 changes: 6 additions & 0 deletions integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# sinatra app for integration test

```
make setup
make deploy
```
19 changes: 19 additions & 0 deletions integration/app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'sinatra/base'

class App < Sinatra::Base
get '/' do
cnt = session[:cnt] || 0
content_type :html
"<!DOCTYPE html><html><head><meta charset='utf-8'><title>apigatewayv2_rack test</title><body><p>Hello from Lambda!</p><form method='post'><p><button type='submit'>+</button> #{cnt}</p></form>"
end

post '/' do
session[:cnt] ||= 0
session[:cnt] += 1
redirect '/'
end

get '/errortown' do
raise RuntimeError, 'errortown...'
end
end
7 changes: 7 additions & 0 deletions integration/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require_relative './app'
require 'rack'
require 'logger'

use(Rack::CommonLogger, Logger.new($stdout))
use(Rack::Session::Cookie, key: 'sess', expire_after: 3600, secret: 'insecure-secret')
run App
21 changes: 21 additions & 0 deletions integration/main.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
$:.unshift(File.join(__dir__, 'lib'))
require 'rack'
require 'rack/builder'
require 'apigatewayv2_rack'


#module Main
# @app = Rack::Builder.load_file(File.join(__dir__, 'config.ru'), {})[0]
# def self.handle(event:, context:)
# puts(JSON.generate(event: event, context: context))
# retval = Apigatewayv2Rack.handle_request(event: event, context: context, app: @app)
# puts(JSON.generate(retval))
# retval
# end
#end
#def handler(event:, context:)
# Main.handle(event: event, context: context)
#end

$stdout.sync = true
Main = Apigatewayv2Rack.handler_from_rack_config_file(File.join(__dir__, 'config.ru'))
26 changes: 26 additions & 0 deletions integration/template.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SAM template
{
AWSTemplateFormatVersion: '2010-09-09',
Transform: 'AWS::Serverless-2016-10-31',
Description: '',

Resources: {
AppFunction: {
Type: 'AWS::Serverless::Function', // https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties: {
PackageType: 'Image',
FunctionUrlConfig: { AuthType: 'NONE' },
},
Metadata: {
DockerContext: '..',
Dockerfile: 'Dockerfile.integration',
},
},
},
Outputs: {
AppUrl: {
Description: 'app endpoint',
Value: { 'Fn::GetAtt': ['AppFunctionUrl', 'FunctionUrl'] },
},
},
}
20 changes: 20 additions & 0 deletions lib/apigatewayv2_rack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,24 @@ def self.handle_request(app:, event:, context:, request_options: {})
status, headers, body = app.call(req.to_h)
Response.new(status: status, headers: headers, body: body, elb: req.elb?, multivalued: req.multivalued?).as_json
end

module Handler
attr_reader :app
def handle(event:, context:)
Apigatewayv2Rack.handle_request(event: event, context: context, app: @app)
end
end

def self.generate_handler(app)
m = Module.new
m.extend(Handler)
m.instance_variable_set(:@app, app)
m
end

def self.handler_from_rack_config_file(path = './config.ru')
require 'rack'
require 'rack/builder'
generate_handler(Rack::Builder.load_file(path, {})[0])
end
end

0 comments on commit 96f5854

Please sign in to comment.