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
@@ -1,61 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SharkOnLambda
|
4
|
-
class ApiGatewayHandler
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def controller_action?(action)
|
9
|
-
controller_actions.include?(action.to_sym)
|
10
|
-
end
|
11
|
-
|
12
|
-
def controller_class_name
|
13
|
-
return @controller_class_name if defined?(@controller_class_name)
|
14
|
-
|
15
|
-
name_inferrer = Inferrers::NameInferrer.from_handler_name(name)
|
16
|
-
@controller_class_name = name_inferrer.controller
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def controller_actions
|
22
|
-
return [] if controller_class.nil?
|
23
|
-
|
24
|
-
controller_class.public_instance_methods(false)
|
25
|
-
end
|
26
|
-
|
27
|
-
def controller_class
|
28
|
-
controller_class_name.safe_constantize
|
29
|
-
end
|
30
|
-
|
31
|
-
def method_missing(action, *args, &_block)
|
32
|
-
return super unless respond_to_missing?(action)
|
33
|
-
|
34
|
-
new.call(action, *args)
|
35
|
-
end
|
36
|
-
|
37
|
-
def respond_to_missing?(name, _include_all = false)
|
38
|
-
controller_action?(name)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
attr_reader :application, :env
|
43
|
-
delegate :context, :event, to: :adapter
|
44
|
-
|
45
|
-
def initialize
|
46
|
-
@application = Application.new
|
47
|
-
end
|
48
|
-
|
49
|
-
def call(action, event:, context:)
|
50
|
-
raise NoMethodError unless self.class.controller_action?(action)
|
51
|
-
|
52
|
-
adapter = RackAdapters::ApiGateway.new(context: context, event: event)
|
53
|
-
env = adapter.env
|
54
|
-
env['shark.controller'] = self.class.controller_class_name
|
55
|
-
env['shark.action'] = action.to_s
|
56
|
-
|
57
|
-
status, headers, body = @application.call(env)
|
58
|
-
adapter.build_response(status, headers, body)
|
4
|
+
class ApiGatewayHandler < RackOnLambda::Handlers::RestApi
|
5
|
+
def self.call(event:, context:)
|
6
|
+
super(event: event, context: context, app: SharkOnLambda.application)
|
59
7
|
end
|
60
8
|
end
|
61
9
|
end
|
@@ -2,16 +2,86 @@
|
|
2
2
|
|
3
3
|
module SharkOnLambda
|
4
4
|
class Application
|
5
|
+
attr_reader :routes
|
6
|
+
|
7
|
+
delegate :middleware, :root, to: :config
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def config
|
11
|
+
@config ||= Configuration.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def inherited(subclass)
|
15
|
+
super
|
16
|
+
|
17
|
+
SharkOnLambda.application = subclass.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
register_jsonapi_rendering
|
23
|
+
initialize_router
|
24
|
+
end
|
25
|
+
|
5
26
|
def call(env)
|
6
|
-
dup.
|
27
|
+
dup.call!(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call!(env)
|
31
|
+
middleware_stack = middleware.build(routes)
|
32
|
+
middleware_stack.call(env)
|
33
|
+
end
|
34
|
+
|
35
|
+
def config
|
36
|
+
self.class.config
|
37
|
+
end
|
38
|
+
|
39
|
+
def config_for(name, env: SharkOnLambda.env)
|
40
|
+
config = load_config_file(name, env: env, fail_with_exception: true)
|
41
|
+
config.deep_merge(load_config_file("#{name}.local", env: env))
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize!
|
45
|
+
load_routes
|
46
|
+
run_initializers
|
7
47
|
end
|
8
48
|
|
9
49
|
private
|
10
50
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
-
|
51
|
+
def initialize_router
|
52
|
+
router_config = ActionDispatch::Routing::RouteSet::Config.new(nil, true)
|
53
|
+
@routes = ActionDispatch::Routing::RouteSet.new_with_config(router_config)
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_config_file(name, env:, fail_with_exception: false)
|
57
|
+
filename = "#{name}.yml"
|
58
|
+
config_file = SharkOnLambda.root.join('config', filename)
|
59
|
+
unless config_file.exist?
|
60
|
+
return {} unless fail_with_exception
|
61
|
+
|
62
|
+
raise ArgumentError,
|
63
|
+
"Could not load configuration. No such file - #{config_file}"
|
64
|
+
end
|
65
|
+
|
66
|
+
erb_parsed_config = ERB.new(config_file.read).result
|
67
|
+
config = YAML.safe_load(erb_parsed_config, [], [], true, filename) || {}
|
68
|
+
config.fetch(env, {}).with_indifferent_access
|
69
|
+
end
|
70
|
+
|
71
|
+
def load_routes
|
72
|
+
routes_path = SharkOnLambda.root.join('config', 'routes.rb').to_s
|
73
|
+
load routes_path if File.exist?(routes_path)
|
74
|
+
end
|
75
|
+
|
76
|
+
def register_jsonapi_rendering
|
77
|
+
::Mime::Type.register('application/vnd.api+json', :jsonapi)
|
78
|
+
::ActionDispatch::Request.parameter_parsers[:jsonapi] =
|
79
|
+
::ActionDispatch::Request.parameter_parsers[:json].dup
|
80
|
+
end
|
81
|
+
|
82
|
+
def run_initializers
|
83
|
+
initializers_folder = SharkOnLambda.root.join('config', 'initializers')
|
84
|
+
Dir.glob(initializers_folder.join('*.rb')).each { |path| load path }
|
15
85
|
end
|
16
86
|
end
|
17
87
|
end
|
@@ -6,13 +6,6 @@ module SharkOnLambda
|
|
6
6
|
AbstractController::Translation,
|
7
7
|
AbstractController::AssetPaths,
|
8
8
|
|
9
|
-
ActionController::UrlFor,
|
10
|
-
ActionController::ConditionalGet,
|
11
|
-
ActionController::EtagWithTemplateDigest,
|
12
|
-
ActionController::EtagWithFlash,
|
13
|
-
ActionController::Caching,
|
14
|
-
ActionController::MimeResponds,
|
15
|
-
ActionController::ImplicitRender,
|
16
9
|
ActionController::Cookies,
|
17
10
|
ActionController::Flash,
|
18
11
|
ActionController::FormBuilder,
|
@@ -24,10 +17,27 @@ module SharkOnLambda
|
|
24
17
|
ActionController::HttpAuthentication::Token::ControllerMethods,
|
25
18
|
ActionView::Layouts
|
26
19
|
].freeze
|
27
|
-
ActionController::
|
20
|
+
ActionController::API.without_modules(EXCLUDED_MODULES).each do |mod|
|
28
21
|
include mod
|
29
22
|
end
|
30
23
|
|
24
|
+
ActionController::Renderers.add :jsonapi do |object, options|
|
25
|
+
response.set_header('content-type', 'application/vnd.api+json')
|
26
|
+
return { data: {} }.to_json if object.nil?
|
27
|
+
|
28
|
+
jsonapi_renderer = JsonapiRenderer.new(object)
|
29
|
+
|
30
|
+
jsonapi_params = params.slice(:fields, :include)
|
31
|
+
jsonapi_params.permit!
|
32
|
+
jsonapi_params = JsonapiParameters.new(jsonapi_params.to_h)
|
33
|
+
|
34
|
+
render_options = jsonapi_params.to_h.deep_merge(options)
|
35
|
+
jsonapi_object = jsonapi_renderer.render(render_options)
|
36
|
+
|
37
|
+
response.status = jsonapi_renderer.status
|
38
|
+
jsonapi_object.to_json
|
39
|
+
end
|
40
|
+
|
31
41
|
def self.dispatch(*)
|
32
42
|
super
|
33
43
|
rescue AbstractController::ActionNotFound,
|
@@ -38,7 +48,17 @@ module SharkOnLambda
|
|
38
48
|
|
39
49
|
def redirect_to(*)
|
40
50
|
super
|
41
|
-
|
51
|
+
|
52
|
+
self.response_body = no_body? ? nil : { data: {} }.to_json
|
53
|
+
end
|
54
|
+
|
55
|
+
def render(object, options = {})
|
56
|
+
options.merge!(
|
57
|
+
jsonapi: object,
|
58
|
+
content_type: 'application/vnd.api+json'
|
59
|
+
)
|
60
|
+
|
61
|
+
super(options)
|
42
62
|
end
|
43
63
|
|
44
64
|
private
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SharkOnLambda
|
4
|
+
module Cacheable
|
5
|
+
delegate :cache, :global_cache, to: SharkOnLambda
|
6
|
+
|
7
|
+
def cache_duration(item)
|
8
|
+
cache_durations[item] || cache_durations[:default]
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def cache_durations
|
14
|
+
return @cache_durations if defined?(@cache_durations)
|
15
|
+
|
16
|
+
settings = SharkOnLambda.application.config_for(:settings) || {}
|
17
|
+
@cache_durations = settings.fetch(:cache_durations, {})
|
18
|
+
@cache_durations = @cache_durations.with_indifferent_access
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -2,63 +2,7 @@
|
|
2
2
|
|
3
3
|
module SharkOnLambda
|
4
4
|
class Configuration < OpenStruct
|
5
|
-
|
6
|
-
|
7
|
-
attr_writer :dispatcher, :stage
|
8
|
-
|
9
|
-
class << self
|
10
|
-
include Concerns::YamlConfigLoader
|
11
|
-
|
12
|
-
attr_writer :database_files, :settings_files
|
13
|
-
|
14
|
-
def database_files
|
15
|
-
return @database_files if defined?(@database_files)
|
16
|
-
|
17
|
-
files = %w[config/database.yml config/database.local.yml]
|
18
|
-
@database_files = paths(files)
|
19
|
-
end
|
20
|
-
|
21
|
-
def load(stage, fallback: :default)
|
22
|
-
load_settings(stage, fallback: fallback)
|
23
|
-
load_database_configuration(stage, fallback: fallback)
|
24
|
-
|
25
|
-
instance
|
26
|
-
end
|
27
|
-
|
28
|
-
def settings_files
|
29
|
-
return @settings_files if defined?(@settings_files)
|
30
|
-
|
31
|
-
files = %w[config/settings.yml config/settings.local.yml]
|
32
|
-
@settings_files = paths(files)
|
33
|
-
end
|
34
|
-
|
35
|
-
protected
|
36
|
-
|
37
|
-
def load_database_configuration(stage, fallback:)
|
38
|
-
instance.database = load_yaml_files(stage: stage,
|
39
|
-
fallback: fallback,
|
40
|
-
paths: paths(database_files))
|
41
|
-
end
|
42
|
-
|
43
|
-
def load_settings(stage, fallback:)
|
44
|
-
settings = load_yaml_files(stage: stage,
|
45
|
-
fallback: fallback,
|
46
|
-
paths: paths(settings_files))
|
47
|
-
settings.each_pair do |key, value|
|
48
|
-
next if key.to_s == 'serverless'
|
49
|
-
|
50
|
-
instance.send("#{key}=", value)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def paths(files)
|
55
|
-
files.map { |file| SharkOnLambda.config.root.join(file) }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def dispatcher
|
60
|
-
@dispatcher ||= Dispatcher.new
|
61
|
-
end
|
5
|
+
attr_reader :root
|
62
6
|
|
63
7
|
def middleware
|
64
8
|
@middleware ||= ActionDispatch::MiddlewareStack.new do |middleware_stack|
|
@@ -66,16 +10,8 @@ module SharkOnLambda
|
|
66
10
|
end
|
67
11
|
end
|
68
12
|
|
69
|
-
def root
|
70
|
-
@root ||= Pathname.new('.')
|
71
|
-
end
|
72
|
-
|
73
13
|
def root=(new_root)
|
74
14
|
@root = Pathname.new(new_root)
|
75
15
|
end
|
76
|
-
|
77
|
-
def stage
|
78
|
-
@stage || 'development'
|
79
|
-
end
|
80
16
|
end
|
81
17
|
end
|
@@ -31,14 +31,17 @@ module SharkOnLambda
|
|
31
31
|
def serializer_class_names
|
32
32
|
return @serializer_class_names if defined?(@serializer_class_names)
|
33
33
|
|
34
|
-
@serializer_class_names =
|
35
|
-
|
36
|
-
|
34
|
+
@serializer_class_names =
|
35
|
+
object_class.ancestors.reduce([]) do |result, ancestor|
|
36
|
+
ancestor_name = ancestor.name
|
37
|
+
next result if ancestor_name.blank?
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
result << serializer_name_from_model_name(ancestor_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def serializer_name_from_model_name(model_name)
|
44
|
+
"#{model_name.underscore}_serializer".camelize
|
42
45
|
end
|
43
46
|
end
|
44
47
|
end
|
@@ -2,21 +2,27 @@
|
|
2
2
|
|
3
3
|
module SharkOnLambda
|
4
4
|
class JsonapiRenderer
|
5
|
-
|
5
|
+
attr_reader :object, :status
|
6
|
+
|
7
|
+
def initialize(object, renderer: nil)
|
8
|
+
@object = object
|
6
9
|
@renderer = renderer || JSONAPI::Serializable::Renderer.new
|
10
|
+
|
11
|
+
@status = 200
|
7
12
|
end
|
8
13
|
|
9
|
-
def render(
|
10
|
-
|
14
|
+
def render(options = {})
|
15
|
+
@status = options[:status] if options[:status]
|
16
|
+
object_to_render = transform_active_model_errors(object)
|
11
17
|
|
12
|
-
unless renderable?(
|
13
|
-
return handle_unrenderable_objects(
|
18
|
+
unless renderable?(object_to_render, options)
|
19
|
+
return handle_unrenderable_objects(object_to_render, options)
|
14
20
|
end
|
15
21
|
|
16
|
-
if error?(
|
17
|
-
render_errors(
|
22
|
+
if error?(object_to_render)
|
23
|
+
render_errors(object_to_render, options)
|
18
24
|
else
|
19
|
-
render_success(
|
25
|
+
render_success(object_to_render, options)
|
20
26
|
end
|
21
27
|
end
|
22
28
|
|
@@ -54,6 +60,7 @@ module SharkOnLambda
|
|
54
60
|
Errors[500].new("Could not find serializer for: #{item.name}.")
|
55
61
|
end
|
56
62
|
|
63
|
+
@status = 500
|
57
64
|
render_errors(errors, options)
|
58
65
|
end
|
59
66
|
|
@@ -93,7 +100,7 @@ module SharkOnLambda
|
|
93
100
|
def transform_active_model_errors(errors)
|
94
101
|
return errors unless active_model_error?(errors)
|
95
102
|
|
96
|
-
|
103
|
+
errors.messages.flat_map do |attribute, attribute_errors|
|
97
104
|
attribute_errors.map do |attribute_error|
|
98
105
|
error_message = "`#{attribute_name(attribute)}' #{attribute_error}"
|
99
106
|
Errors[422].new(error_message).tap do |error|
|
@@ -101,7 +108,6 @@ module SharkOnLambda
|
|
101
108
|
end
|
102
109
|
end
|
103
110
|
end
|
104
|
-
result.flatten! || result
|
105
111
|
end
|
106
112
|
|
107
113
|
def unrenderable_objects(object, options)
|
@@ -11,24 +11,29 @@ module SharkOnLambda
|
|
11
11
|
@tags = tags
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
def _call(env)
|
17
|
-
@env = env
|
14
|
+
def call!(env)
|
18
15
|
app.call(env)
|
19
16
|
rescue StandardError => e
|
20
|
-
notify(e) unless shark_error?(e)
|
17
|
+
notify(e, env) unless shark_error?(e) && client_error?(e)
|
21
18
|
|
22
19
|
raise e
|
23
20
|
end
|
24
21
|
|
25
|
-
|
22
|
+
private
|
23
|
+
|
24
|
+
def client_error?(error)
|
25
|
+
error.respond_to?(:status) && error.status < 500
|
26
|
+
end
|
27
|
+
|
28
|
+
def notify(error, env)
|
29
|
+
params = env.fetch('action_dispatch.request.parameters', {})
|
30
|
+
|
26
31
|
::Honeybadger.notify(
|
27
32
|
error,
|
28
33
|
tags: tags,
|
29
|
-
controller:
|
30
|
-
action:
|
31
|
-
parameters:
|
34
|
+
controller: params[:controller],
|
35
|
+
action: params[:action],
|
36
|
+
parameters: params
|
32
37
|
)
|
33
38
|
end
|
34
39
|
|