lamby 1.0.2 → 2.2.1

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 (52) 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 +58 -0
  6. data/Dockerfile +12 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +83 -84
  9. data/README.md +9 -3
  10. data/Rakefile +1 -0
  11. data/bin/_setup +6 -0
  12. data/bin/_test +6 -0
  13. data/bin/bootstrap +3 -17
  14. data/bin/setup +3 -3
  15. data/bin/test +6 -3
  16. data/bin/update +2 -2
  17. data/docker-compose.yml +14 -0
  18. data/lamby.gemspec +1 -2
  19. data/lib/lamby.rb +7 -4
  20. data/lib/lamby/debug.rb +8 -1
  21. data/lib/lamby/handler.rb +16 -3
  22. data/lib/lamby/logger.rb +13 -9
  23. data/lib/lamby/rack.rb +21 -9
  24. data/lib/lamby/rack_alb.rb +12 -15
  25. data/lib/lamby/rack_http.rb +84 -0
  26. data/lib/lamby/{rack_api.rb → rack_rest.rb} +7 -3
  27. data/lib/lamby/ssm_parameter_store.rb +8 -7
  28. data/lib/lamby/templates.rake +4 -3
  29. data/lib/lamby/templates/{application_load_balancer.rb → alb.rb} +2 -2
  30. data/lib/lamby/templates/{application_load_balancer → alb}/app.rb +0 -0
  31. data/lib/lamby/templates/{application_load_balancer → alb}/build +1 -1
  32. data/lib/lamby/templates/{application_load_balancer → alb}/deploy +1 -1
  33. data/lib/lamby/templates/{application_load_balancer → alb}/template.yaml +1 -1
  34. data/lib/lamby/templates/{api_gateway.rb → http.rb} +3 -3
  35. data/lib/lamby/templates/{api_gateway → http}/app.rb +1 -1
  36. data/lib/lamby/templates/{api_gateway → http}/build +1 -1
  37. data/lib/lamby/templates/{api_gateway → http}/deploy +1 -1
  38. data/lib/lamby/templates/http/template.yaml +48 -0
  39. data/lib/lamby/templates/rest.rb +25 -0
  40. data/lib/lamby/templates/rest/app.rb +10 -0
  41. data/lib/lamby/templates/rest/build +23 -0
  42. data/lib/lamby/templates/rest/deploy +22 -0
  43. data/lib/lamby/templates/{api_gateway → rest}/template.yaml +2 -2
  44. data/lib/lamby/templates/shared.rb +3 -1
  45. data/lib/lamby/version.rb +1 -1
  46. data/vendor/.keep +0 -0
  47. metadata +29 -37
  48. data/.python-version +0 -1
  49. data/.travis.yml +0 -17
  50. data/Brewfile +0 -5
  51. data/Pipfile +0 -13
  52. data/Pipfile.lock +0 -370
data/bin/setup CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
-
3
2
  set -e
4
3
 
5
- bundle install
6
- pipenv install
4
+ docker-compose run \
5
+ cicd \
6
+ ./bin/_setup
data/bin/test CHANGED
@@ -1,5 +1,8 @@
1
- #!/usr/bin/env bash
2
-
1
+ #!/bin/bash
3
2
  set -e
4
3
 
5
- bundle exec rake test
4
+ export RAILS_ENV="test"
5
+
6
+ docker-compose run \
7
+ cicd \
8
+ ./bin/_test
data/bin/update CHANGED
@@ -1,7 +1,7 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env bash
2
2
 
3
3
  set -e
4
4
 
5
- pipenv --rm
5
+ rm -rf ./vendor/bundle
6
6
 
7
7
  ./bin/setup
@@ -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
@@ -19,12 +19,11 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
  spec.add_dependency 'rack'
22
- spec.add_dependency 'rails'
23
22
  spec.add_development_dependency 'aws-sdk-ssm'
24
23
  spec.add_development_dependency 'bundler'
25
24
  spec.add_development_dependency 'rake'
26
25
  spec.add_development_dependency 'minitest'
26
+ spec.add_development_dependency 'minitest-focus'
27
27
  spec.add_development_dependency 'mocha'
28
28
  spec.add_development_dependency 'pry'
29
- spec.add_development_dependency 'sqlite3'
30
29
  end
@@ -1,16 +1,19 @@
1
1
  require 'lamby/logger'
2
2
  require 'rack'
3
3
  require 'base64'
4
- require 'active_support/all'
5
4
  require 'lamby/version'
6
5
  require 'lamby/sam_helpers'
7
6
  require 'lamby/rack'
8
7
  require 'lamby/rack_alb'
9
- require 'lamby/rack_api'
8
+ require 'lamby/rack_rest'
9
+ require 'lamby/rack_http'
10
10
  require 'lamby/debug'
11
11
  require 'lamby/handler'
12
- require 'rails/railtie'
13
- require 'lamby/railtie'
12
+
13
+ if defined?(Rails)
14
+ require 'rails/railtie'
15
+ require 'lamby/railtie'
16
+ end
14
17
 
15
18
  module Lamby
16
19
 
@@ -6,7 +6,7 @@ module Lamby
6
6
 
7
7
  def on?(event)
8
8
  params = event['multiValueQueryStringParameters'] || event['queryStringParameters']
