shark-on-lambda 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/changelog.md +12 -0
  4. data/doc/upgrade-from-0.6.x-to-1.x.md +3 -3
  5. data/lib/shark-on-lambda.rb +9 -0
  6. data/lib/shark_on_lambda.rb +14 -54
  7. data/lib/shark_on_lambda/api_gateway_handler.rb +3 -55
  8. data/lib/shark_on_lambda/application.rb +74 -2
  9. data/lib/shark_on_lambda/base_controller.rb +29 -9
  10. data/lib/shark_on_lambda/cacheable.rb +21 -0
  11. data/lib/shark_on_lambda/configuration.rb +1 -65
  12. data/lib/shark_on_lambda/inferrers/serializer_inferrer.rb +10 -7
  13. data/lib/shark_on_lambda/jsonapi_renderer.rb +1 -2
  14. data/lib/shark_on_lambda/middleware/honeybadger.rb +5 -3
  15. data/lib/shark_on_lambda/middleware/jsonapi_rescuer.rb +21 -1
  16. data/lib/shark_on_lambda/middleware/lambda_logger.rb +5 -13
  17. data/lib/shark_on_lambda/rake_tasks.rb +16 -0
  18. data/lib/shark_on_lambda/request.rb +0 -3
  19. data/lib/shark_on_lambda/rspec/env_builder.rb +71 -37
  20. data/lib/shark_on_lambda/rspec/helpers.rb +5 -88
  21. data/lib/shark_on_lambda/rspec/request_helpers.rb +63 -0
  22. data/lib/shark_on_lambda/rspec/{jsonapi_helpers.rb → response_helpers.rb} +4 -10
  23. data/lib/shark_on_lambda/version.rb +1 -1
  24. data/shark-on-lambda.gemspec +7 -5
  25. metadata +37 -37
  26. data/lib/shark_on_lambda/concerns/resettable_singleton.rb +0 -18
  27. data/lib/shark_on_lambda/concerns/yaml_config_loader.rb +0 -28
  28. data/lib/shark_on_lambda/dispatcher.rb +0 -26
  29. data/lib/shark_on_lambda/inferrers/name_inferrer.rb +0 -66
  30. data/lib/shark_on_lambda/jsonapi_controller.rb +0 -32
  31. data/lib/shark_on_lambda/middleware/rescuer.rb +0 -37
  32. data/lib/shark_on_lambda/query.rb +0 -67
  33. data/lib/shark_on_lambda/rack_adapters/api_gateway.rb +0 -128
  34. data/lib/shark_on_lambda/secrets.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e65d5fd63a3fe9849667d0a023286a7ec8b4c78e868d3576dfd8c229d627e7d1
4
- data.tar.gz: c30474a3ce2381caf7bf5e75e76d0ec441efef525554819ce1c6bc46e8cbec50
3
+ metadata.gz: 224a4b3bddf92d9409d38d0ad65bae7511e05c0d9d40d8d2b1b6ba5c4224f6e0
4
+ data.tar.gz: 46a5ac6faeac926b023069ad953fb2b96340f969a9dcc8df116e29d6536999a2
5
5
  SHA512:
6
- metadata.gz: 5c1ab8d3c7aedcd042dace73a1771a755fc3210e65a2a229591325e96902925f3b5aa5e6ee7efdde8890862a6059ba7be365d562f1a0f82216de10d7a495c42e
7
- data.tar.gz: 34398cf95e10a12e576735a0f09ac4b269301d48d9f34ce441f0d8d232d812173ab001181f215f2ceea9012e60372c54b97c5653e407a719edf2fadf63ef3900
6
+ metadata.gz: 5cb73c903cddc2898212b09cc314f9cf35df9f099e8f21eba27df660b2d20ae78c6a828ab29e32a46e433e6485031e1609848c6af49fc53307c04651861d90c6
7
+ data.tar.gz: 92f7f4ab12057333a45089f8734c081dab440f22d42bbb6c72de0057e24cfb444089b72ffe1d3c2207ddd70e8d13ae16c88170807dbd6e68a5fd319ed65d0a84
@@ -1,4 +1,5 @@
1
1
  AllCops:
2
+ NewCops: enable
2
3
  TargetRubyVersion: 2.5
3
4
 
4
5
  Metrics/BlockLength:
@@ -6,6 +7,9 @@ Metrics/BlockLength:
6
7
  - 'spec/**/*_spec.rb'
7
8
  - 'spec/factories/*.rb'
8
9
 
10
+ Layout/LineLength:
11
+ Max: 80
12
+
9
13
  Naming/FileName:
10
14
  Exclude:
11
15
  - lib/shark-on-lambda.rb
@@ -2,6 +2,18 @@
2
2
 
3
3
  #### Unreleased
4
4
 
5
+ #### 2.0.0
6
+ - [Deprecate] Requiring `shark-on-lambda` is marked as deprecated in favour of requiring `shark_on_lambda`.
7
+ - [Break] `SharkOnLambda::Dispatcher` was removed in favour of routing via `ActionDispatch::Routing`.
8
+ - [Break] `SharkOnLambda::BaseController` now renders _JSON API_-compliant responses.
9
+ - [Break] `SharkOnLambda::JsonapiController` was removed.
10
+ - [Break] Support for `path_parameters` in RSpec helpers was removed.
11
+ - [Break] Configuration files are not loaded automatically anymore.
12
+ - Added `SharkOnLambda::Cacheable`.
13
+ - Added `SharkOnLambda.cache` and `SharkOnLambda.global_cache`.
14
+ - Added support for routing.
15
+ - Use `rack-on-lambda` as an adapter for events from the (REST API flavoured) API Gateway.
16
+
5
17
  #### 1.0.1
