lamby 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/cicd.yml +17 -0
  3. data/.gitignore +1 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +25 -0
  6. data/Dockerfile +12 -0
  7. data/Gemfile.lock +82 -83
  8. data/README.md +9 -3
  9. data/Rakefile +1 -0
  10. data/bin/_setup +6 -0
  11. data/bin/_test +6 -0
  12. data/bin/bootstrap +3 -17
  13. data/bin/setup +3 -3
  14. data/bin/test +6 -3
  15. data/bin/update +2 -2
  16. data/docker-compose.yml +14 -0
  17. data/lamby.gemspec +1 -1
  18. data/lib/lamby.rb +2 -1
  19. data/lib/lamby/handler.rb +16 -3
  20. data/lib/lamby/logger.rb +13 -9
  21. data/lib/lamby/rack.rb +11 -9
  22. data/lib/lamby/rack_alb.rb +11 -14
  23. data/lib/lamby/rack_http.rb +80 -0
  24. data/lib/lamby/{rack_api.rb → rack_rest.rb} +7 -3
  25. data/lib/lamby/ssm_parameter_store.rb +1 -1
  26. data/lib/lamby/templates.rake +4 -3
  27. data/lib/lamby/templates/{application_load_balancer.rb → alb.rb} +1 -1
  28. data/lib/lamby/templates/{application_load_balancer → alb}/app.rb +0 -0
  29. data/lib/lamby/templates/{application_load_balancer → alb}/build +1 -1
  30. data/lib/lamby/templates/{application_load_balancer → alb}/deploy +1 -1
  31. data/lib/lamby/templates/{application_load_balancer → alb}/template.yaml +1 -1
  32. data/lib/lamby/templates/{api_gateway.rb → http.rb} +2 -2
  33. data/lib/lamby/templates/{api_gateway → http}/app.rb +1 -1
  34. data/lib/lamby/templates/{api_gateway → http}/build +1 -1
  35. data/lib/lamby/templates/{api_gateway → http}/deploy +1 -1
  36. data/lib/lamby/templates/http/template.yaml +48 -0
  37. data/lib/lamby/templates/rest.rb +25 -0
  38. data/lib/lamby/templates/rest/app.rb +10 -0
  39. data/lib/lamby/templates/rest/build +23 -0
  40. data/lib/lamby/templates/rest/deploy +22 -0
  41. data/lib/lamby/templates/{api_gateway → rest}/template.yaml +2 -2
  42. data/lib/lamby/templates/shared.rb +3 -1
  43. data/lib/lamby/version.rb +1 -1
  44. data/vendor/.keep +0 -0
  45. metadata +29 -23
  46. data/.python-version +0 -1
  47. data/.travis.yml +0 -17
  48. data/Brewfile +0 -5
  49. data/Pipfile +0 -13
  50. data/Pipfile.lock +0 -370
@@ -0,0 +1,14 @@
1
+ version: '3.7'
2
+ services:
3
+ cicd:
4
+ build: .
5
+ environment:
6
+ - AWS_PROFILE=${AWS_PROFILE-default}
7
+ - RAILS_ENV=${RAILS_ENV-test}
8
+ - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION-us-east-1}
9
+ - SAM_CLI_TELEMETRY=0
10
+ - DIDPWD=${PWD}
11
+ volumes:
12
+ - ~/.aws:/root/.aws:delegated
13
+ - .:/var/task:delegated
14
+ - /var/run/docker.sock:/var/run/docker.sock
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'bundler'
25
25
  spec.add_development_dependency 'rake'
26
26
  spec.add_development_dependency 'minitest'
27
+ spec.add_development_dependency 'minitest-focus'
27
28
  spec.add_development_dependency 'mocha'
28
29
  spec.add_development_dependency 'pry'
29
- spec.add_development_dependency 'sqlite3'
30
30
  end
@@ -6,7 +6,8 @@ require 'lamby/version'
6
6
  require 'lamby/sam_helpers'
7
7
  require 'lamby/rack'
8
8
  require 'lamby/rack_alb'
9
- require 'lamby/rack_api'
9
+ require 'lamby/rack_rest'
10
+ require 'lamby/rack_http'
10
11
  require 'lamby/debug'
11
12
  require 'lamby/handler'
12
13
  require 'rails/railtie'
@@ -44,14 +44,27 @@ module Lamby
44
44
  self
45
45
  end
46
46
 
47
+ def base64_encodeable?
48
+ @headers && (
49
+ @headers['Content-Transfer-Encoding'] == 'binary' ||
50
+ @headers['X-Lamby-Base64'] == '1'
51
+ )
52
+ end
53
+
54
+ def body64
55
+ Base64.strict_encode64(body)
56
+ end
57
+
47
58
  private
