lamby-no-rails 1.0.3

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.python-version +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +17 -0
  6. data/Brewfile +5 -0
  7. data/CHANGELOG.md +109 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +3 -0
  10. data/Gemfile.lock +168 -0
  11. data/LICENSE.txt +21 -0
  12. data/Pipfile +13 -0
  13. data/Pipfile.lock +370 -0
  14. data/README.md +37 -0
  15. data/Rakefile +12 -0
  16. data/bin/bootstrap +20 -0
  17. data/bin/console +14 -0
  18. data/bin/setup +6 -0
  19. data/bin/test +5 -0
  20. data/bin/update +7 -0
  21. data/lamby.gemspec +29 -0
  22. data/lib/lamby.rb +25 -0
  23. data/lib/lamby/debug.rb +46 -0
  24. data/lib/lamby/handler.rb +71 -0
  25. data/lib/lamby/logger.rb +23 -0
  26. data/lib/lamby/rack.rb +85 -0
  27. data/lib/lamby/rack_alb.rb +73 -0
  28. data/lib/lamby/rack_api.rb +36 -0
  29. data/lib/lamby/railtie.rb +12 -0
  30. data/lib/lamby/sam_helpers.rb +9 -0
  31. data/lib/lamby/ssm_parameter_store.rb +154 -0
  32. data/lib/lamby/tasks.rake +15 -0
  33. data/lib/lamby/templates.rake +36 -0
  34. data/lib/lamby/templates/api_gateway.rb +25 -0
  35. data/lib/lamby/templates/api_gateway/app.rb +10 -0
  36. data/lib/lamby/templates/api_gateway/build +23 -0
  37. data/lib/lamby/templates/api_gateway/deploy +22 -0
  38. data/lib/lamby/templates/api_gateway/template.yaml +84 -0
  39. data/lib/lamby/templates/application_load_balancer.rb +25 -0
  40. data/lib/lamby/templates/application_load_balancer/app.rb +10 -0
  41. data/lib/lamby/templates/application_load_balancer/build +23 -0
  42. data/lib/lamby/templates/application_load_balancer/deploy +39 -0
  43. data/lib/lamby/templates/application_load_balancer/template.yaml +90 -0
  44. data/lib/lamby/templates/shared.rb +15 -0
  45. data/lib/lamby/version.rb +3 -0
  46. metadata +199 -0
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ bundle install
6
+ pipenv install
data/bin/test ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ bundle exec rake test
data/bin/update ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ pipenv --rm
6
+
7
+ ./bin/setup
data/lamby.gemspec ADDED
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lamby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "lamby-no-rails"
8
+ spec.version = Lamby::VERSION
9
+ spec.authors = ["Ken Collins"]
10
+ spec.email = ["kcollins@customink.com"]
11
+ spec.summary = %q{Simple Rails & AWS Lambda Integration using Rack}
12
+ spec.description = %q{Simple Rails & AWS Lambda Integration using Rack and various utilities.}
13
+ spec.homepage = "https://github.com/customink/lamby"
14
+ spec.license = "MIT"
15
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
16
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+ spec.add_dependency 'rack'
22
+ spec.add_development_dependency 'aws-sdk-ssm'
23
+ spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'minitest'
26
+ spec.add_development_dependency 'mocha'
27
+ spec.add_development_dependency 'pry'
28
+ spec.add_development_dependency 'sqlite3'
29
+ end
data/lib/lamby.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'lamby/logger'
2
+ require 'rack'
3
+ require 'base64'
4
+ require 'active_support/all'
5
+ require 'lamby/version'
6
+ require 'lamby/sam_helpers'
7
+ require 'lamby/rack'
8
+ require 'lamby/rack_alb'
9
+ require 'lamby/rack_api'
10
+ require 'lamby/debug'
11
+ require 'lamby/handler'
12
+ require 'rails/railtie'
13
+ require 'lamby/railtie'
14
+
15
+ module Lamby
16
+
17
+ extend self
18
+
19
+ def handler(app, event, context, options = {})
20
+ Handler.call(app, event, context, options)
21
+ end
22
+
23
+ autoload :SsmParameterStore, 'lamby/ssm_parameter_store'
24
+
25
+ end
@@ -0,0 +1,46 @@
1
+ module Lamby
2
+ module Debug
3
+ include Lamby::SamHelpers
4
+
5
+ extend self
6
+
7
+ def on?(event)
8
+ params = event['multiValueQueryStringParameters'] || event['queryStringParameters']
9
+ (Rails.env.development? || ENV['LAMBY_DEBUG']) && params && params['debug'] == '1'
10
+ end
11
+
12
+ def call(event, context, env)
13
+ [ 200, { 'Content-Type' => 'text/html' }, [body(event, context, env)] ]
14
+ end
15
+
16
+ private
17
+
18
+ def body(event, context, env)
19
+ <<-HTML
20
+ <!DOCTYPE html>
21
+ <html>
22
+ <body>
23
+ <h1>Lamby Debug Response</h1>
24
+ <h2>Event</h2>
25
+ <pre>
26
+ #{JSON.pretty_generate(event)}
27
+ </pre>
28
+ <h2>Rack Env</h2>
29
+ <pre>
30
+ #{JSON.pretty_generate(env)}
31
+ </pre>
32
+ <h2>#{context.class.name}</h2>
33
+ <code>
34
+ #{CGI::escapeHTML(context.inspect)}
35
+ </code>
36
+ <h2>Environment</h2>
37
+ <pre>
38
+ #{sam_local? ? JSON.pretty_generate(ENV.to_h) : 'N/A'}
39
+ </pre>
40
+ </body>
41
+ </html>
42
+ HTML
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,71 @@
1
+ module Lamby
2
+ class Handler
3
+
4
+ class << self
5
+
6
+ def call(app, event, context, options = {})
7
+ new(app, event, context, options).call.response
8
+ end
9
+
10
+ end
11
+
12
+ def initialize(app, event, context, options = {})
13
+ @app = app
14
+ @event = event
15
+ @context = context
16
+ @options = options
17
+ @called = false
18
+ end
19
+
20
+ def response
21
+ { statusCode: status,
22
+ headers: headers,
23
+ body: body }.merge(rack_response)
24
+ end
25
+
26
+ def status
27
+ @status
28
+ end
29
+
30
+ def headers
31
+ @headers
32
+ end
33
+
34
+ def body
35
+ @rbody ||= ''.tap do |rbody|
36
+ @body.each { |part| rbody << part }
37
+ end
38
+ end
39
+
40
+ def call
41
+ return self if @called
42
+ @status, @headers, @body = call_app
43
+ @called = true
44
+ self
45
+ end
46
+
47
+ private
48
+
49
+ def rack
50
+ @rack ||= case @options[:rack]
51
+ when :api
52
+ Lamby::RackApi.new @event, @context
53
+ else
54
+ Lamby::RackAlb.new @event, @context
55
+ end
56
+ end
57
+
58
+ def rack_response
59
+ rack.response(self)
60
+ end
61
+
62
+ def call_app
63
+ if Debug.on?(@event)
64
+ Debug.call @event, @context, rack.env
65
+ else
66
+ @app.call rack.env
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ require 'logger'
2
+
3
+ ENV['RAILS_LOG_TO_STDOUT'] = '1'
4
+
5
+ module Lamby
6
+ module Logger
7
+
8
+ def initialize(*args)
9
+ args[0] = STDOUT
10
+ super(*args)
11
+ end
12
+
13
+ end
14
+ end
15
+
16
+ Logger.prepend Lamby::Logger
17
+
18
+ # TODO: Railtie initializer
19
+ # Rails.application.config.logger = ActiveSupport::TaggedLogging.new(
20
+ # ActiveSupport::Logger.new(STDOUT).tap { |logger|
21
+ # logger.formatter = Rails.application.config.log_formatter
22
+ # }
23
+ # )
data/lib/lamby/rack.rb ADDED
@@ -0,0 +1,85 @@
1
+ module Lamby
2
+ class Rack
3
+
4
+ include SamHelpers
5
+
6
+ LAMBDA_EVENT = 'lambda.event'.freeze
7
+ LAMBDA_CONTEXT = 'lambda.context'.freeze
8
+ HTTP_X_REQUESTID = 'HTTP_X_REQUEST_ID'.freeze
9
+
10
+ attr_reader :event, :context
11
+
12
+ def initialize(event, context)
13
+ @event = event
14
+ @context = context
15
+ end
16
+
17
+ def env
18
+ @env ||= env_base.merge!(env_headers)
19
+ end
20
+
21
+ def response(_handler)
22
+ {}
23
+ end
24
+
25
+ def api?
26
+ false
27
+ end
28
+
29
+ def alb?
30
+ false
31
+ end
32
+
33
+ def multi_value?
34
+ false
35
+ end
36
+
37
+ private
38
+
39
+ def env_base
40
+ raise NotImplementedError
41
+ end
42
+
43
+ def env_headers
44
+ headers.transform_keys do |key|
45
+ "HTTP_#{key.to_s.upcase.tr '-', '_'}"
46
+ end.tap do |hdrs|
47
+ hdrs[HTTP_X_REQUESTID] = request_id
48
+ end
49
+ end
50
+
51
+ def content_type
52
+ headers.delete('Content-Type') || headers.delete('content-type') || headers.delete('CONTENT_TYPE')
53
+ end
54
+
55
+ def content_length
56
+ bytesize = body.bytesize.to_s if body
57
+ headers.delete('Content-Length') || headers.delete('content-length') || headers.delete('CONTENT_LENGTH') || bytesize
58
+ end
59
+
60
+ def body
61
+ @body ||= if event['body'] && base64_encoded?
62
+ Base64.decode64 event['body']
63
+ else
64
+ event['body']
65
+ end
66
+ end
67
+
68
+ def headers
69
+ @headers ||= event['headers'] || {}
70
+ end
71
+
72
+ def query_string
73
+ @query_string ||= event['queryStringParameters'].try(:to_query)
74
+ end
75
+
76
+ def base64_encoded?
77
+ event['isBase64Encoded']
78
+ end
79
+
80
+ def request_id
81
+ context.aws_request_id
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,73 @@
1
+ module Lamby
2
+ class RackAlb < Lamby::Rack
3
+
4
+ def alb?
5
+ true
6
+ end
7
+
8
+ def multi_value?
9
+ event.key? 'multiValueHeaders'
10
+ end
11
+
12
+ def response(handler)
13
+ hhdrs = handler.headers
14
+ multivalue_headers = hhdrs.transform_values { |v| Array.wrap(v) } if multi_value?
15
+ status_description = "#{handler.status} #{::Rack::Utils::HTTP_STATUS_CODES[handler.status]}"
16
+ base64_encode = hhdrs['Content-Transfer-Encoding'] == 'binary' || hhdrs['X-Lamby-Base64'] == '1'
17
+ body = Base64.strict_encode64(handler.body) if base64_encode
18
+ { multiValueHeaders: multivalue_headers,
19
+ statusDescription: status_description,
20
+ isBase64Encoded: base64_encode,
21
+ body: body }.compact
22
+ end
23
+
24
+ private
25
+
26
+ def env_base
27
+ { ::Rack::REQUEST_METHOD => event['httpMethod'],
28
+ ::Rack::SCRIPT_NAME => '',
29
+ ::Rack::PATH_INFO => event['path'] || '',
30
+ ::Rack::QUERY_STRING => query_string,
31
+ ::Rack::SERVER_NAME => headers['host'],
32
+ ::Rack::SERVER_PORT => headers['x-forwarded-port'],
33
+ ::Rack::SERVER_PROTOCOL => 'HTTP/1.1',
34
+ ::Rack::RACK_VERSION => ::Rack::VERSION,
35
+ ::Rack::RACK_URL_SCHEME => headers['x-forwarded-proto'],
36
+ ::Rack::RACK_INPUT => StringIO.new(body || ''),
37
+ ::Rack::RACK_ERRORS => $stderr,
38
+ ::Rack::RACK_MULTITHREAD => false,
39
+ ::Rack::RACK_MULTIPROCESS => false,
40
+ ::Rack::RACK_RUNONCE => false,
41
+ LAMBDA_EVENT => event,
42
+ LAMBDA_CONTEXT => context
43
+ }.tap do |env|
44
+ ct = content_type
45
+ cl = content_length
46
+ env['CONTENT_TYPE'] = ct if ct
47
+ env['CONTENT_LENGTH'] = cl if cl
48
+ end
49
+ end
50
+
51
+ def headers
52
+ @headers ||= multi_value? ? headers_multi : super
53
+ end
54
+
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('&')
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ module Lamby
2
+ class RackApi < Lamby::Rack
3
+
4
+ def api?
5
+ true
6
+ end
7
+
8
+ private
9
+
10
+ def env_base
11
+ { ::Rack::REQUEST_METHOD => event['httpMethod'],
12
+ ::Rack::SCRIPT_NAME => '',
13
+ ::Rack::PATH_INFO => event['path'] || '',
14
+ ::Rack::QUERY_STRING => query_string,
15
+ ::Rack::SERVER_NAME => headers['Host'],
16
+ ::Rack::SERVER_PORT => headers['X-Forwarded-Port'],
17
+ ::Rack::SERVER_PROTOCOL => event.dig('requestContext', 'protocol') || 'HTTP/1.1',
18
+ ::Rack::RACK_VERSION => ::Rack::VERSION,
19
+ ::Rack::RACK_URL_SCHEME => 'https',
20
+ ::Rack::RACK_INPUT => StringIO.new(body || ''),
21
+ ::Rack::RACK_ERRORS => $stderr,
22
+ ::Rack::RACK_MULTITHREAD => false,
23
+ ::Rack::RACK_MULTIPROCESS => false,
24
+ ::Rack::RACK_RUNONCE => false,
25
+ LAMBDA_EVENT => event,
26
+ LAMBDA_CONTEXT => context
27
+ }.tap do |env|
28
+ ct = content_type
29
+ cl = content_length
30
+ env['CONTENT_TYPE'] = ct if ct
31
+ env['CONTENT_LENGTH'] = cl if cl
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ module Lamby
2
+ class Railtie < ::Rails::Railtie
3
+
4
+ config.lamby = ActiveSupport::OrderedOptions.new
5
+
6
+ rake_tasks do
7
+ load 'lamby/tasks.rake'
8
+ load 'lamby/templates.rake'
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Lamby
2
+ module SamHelpers
3
+
4
+ def sam_local?
5
+ ENV['AWS_SAM_LOCAL'] == 'true'
6
+ end
7
+
8
+ end
9
+ end