shark-on-lambda 1.0.0.rc4 → 2.0.0.rc3
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -3
- data/.rubocop.yml +4 -0
- data/README.md +70 -17
- data/changelog.md +19 -1
- data/doc/upgrade-from-0.6.x-to-1.x.md +122 -0
- data/lib/shark-on-lambda.rb +9 -0
- data/lib/shark_on_lambda.rb +14 -54
- data/lib/shark_on_lambda/api_gateway_handler.rb +3 -55
- data/lib/shark_on_lambda/application.rb +75 -5
- data/lib/shark_on_lambda/base_controller.rb +29 -9
- data/lib/shark_on_lambda/cacheable.rb +21 -0
- data/lib/shark_on_lambda/configuration.rb +1 -65
- data/lib/shark_on_lambda/inferrers/serializer_inferrer.rb +10 -7
- data/lib/shark_on_lambda/jsonapi_renderer.rb +16 -10
- data/lib/shark_on_lambda/middleware/base.rb +2 -4
- data/lib/shark_on_lambda/middleware/honeybadger.rb +14 -9
- data/lib/shark_on_lambda/middleware/jsonapi_rescuer.rb +21 -1
- data/lib/shark_on_lambda/middleware/lambda_logger.rb +8 -16
- data/lib/shark_on_lambda/rake_tasks.rb +16 -0
- data/lib/shark_on_lambda/request.rb +0 -3
- data/lib/shark_on_lambda/rspec/env_builder.rb +71 -34
- data/lib/shark_on_lambda/rspec/helpers.rb +5 -87
- data/lib/shark_on_lambda/rspec/request_helpers.rb +63 -0
- data/lib/shark_on_lambda/rspec/{jsonapi_helpers.rb → response_helpers.rb} +4 -10
- data/lib/shark_on_lambda/version.rb +1 -1
- data/shark-on-lambda.gemspec +7 -5
- metadata +32 -37
- data/lib/shark_on_lambda/concerns/resettable_singleton.rb +0 -18
- data/lib/shark_on_lambda/concerns/yaml_config_loader.rb +0 -28
- data/lib/shark_on_lambda/dispatcher.rb +0 -26
- data/lib/shark_on_lambda/inferrers/name_inferrer.rb +0 -66
- data/lib/shark_on_lambda/jsonapi_controller.rb +0 -29
- data/lib/shark_on_lambda/middleware/rescuer.rb +0 -37
- data/lib/shark_on_lambda/query.rb +0 -67
- data/lib/shark_on_lambda/rack_adapters/api_gateway.rb +0 -128
- data/lib/shark_on_lambda/secrets.rb +0 -43
@@ -2,7 +2,15 @@
|
|
2
2
|
|
3
3
|
module SharkOnLambda
|
4
4
|
module Middleware
|
5
|
-
class JsonapiRescuer <
|
5
|
+
class JsonapiRescuer < Base
|
6
|
+
def call!(env)
|
7
|
+
app.call(env)
|
8
|
+
rescue Errors::Base => e
|
9
|
+
rescue_shark_error(e)
|
10
|
+
rescue StandardError => e
|
11
|
+
rescue_standard_error(e)
|
12
|
+
end
|
13
|
+
|
6
14
|
private
|
7
15
|
|
8
16
|
def error_object(status, message)
|
@@ -26,6 +34,18 @@ module SharkOnLambda
|
|
26
34
|
|
27
35
|
[status, headers, response_body]
|
28
36
|
end
|
37
|
+
|
38
|
+
def rescue_shark_error(error)
|
39
|
+
status = error.status || 500
|
40
|
+
error_response(status, {}, error.message)
|
41
|
+
end
|
42
|
+
|
43
|
+
def rescue_standard_error(error)
|
44
|
+
SharkOnLambda.logger.error(error.message)
|
45
|
+
SharkOnLambda.logger.error(error.backtrace.join("\n"))
|
46
|
+
|
47
|
+
error_response(500, {}, error.message)
|
48
|
+
end
|
29
49
|
end
|
30
50
|
end
|
31
51
|
end
|
@@ -10,37 +10,34 @@ module SharkOnLambda
|
|
10
10
|
@logger = logger
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
def _call(env)
|
13
|
+
def call!(env)
|
16
14
|
start_time = Time.now
|
17
15
|
response = app.call(env)
|
18
|
-
|
16
|
+
duration = duration_in_ms(start_time, Time.now)
|
19
17
|
|
20
18
|
if logger.info?
|
21
|
-
log_request(env: env,
|
22
|
-
response: response,
|
23
|
-
start_time: start_time,
|
24
|
-
end_time: end_time)
|
19
|
+
log_request(env: env, response: response, duration: duration)
|
25
20
|
end
|
26
21
|
|
27
22
|
response
|
28
23
|
end
|
29
24
|
|
25
|
+
private
|
26
|
+
|
30
27
|
def body_size(body)
|
31
28
|
size = 0
|
32
29
|
body.each { |chunk| size += chunk.bytesize }
|
33
30
|
size
|
34
31
|
end
|
35
32
|
|
36
|
-
def log_request(env:, response:,
|
33
|
+
def log_request(env:, response:, duration:)
|
37
34
|
log_object = {
|
38
35
|
url: env['PATH_INFO'],
|
39
36
|
method: env['REQUEST_METHOD'],
|
40
|
-
params:
|
37
|
+
params: env.fetch('action_dispatch.request.parameters', {}),
|
41
38
|
status: response[0],
|
42
39
|
length: body_size(response[2]),
|
43
|
-
duration: "#{
|
40
|
+
duration: "#{duration} ms"
|
44
41
|
}
|
45
42
|
logger.info log_object.to_json
|
46
43
|
end
|
@@ -49,11 +46,6 @@ module SharkOnLambda
|
|
49
46
|
duration = (end_time - start_time) * 1000
|
50
47
|
duration.abs.floor(3)
|
51
48
|
end
|
52
|
-
|
53
|
-
def params(env)
|
54
|
-
query_params = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
55
|
-
query_params.merge(env['shark.path_parameters'] || {})
|
56
|
-
end
|
57
49
|
end
|
58
50
|
end
|
59
51
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_dispatch/routing/inspector'
|
4
|
+
require 'rake'
|
5
|
+
require 'shark_on_lambda'
|
6
|
+
|
7
|
+
namespace :'shark-on-lambda' do
|
8
|
+
desc 'Print out all defined routes in match order, with names'
|
9
|
+
task :routes do
|
10
|
+
routes = SharkOnLambda.application.routes.routes
|
11
|
+
inspector = ActionDispatch::Routing::RoutesInspector.new(routes)
|
12
|
+
formatter = ActionDispatch::Routing::ConsoleFormatter::Sheet.new
|
13
|
+
|
14
|
+
puts inspector.format(formatter)
|
15
|
+
end
|
16
|
+
end
|
@@ -3,23 +3,14 @@
|
|
3
3
|
module SharkOnLambda
|
4
4
|
module RSpec
|
5
5
|
class EnvBuilder
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(options = {})
|
9
|
-
@method = options.fetch(:method).to_s.upcase
|
10
|
-
@controller = options.fetch(:controller)
|
11
|
-
@action = options.fetch(:action)
|
12
|
-
|
13
|
-
@headers = (options[:headers] || {}).deep_stringify_keys
|
14
|
-
@headers.transform_keys!(&:downcase)
|
15
|
-
@params = options[:params]
|
16
|
-
|
17
|
-
initialize_env
|
18
|
-
add_headers
|
19
|
-
add_request_body
|
6
|
+
def initialize(**options)
|
7
|
+
@options = options
|
20
8
|
end
|
21
9
|
|
22
10
|
def build
|
11
|
+
initialize_env
|
12
|
+
add_headers
|
13
|
+
add_request_body_as_json if body? && jsonable_params? && json_request?
|
23
14
|
env.deep_stringify_keys
|
24
15
|
end
|
25
16
|
|
@@ -27,42 +18,88 @@ module SharkOnLambda
|
|
27
18
|
|
28
19
|
attr_reader :env
|
29
20
|
|
30
|
-
def
|
31
|
-
|
32
|
-
key = case name
|
33
|
-
when 'CONTENT_LENGTH', 'CONTENT_TYPE' then name
|
34
|
-
else "HTTP_#{name}"
|
35
|
-
end
|
36
|
-
@env[key] = value.to_s
|
21
|
+
def action
|
22
|
+
@options.fetch(:action)
|
37
23
|
end
|
38
24
|
|
39
25
|
def add_headers
|
40
|
-
headers.each_pair
|
26
|
+
headers.each_pair do |name, value|
|
27
|
+
name = name.upcase.tr('-', '_')
|
28
|
+
key = case name
|
29
|
+
when 'CONTENT_LENGTH', 'CONTENT_TYPE' then name
|
30
|
+
else "HTTP_#{name}"
|
31
|
+
end
|
32
|
+
env[key] = value.to_s
|
33
|
+
end
|
41
34
|
end
|
42
35
|
|
43
|
-
def
|
44
|
-
return if %w[GET HEAD OPTIONS].include?(env['REQUEST_METHOD'])
|
45
|
-
return unless params.is_a?(Hash)
|
46
|
-
|
36
|
+
def add_request_body_as_json
|
47
37
|
body = params.to_json
|
48
38
|
|
49
39
|
env['rack.input'] = StringIO.new(body).set_encoding(Encoding::BINARY)
|
50
|
-
|
40
|
+
env['CONTENT_TYPE'] = headers['content-type']
|
41
|
+
env['CONTENT_LENGTH'] = env['rack.input'].length.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def as
|
45
|
+
@options.fetch(:as, :json)
|
46
|
+
end
|
47
|
+
|
48
|
+
def body?
|
49
|
+
!%w[GET HEAD OPTIONS].include?(env['REQUEST_METHOD'])
|
51
50
|
end
|
52
51
|
|
53
52
|
def initialize_env
|
54
53
|
@env = Rack::MockRequest.env_for(
|
55
|
-
|
54
|
+
request_uri.to_s,
|
56
55
|
method: method,
|
57
|
-
params: params
|
58
|
-
'shark.controller' => controller,
|
59
|
-
'shark.action' => action
|
56
|
+
params: params
|
60
57
|
)
|
61
58
|
end
|
62
59
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
60
|
+
def controller
|
61
|
+
@options.fetch(:controller, nil)
|
62
|
+
end
|
63
|
+
|
64
|
+
def headers
|
65
|
+
return @headers if defined?(@headers)
|
66
|
+
|
67
|
+
@headers = @options.fetch(:headers, {}).deep_stringify_keys
|
68
|
+
@headers.transform_keys!(&:downcase)
|
69
|
+
@headers
|
70
|
+
end
|
71
|
+
|
72
|
+
def json_request?
|
73
|
+
as == :json
|
74
|
+
end
|
75
|
+
|
76
|
+
def jsonable_params?
|
77
|
+
params.is_a?(Hash)
|
78
|
+
end
|
79
|
+
|
80
|
+
def method
|
81
|
+
@options.fetch(:method).to_s.upcase
|
82
|
+
end
|
83
|
+
|
84
|
+
def params
|
85
|
+
@options.fetch(:params, {}).deep_stringify_keys
|
86
|
+
end
|
87
|
+
|
88
|
+
def path_from_routes
|
89
|
+
path_params = {
|
90
|
+
controller: controller.name.underscore.sub(/_controller$/, ''),
|
91
|
+
action: action,
|
92
|
+
only_path: true
|
93
|
+
}
|
94
|
+
url = SharkOnLambda.application.routes.url_for(path_params, nil)
|
95
|
+
URI.parse(url).path
|
96
|
+
end
|
97
|
+
|
98
|
+
def request_uri
|
99
|
+
return @request_uri if defined?(@request_uri)
|
100
|
+
|
101
|
+
path = action.is_a?(String) ? action : path_from_routes
|
102
|
+
@request_uri = URI.join('https://localhost:9292', path)
|
66
103
|
end
|
67
104
|
end
|
68
105
|
end
|
@@ -3,94 +3,12 @@
|
|
3
3
|
module SharkOnLambda
|
4
4
|
module RSpec
|
5
5
|
module Helpers
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def get(controller_method, options = {})
|
11
|
-
make_request('GET', controller_method, options)
|
12
|
-
end
|
13
|
-
|
14
|
-
def patch(controller_method, options = {})
|
15
|
-
make_request('PATCH', controller_method, options)
|
16
|
-
end
|
17
|
-
|
18
|
-
def post(controller_method, options = {})
|
19
|
-
make_request('POST', controller_method, options)
|
20
|
-
end
|
21
|
-
|
22
|
-
def put(controller_method, options = {})
|
23
|
-
make_request('PUT', controller_method, options)
|
24
|
-
end
|
25
|
-
|
26
|
-
def response
|
27
|
-
if @response.nil?
|
28
|
-
raise 'You must make a request before you can request a response.'
|
29
|
-
end
|
30
|
-
|
31
|
-
@response
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def build_env(method, action, options = {})
|
37
|
-
env_builder = EnvBuilder.new(
|
38
|
-
method: method,
|
39
|
-
controller: controller_name,
|
40
|
-
action: action,
|
41
|
-
headers: options[:headers],
|
42
|
-
params: options[:params]
|
43
|
-
)
|
44
|
-
env_builder.build
|
45
|
-
end
|
46
|
-
|
47
|
-
def controller?
|
48
|
-
controller_name.present?
|
49
|
-
end
|
50
|
-
|
51
|
-
def controller_name
|
52
|
-
self.class.ancestors.find do |klass|
|
53
|
-
klass.name.end_with?('Controller')
|
54
|
-
end&.description
|
55
|
-
end
|
56
|
-
|
57
|
-
def default_content_type
|
58
|
-
'application/json'
|
59
|
-
end
|
60
|
-
|
61
|
-
def dispatch_request(env, skip_middleware: false)
|
62
|
-
return SharkOnLambda.application.call(env) unless skip_middleware
|
63
|
-
|
64
|
-
controller_class = env['shark.controller'].constantize
|
65
|
-
action = env['shark.action']
|
66
|
-
|
67
|
-
request = Request.new(env)
|
68
|
-
response = Response.new
|
69
|
-
controller_class.dispatch(action, request, response)
|
70
|
-
response.prepare!
|
71
|
-
end
|
72
|
-
|
73
|
-
def headers_with_content_type(headers)
|
74
|
-
headers ||= {}
|
75
|
-
headers.transform_keys! { |key| key.to_s.downcase }
|
76
|
-
headers['content-type'] ||= default_content_type
|
77
|
-
headers
|
78
|
-
end
|
79
|
-
|
80
|
-
def make_request(method, action, options = {})
|
81
|
-
raise ArgumentError, 'Cannot find controller name.' unless controller?
|
82
|
-
|
83
|
-
options = options.with_indifferent_access
|
84
|
-
options[:headers] = headers_with_content_type(options[:headers])
|
85
|
-
|
86
|
-
env = build_env(method, action, options)
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include RequestHelpers
|
8
|
+
include ResponseHelpers
|
87
9
|
|
88
|
-
|
89
|
-
|
90
|
-
skip_middleware: options[:skip_middleware]
|
91
|
-
)
|
92
|
-
errors = env['rack.errors']
|
93
|
-
@response = Rack::MockResponse.new(status, headers, body, errors)
|
10
|
+
included do
|
11
|
+
include SharkOnLambda.application.routes.url_helpers
|
94
12
|
end
|
95
13
|
end
|
96
14
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SharkOnLambda
|
4
|
+
module RSpec
|
5
|
+
module RequestHelpers
|
6
|
+
attr_writer :app
|
7
|
+
|
8
|
+
SUPPORTED_HTTP_METHODS = %w[
|
9
|
+
DELETE GET HEAD OPTIONS PATCH POST PUT
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
SUPPORTED_HTTP_METHODS.each do |http_method|
|
13
|
+
define_method(http_method.underscore) do |action, **options|
|
14
|
+
make_request(http_method, action, **options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def app
|
19
|
+
@app ||= SharkOnLambda.application
|
20
|
+
end
|
21
|
+
|
22
|
+
def response
|
23
|
+
if @response.nil?
|
24
|
+
raise 'You must make a request before you can request a response.'
|
25
|
+
end
|
26
|
+
|
27
|
+
@response
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def build_env(method, action, **options)
|
33
|
+
headers = options.fetch(:headers, {})
|
34
|
+
env_builder = EnvBuilder.new(
|
35
|
+
method: method,
|
36
|
+
controller: described_class,
|
37
|
+
action: action,
|
38
|
+
headers: normalized_headers(headers),
|
39
|
+
params: options.fetch(:params, {})
|
40
|
+
)
|
41
|
+
env_builder.build
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_content_type
|
45
|
+
'application/vnd.api+json'
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_request(method, action, **options)
|
49
|
+
env = build_env(method, action, **options)
|
50
|
+
|
51
|
+
status, headers, body = app.call(env)
|
52
|
+
errors = env['rack.errors']
|
53
|
+
@response = Rack::MockResponse.new(status, headers, body, errors)
|
54
|
+
end
|
55
|
+
|
56
|
+
def normalized_headers(headers)
|
57
|
+
headers.transform_keys! { |key| key.to_s.downcase }
|
58
|
+
headers['content-type'] ||= default_content_type
|
59
|
+
headers
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -2,27 +2,21 @@
|
|
2
2
|
|
3
3
|
module SharkOnLambda
|
4
4
|
module RSpec
|
5
|
-
module
|
6
|
-
include Helpers
|
7
|
-
|
5
|
+
module ResponseHelpers
|
8
6
|
def jsonapi_attributes
|
9
|
-
jsonapi_data
|
7
|
+
jsonapi_data.fetch(:attributes, {})
|
10
8
|
end
|
11
9
|
|
12
10
|
def jsonapi_data
|
13
|
-
parsed_body
|
11
|
+
parsed_body.fetch(:data, {})
|
14
12
|
end
|
15
13
|
|
16
14
|
def jsonapi_errors
|
17
|
-
parsed_body
|
15
|
+
parsed_body.fetch(:errors, [])
|
18
16
|
end
|
19
17
|
|
20
18
|
private
|
21
19
|
|
22
|
-
def default_content_type
|
23
|
-
'application/vnd.api+json'
|
24
|
-
end
|
25
|
-
|
26
20
|
def parsed_body
|
27
21
|
@parsed_body ||= JSON.parse(response.body).with_indifferent_access
|
28
22
|
end
|