48
59
 
49
60
  def rack
50
61
  @rack ||= case @options[:rack]
51
- when :api
52
- Lamby::RackApi.new @event, @context
53
- else
62
+ when :rest, :api
63
+ Lamby::RackRest.new @event, @context
64
+ when :alb
54
65
  Lamby::RackAlb.new @event, @context
66
+ else
67
+ Lamby::RackHttp.new @event, @context
55
68
  end
56
69
  end
57
70
 
@@ -1,19 +1,23 @@
1
1
  require 'logger'
2
2
 
3
- ENV['RAILS_LOG_TO_STDOUT'] = '1'
3
+ unless ENV['LAMBY_TEST']
4
4
 
5
- module Lamby
6
- module Logger
5
+ ENV['RAILS_LOG_TO_STDOUT'] = '1'
7
6
 
8
- def initialize(*args)
9
- args[0] = STDOUT
10
- super(*args)
11
- end
7
+ module Lamby
8
+ module Logger
9
+
10
+ def initialize(*args)
11
+ args[0] = STDOUT
12
+ super(*args)
13
+ end
12
14
 
15
+ end
13
16
  end
14
- end
15
17
 
16
- Logger.prepend Lamby::Logger
18
+ Logger.prepend Lamby::Logger
19
+
20
+ end
17
21
 
18
22
  # TODO: Railtie initializer
19
23
  # Rails.application.config.logger = ActiveSupport::TaggedLogging.new(
@@ -6,6 +6,7 @@ module Lamby
6
6
  LAMBDA_EVENT = 'lambda.event'.freeze
7
7
  LAMBDA_CONTEXT = 'lambda.context'.freeze
8
8
  HTTP_X_REQUESTID = 'HTTP_X_REQUEST_ID'.freeze
9
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
9
10
 
10
11
  attr_reader :event, :context
11
12
 
@@ -22,14 +23,6 @@ module Lamby
22
23
  {}
23
24
  end
24
25
 
25
- def api?
26
- false
27
- end
28
-
29
- def alb?
30
- false
31
- end
32
-
33
26
  def multi_value?
34
27
  false
35
28
  end
@@ -70,7 +63,16 @@ module Lamby
70
63
  end
71
64
 
72
65
  def query_string
73
- @query_string ||= event['queryStringParameters'].try(:to_query)
66
+ @query_string ||= if event.key?('rawQueryString')
67
+ event['rawQueryString']
68
+ elsif event.key?('multiValueQueryStringParameters')
69
+ query = event['multiValueQueryStringParameters'] || {}
70
+ query.map do |key, value|
71
+ value.map{ |v| "#{key}=#{v}" }.join('&')
72
+ end.flatten.join('&')
73
+ else
74
+ event['queryStringParameters'].try(:to_query)
75
+ end
74
76
  end
75
77
 
76
78
  def base64_encoded?
@@ -53,20 +53,17 @@ module Lamby
53
53
  end
54
54
 
55
55
  def headers_multi
56
- (event['multiValueHeaders'] || {}).transform_values do |v|
57
- v.is_a?(Array) ? v.first : v
58
- end
59
- end
60
-
61
- def query_string
62
- @query_string ||= multi_value? ? query_string_multi : super
63
- end
64
-
65
- def query_string_multi
66
- query = event['multiValueQueryStringParameters'] || {}
67
- string = query.map do |key, value|
68
- value.map{ |v| "#{key}=#{v}" }.join('&')
69
- end.flatten.join('&')
56
+ Hash[(event['multiValueHeaders'] || {}).map do |k,v|
57
+ if v.is_a?(Array)
58
+ if k == 'x-forwarded-for'
59
+ [k, v.join(', ')]
60
+ else
61
+ [k, v.first]
62
+ end
63
+ else
64
+ [k,v]
65
+ end
66
+ end]
70
67
  end
71
68
 
72
69
  end