6
18
 
7
19
  - [Fix] `Jsonapi::Renderer#render` should always return a hash.
@@ -31,13 +31,13 @@ your RSpec tests and then use the methods `delete`, `get`, `patch`, `post`, or
31
31
 
32
32
  ```ruby
33
33
  RSpec.describe MyController do
34
- let!(:service_token) { 'my-super-secret-service-token' }
35
- let!(:headers) do
34
+ let(:service_token) { 'my-super-secret-service-token' }
35
+ let(:headers) do
36
36
  {
37
37
  'authorization' => "Bearer #{service_token}"
38
38
  }
39
39
  end
40
- let!(:params) do
40
+ let(:params) do
41
41
  {
42
42
  id: 1
43
43
  }
@@ -1,3 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/string'
4
+ require 'active_support/deprecation'
5
+
6
+ deprecation_message = <<-MESSAGE.squish
7
+ Requiring `shark-on-lambda` is deprecated and will be removed in version 3.0.
8
+ Please require `shark_on_lambda` instead.
9
+ MESSAGE
10
+ ActiveSupport::Deprecation.warn(deprecation_message, caller(2))
11
+
3
12
  require 'shark_on_lambda'
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'pry' if Gem.loaded_specs.key?('pry')
4
4
 
5
+ require 'erb'
5
6
  require 'forwardable'
6
7
  require 'ostruct'
7
8
  require 'pathname'
@@ -14,6 +15,7 @@ require 'active_support/all'
14
15
  require 'jsonapi/deserializable'
15
16
  require 'jsonapi/serializable'
16
17
  require 'rack/utils'
18
+ require 'rack-on-lambda'
17
19
  require 'yaml'
18
20
  require 'zeitwerk'
19
21
 
@@ -22,6 +24,7 @@ module SharkOnLambda; end
22
24
 
23
25
  Zeitwerk::Loader.for_gem.tap do |loader|
24
26
  loader.ignore(File.expand_path('shark-on-lambda.rb', __dir__))
27
+ loader.ignore(File.expand_path('shark_on_lambda/rake_tasks.rb', __dir__))
25
28
  loader.inflector.inflect(
26
29
  'rspec' => 'RSpec',
27
30
  'version' => 'VERSION'
@@ -35,75 +38,32 @@ module SharkOnLambda
35
38
  class << self
36
39
  extend Forwardable
37
40
 
38
- attr_writer :logger
41
+ attr_writer :application, :cache, :env, :global_cache, :logger
39
42
 
40
- def_instance_delegators :config, :root, :stage
43
+ def_instance_delegators :application, :initialize!, :root
41
44
 
42
45
  def application
43
46
  @application ||= Application.new
44
47
  end
45
48
 
46
- def config
47
- Configuration.instance
49
+ def configuration
50
+ application.config
48
51
  end
49
52
 
50
- def configure
51
- yield(config, secrets)
53
+ def cache
54
+ @cache ||= ActiveSupport::Cache::NullStore.new
52
55
  end
53
56
 
54
- def initialize!
55
- enable_jsonapi!
56
-
57
- yield(config, secrets)
58
-
59
- Configuration.load(stage)
60
- Secrets.load(stage)
61
- run_initializers
62
-
63
- true
57
+ def env
58
+ @env || ENV['STAGE'].presence || 'development'
64
59
  end
65
60
 
66
- def load_configuration
67
- Configuration.load(stage)
68
- Secrets.load(stage)
69
-
70
- true
61
+ def global_cache
62
+ @global_cache ||= ActiveSupport::Cache::NullStore.new
71
63
  end
72
64
 
73
65
  def logger
74
- @logger ||= Logger.new(STDOUT)
75
- end
76
-
77
- def reset_configuration
78
- known_stage = config.stage
79
- known_root = config.root
80
-
81
- Configuration.reset
82
- Secrets.reset
83
-
84
- config.root = known_root
85
- config.stage = known_stage
86
-
87
- true
88
- end
89
-
90
- def secrets
91
- Secrets.instance
92
- end
93
-
94
- private
95
-
96
- def enable_jsonapi!
97
- ::Mime::Type.register('application/vnd.api+json', :jsonapi)
98
- ::ActionDispatch::Request.parameter_parsers[:jsonapi] =
99
- ::ActionDispatch::Request.parameter_parsers[:json].dup
100
- end
101
-
102
- def run_initializers
103
- initializers_path = root.join('config', 'initializers')
104
- Dir.glob(initializers_path.join('*.rb')).each do |path|
105
- load path
106
- end
66
+ @logger ||= Logger.new($stdout)
107
67
  end
108
68
  end
109
69
  end
@@ -1,61 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SharkOnLambda
4
- class ApiGatewayHandler
5
- class << self
6
- attr_writer :controller_class_name
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,14 +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
27
  dup.call!(env)
7
28
  end
8
29
 
9
30
  def call!(env)
10
- dispatcher = SharkOnLambda.config.dispatcher
11
- middleware_stack = SharkOnLambda.config.middleware.build(dispatcher)
31
+ middleware_stack = middleware.build(routes)
12
32
  middleware_stack.call(env)
13
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
47
+ end
48
+
49
+ private
50
+
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 }
85
+ end
14
86
  end
15
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::Base.without_modules(EXCLUDED_MODULES).each do |mod|
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
- self.response_body = '' if no_body?
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
- include Concerns::ResettableSingleton
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