9
- (Rails.env.development? || ENV['LAMBY_DEBUG']) && params && params['debug'] == '1'
9
+ (development? || ENV['LAMBY_DEBUG']) && params && params['debug'] == '1'
10
10
  end
11
11
 
12
12
  def call(event, context, env)
@@ -42,5 +42,12 @@ module Lamby
42
42
  HTML
43
43
  end
44
44
 
45
+ def development?
46
+ ENV
47
+ .slice('RACK_ENV', 'RAILS_ENV')
48
+ .values
49
+ .any? { |v| v.to_s.casecmp('development').zero? }
50
+ end
51
+
45
52
  end
46
53
  end
@@ -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,26 @@ 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
+ build_query_string
75
+ end
76
+ end
77
+
78
+ def build_query_string
79
+ return if event['queryStringParameters'].nil?
80
+
81
+ Rack::Utils.build_nested_query(
82
+ event.fetch('queryStringParameters')
83
+ )
84
+ .gsub('[', '%5B')
85
+ .gsub(']', '%5D')
74
86
  end
75
87
 
76
88
  def base64_encoded?
@@ -11,7 +11,7 @@ module Lamby
11
11
 
12
12
  def response(handler)
13
13
  hhdrs = handler.headers
14
- multivalue_headers = hhdrs.transform_values { |v| Array.wrap(v) } if multi_value?
14
+ multivalue_headers = hhdrs.transform_values { |v| Array[v].compact.flatten } if multi_value?
15
15
  status_description = "#{handler.status} #{::Rack::Utils::HTTP_STATUS_CODES[handler.status]}"
16
16
  base64_encode = hhdrs['Content-Transfer-Encoding'] == 'binary' || hhdrs['X-Lamby-Base64'] == '1'
17
17
  body = Base64.strict_encode64(handler.body) if base64_encode
@@ -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,84 @@
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 cookies.any?
42
+ hdrs[HTTP_COOKIE] = 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
+ def cookies
52
+ event['cookies'] || []
53
+ end
54
+
55
+ # Using custom domain names with v1.0 yields a good `path` parameter sans
56
+ # stage. However, v2.0 and others do not. So we are just going to remove stage
57
+ # no matter waht from other places for both.
58
+ #
59
+ def path_info
60
+ stage = event.dig('requestContext', 'stage')
61
+ spath = event.dig('requestContext', 'http', 'path') || event.dig('requestContext', 'path')
62
+ spath.sub /\A\/#{stage}/, ''
63
+ end
64
+
65
+ def server_name
66
+ headers['x-forwarded-host'] ||
67
+ headers['X-Forwarded-Host'] ||
68
+ headers['host'] ||
69
+ headers['Host']
70
+ end
71
+
72
+ def server_port
73
+ headers['x-forwarded-port'] || headers['X-Forwarded-Port']
74
+ end
75
+
76
+ def server_protocol
77
+ event.dig('requestContext', 'http', 'protocol') ||
78
+ event.dig('requestContext', 'protocol') ||
79
+ 'HTTP/1.1'
80
+ end
81
+
82
+
83
+ end
84
+ 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
@@ -16,12 +16,13 @@ module Lamby
16
16
  end
17
17
 
18
18
  def get!(path)
19
- parts = path.from(1).split('/')
19
+ parts = path[1..-1].split('/')
20
20
  env = parts.pop
21
21
  path = "/#{parts.join('/')}"
22
- new(path).get!.params.detect do |p|
22
+ param = new(path).get!.params.detect do |p|
23
23
  p.env == env
24
- end.try(:value)
24
+ end
25
+ param&.value
25
26
  end
26
27
 
27
28
  end
@@ -44,7 +45,7 @@ module Lamby
44
45
 
45
46
  def get!
46
47
  get_all!
47
- get_history! if label.present?
48
+ get_history! unless label.to_s.empty?
48
49
  self
49
50
  end
50
51
 
@@ -68,7 +69,7 @@ module Lamby
68
69
 
69
70
  def dotenv_contents
70
71
  params.each_with_object('') do |param, contents|
71
- line = "export #{param.env}=#{param.value}\n"
72
+ line = "#{param.env}=#{param.value}\n"
72
73
  contents << line
73
74
  end
74
75
  end
@@ -98,7 +99,7 @@ module Lamby
98
99
  with_decryption: true,
99
100
  max_results: MAX_RESULTS
100
101
  }.tap { |options|
101
- token = @all_response.try(:next_token)
102
+ token = @all_response&.next_token
102
103
  options[:next_token] = token if token
103
104
  }
104
105
  end
@@ -137,7 +138,7 @@ module Lamby
137
138
  with_decryption: true,
138
139
  max_results: MAX_RESULTS
139
140
  }.tap { |options|
140
- token = @hist_response.try(:next_token)
141
+ token = @hist_response&.next_token
141
142
  options[:next_token] = token if token
142
143
  }
143
144
  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...'
@@ -10,7 +10,7 @@ gsub_file app_file('template.yaml'), /APPNAMEHERE/, appname
10
10
 
11
11
  say 'Adding to .gitignore...'
12
12
  FileUtils.touch app_file('.gitignore')
13
- append_to_file app_file('.gitignore'), <<-GITIGNORE.strip_heredoc
13
+ append_to_file app_file('.gitignore'), <<~GITIGNORE
14
14
  # Lamby
15
15
  /.aws-sam
16
16
  GITIGNORE