@@ -0,0 +1,80 @@
1
+ module Lamby
2
+ class RackHttp < Lamby::Rack
3
+
4
+ def response(handler)
5
+ if handler.base64_encodeable?
6
+ { isBase64Encoded: true, body: handler.body64 }
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def env_base
15
+ { ::Rack::REQUEST_METHOD => request_method,
16
+ ::Rack::SCRIPT_NAME => '',
17
+ ::Rack::PATH_INFO => path_info,
18
+ ::Rack::QUERY_STRING => query_string,
19
+ ::Rack::SERVER_NAME => server_name,
20
+ ::Rack::SERVER_PORT => server_port,
21
+ ::Rack::SERVER_PROTOCOL => server_protocol,
22
+ ::Rack::RACK_VERSION => ::Rack::VERSION,
23
+ ::Rack::RACK_URL_SCHEME => 'https',
24
+ ::Rack::RACK_INPUT => StringIO.new(body || ''),
25
+ ::Rack::RACK_ERRORS => $stderr,
26
+ ::Rack::RACK_MULTITHREAD => false,
27
+ ::Rack::RACK_MULTIPROCESS => false,
28
+ ::Rack::RACK_RUNONCE => false,
29
+ LAMBDA_EVENT => event,
30
+ LAMBDA_CONTEXT => context
31
+ }.tap do |env|
32
+ ct = content_type
33
+ cl = content_length
34
+ env['CONTENT_TYPE'] = ct if ct
35
+ env['CONTENT_LENGTH'] = cl if cl
36
+ end
37
+ end
38
+
39
+ def env_headers
40
+ super.tap do |hdrs|
41
+ if event['cookies'].present?
42
+ hdrs[HTTP_COOKIE] = event['cookies'].join('; ')
43
+ end
44
+ end
45
+ end
46
+
47
+ def request_method
48
+ event.dig('requestContext', 'http', 'method') || event['httpMethod']
49
+ end
50
+
51
+ # Using custom domain names with v1.0 yields a good `path` parameter sans
52
+ # stage. However, v2.0 and others do not. So we are just going to remove stage
53
+ # no matter waht from other places for both.
54
+ #
55
+ def path_info
56
+ stage = event.dig('requestContext', 'stage')
57
+ spath = event.dig('requestContext', 'http', 'path') || event.dig('requestContext', 'path')
58
+ spath.sub /\A\/#{stage}/, ''
59
+ end
60
+
61
+ def server_name
62
+ headers['x-forwarded-host'] ||
63
+ headers['X-Forwarded-Host'] ||
64
+ headers['host'] ||
65
+ headers['Host']
66
+ end
67
+
68
+ def server_port
69
+ headers['x-forwarded-port'] || headers['X-Forwarded-Port']
70
+ end
71
+
72
+ def server_protocol
73
+ event.dig('requestContext', 'http', 'protocol') ||
74
+ event.dig('requestContext', 'protocol') ||
75
+ 'HTTP/1.1'
76
+ end
77
+
78
+
79
+ end
80
+ end
@@ -1,8 +1,12 @@
1
1
  module Lamby
2
- class RackApi < Lamby::Rack
2
+ class RackRest < Lamby::Rack
3
3
 
4
- def api?
5
- true
4
+ def response(handler)
5
+ if handler.base64_encodeable?
6
+ { isBase64Encoded: true, body: handler.body64 }
7
+ else
8
+ super
9
+ end
6
10
  end
7
11
 
8
12
  private
@@ -68,7 +68,7 @@ module Lamby
68
68
 
69
69
  def dotenv_contents
70
70
  params.each_with_object('') do |param, contents|
71
- line = "export #{param.env}=#{param.value}\n"
71
+ line = "#{param.env}=#{param.value}\n"
72
72
  contents << line
73
73
  end
74
74
  end
@@ -1,6 +1,7 @@
1
1
  installers = {
2
- 'API Gateway' => :api_gateway,
3
- 'Application Load Balancer' => :application_load_balancer
2
+ 'API Gateway (HTTP API)' => :http,
3
+ 'API Gateway (REST API)' => :rest,
4
+ 'Application Load Balancer' => :alb
4
5
  }.freeze
5
6
 
6
7
  namespace :lamby do
@@ -32,5 +33,5 @@ namespace :lamby do
32
33
  end
33
34
 
34
35
  desc "Install Lamby files for #{installers.first.first}"
35
- task install: 'install:application_load_balancer'
36
+ task install: 'install:http'
36
37
  end
@@ -1,4 +1,4 @@
1
- $LAMBY_INSTALLER_NAME = 'application_load_balancer'
1
+ $LAMBY_INSTALLER_NAME = 'alb'
2
2
  load 'lamby/templates/shared.rb'
3
3
 
4
4
  say '==> Running Lamby Application Load Balancer installer...'
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  # Clean any previous bundle/builds.
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  export RAILS_ENV=${RAILS_ENV:="production"}
@@ -70,7 +70,7 @@ Resources:
70
70
  Properties:
71
71
  CodeUri: .
72
72
  Handler: app.handler
73
- Runtime: ruby2.5
73
+ Runtime: ruby2.7
74
74
  MemorySize: 512
75
75
  Timeout: 30
76
76
  Environment:
@@ -1,7 +1,7 @@
1
- $LAMBY_INSTALLER_NAME = 'api_gateway'
1
+ $LAMBY_INSTALLER_NAME = 'http'
2
2
  load 'lamby/templates/shared.rb'
3
3
 
4
- say '==> Running Lamby API Gateway installer...'
4
+ say '==> Running Lamby API Gateway (HTTP API) installer...'
5
5
 
6
6
  say 'Copying files...'
7
7
  copy_file tpl_file('app.rb'), app_file('app.rb')
@@ -6,5 +6,5 @@ require_relative 'config/environment'
6
6
  $app = Rack::Builder.new { run Rails.application }.to_app
7
7
 
8
8
  def handler(event:, context:)
9
- Lamby.handler $app, event, context, rack: :api
9
+ Lamby.handler $app, event, context, rack: :http
10
10
  end
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  # Clean any previous bundle/builds.
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
  set -e
3
3
 
4
4
  export RAILS_ENV=${RAILS_ENV:="production"}
@@ -0,0 +1,48 @@
1
+ AWSTemplateFormatVersion: '2010-09-09'
2
+ Transform: AWS::Serverless-2016-10-31
3
+ Description: APPNAMEHERE Lambda (HTTP API)
4
+
5
+ Parameters:
6
+
7
+ RailsEnv:
8
+ Type: String
9
+ Default: production
10
+ AllowedValues:
11
+ - staging
12
+ - production
13
+
14
+ Resources:
15
+
16
+ RailsFunction:
17
+ Type: AWS::Serverless::Function
18
+ Properties:
19
+ CodeUri: .
20
+ Handler: app.handler
21
+ Runtime: ruby2.7
22
+ MemorySize: 512
23
+ Timeout: 30
24
+ Environment:
25
+ Variables:
26
+ RAILS_ENV: !Ref RailsEnv
27
+ FunctionName: !Join [ '', [ 'APPNAMEHERE-', !Ref RailsEnv, '-', !Ref 'AWS::Region' ] ]
28
+ Events:
29
+ RailsApiProxy:
30
+ Type: HttpApi
31
+ Properties:
32
+ ApiId: !Ref RailsApi
33
+ AutoPublishAlias: live
34
+
35
+ RailsApi:
36
+ Type: AWS::Serverless::HttpApi
37
+ Properties:
38
+ StageName: !Ref RailsEnv
39
+
40
+ Outputs:
41
+
42
+ RailsApiUrl:
43
+ Description: API Gateway Endpoint
44
+ Value: !Sub "https://${RailsApi}.execute-api.${AWS::Region}.amazonaws.com/${RailsEnv}/"
45
+
46
+ RailsFunctionArn:
47
+ Description: Lambda ARN
48
+ Value: !GetAtt RailsFunction.Arn
@@ -0,0 +1,25 @@
1
+ $LAMBY_INSTALLER_NAME = 'rest'
2
+ load 'lamby/templates/shared.rb'
3
+
4
+ say '==> Running Lamby API Gateway (REST API) installer...'
5
+
6
+ say 'Copying files...'
7
+ copy_file tpl_file('app.rb'), app_file('app.rb')
8
+ copy_file tpl_file('template.yaml'), app_file('template.yaml')
9
+ gsub_file app_file('template.yaml'), /APPNAMEHERE/, appname
10
+
11
+ say 'Adding to .gitignore...'
12
+ FileUtils.touch app_file('.gitignore')
13
+ append_to_file app_file('.gitignore'), <<-GITIGNORE.strip_heredoc
14
+ # Lamby
15
+ /.aws-sam
16
+ GITIGNORE
17
+
18
+ say 'Creating ./bin files for build and deploy...'
19
+ copy_file tpl_file('build'), app_file('bin/build')
20
+ chmod app_file('bin/build'), 0755
21
+ copy_file tpl_file('deploy'), app_file('bin/deploy')
22
+ chmod app_file('bin/deploy'), 0755
23
+ gsub_file app_file('bin/deploy'), /APPNAMEHERE/, appname.downcase
24
+
25
+ say 'Welcome to AWS Lambda and Rails 🎉', :green
@@ -0,0 +1,10 @@
1
+ require_relative 'config/boot'
2
+ require 'lamby'
3
+ require_relative 'config/application'
4
+ require_relative 'config/environment'
5
+
6
+ $app = Rack::Builder.new { run Rails.application }.to_app
7
+
8
+ def handler(event:, context:)
9
+ Lamby.handler $app, event, context, rack: :rest
10
+ end