shark-on-lambda 0.0.0 → 0.6.7

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.gitlab-ci.yml +13 -0
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +9 -2
  6. data/README.md +184 -18
  7. data/Rakefile +2 -0
  8. data/bin/console +4 -9
  9. data/changelog.md +17 -0
  10. data/gems.locked +92 -0
  11. data/{Gemfile → gems.rb} +2 -1
  12. data/lib/shark-on-lambda.rb +1 -5
  13. data/lib/shark_on_lambda.rb +104 -1
  14. data/lib/shark_on_lambda/api_gateway/base_controller.rb +76 -0
  15. data/lib/shark_on_lambda/api_gateway/base_handler.rb +82 -0
  16. data/lib/shark_on_lambda/api_gateway/concerns/http_response_validation.rb +61 -0
  17. data/lib/shark_on_lambda/api_gateway/errors.rb +49 -0
  18. data/lib/shark_on_lambda/api_gateway/headers.rb +37 -0
  19. data/lib/shark_on_lambda/api_gateway/jsonapi_controller.rb +77 -0
  20. data/lib/shark_on_lambda/api_gateway/jsonapi_parameters.rb +68 -0
  21. data/lib/shark_on_lambda/api_gateway/jsonapi_renderer.rb +105 -0
  22. data/lib/shark_on_lambda/api_gateway/parameters.rb +18 -0
  23. data/lib/shark_on_lambda/api_gateway/query.rb +69 -0
  24. data/lib/shark_on_lambda/api_gateway/request.rb +148 -0
  25. data/lib/shark_on_lambda/api_gateway/response.rb +82 -0
  26. data/lib/shark_on_lambda/api_gateway/serializers/base_error_serializer.rb +20 -0
  27. data/lib/shark_on_lambda/concerns/filter_actions.rb +82 -0
  28. data/lib/shark_on_lambda/concerns/resettable_singleton.rb +18 -0
  29. data/lib/shark_on_lambda/concerns/yaml_config_loader.rb +28 -0
  30. data/lib/shark_on_lambda/configuration.rb +71 -0
  31. data/lib/shark_on_lambda/inferrers/name_inferrer.rb +66 -0
  32. data/lib/shark_on_lambda/inferrers/serializer_inferrer.rb +45 -0
  33. data/lib/shark_on_lambda/secrets.rb +43 -0
  34. data/lib/shark_on_lambda/tasks.rb +3 -0
  35. data/lib/shark_on_lambda/tasks/build.rake +146 -0
  36. data/lib/{shark-on-lambda → shark_on_lambda}/version.rb +1 -1
  37. data/shark-on-lambda.gemspec +21 -6
  38. metadata +158 -20
  39. data/Gemfile.lock +0 -35
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ module ApiGateway
5
+ module Errors
6
+ class BaseSerializer < ::JSONAPI::Serializable::Error
7
+ id { @object.id }
8
+ status { @object.status }
9
+ code { @object.code }
10
+ title { @object.title }
11
+ detail { @object.detail }
12
+ meta { @object.meta }
13
+ source do
14
+ pointer @object.pointer if @object.pointer.present?
15
+ parameter @object.parameter if @object.parameter.present?
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ module Concerns
5
+ module FilterActions
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def after_action(symbol, only: [], except: [])
12
+ @after_actions ||= []
13
+ @after_actions << {
14
+ symbol: symbol,
15
+ only: Array(only),
16
+ except: Array(except)
17
+ }
18
+ end
19
+
20
+ def after_actions
21
+ @after_actions || []
22
+ end
23
+
24
+ def before_action(symbol, only: [], except: [])
25
+ @before_actions ||= []
26
+ @before_actions << {
27
+ symbol: symbol,
28
+ only: Array(only),
29
+ except: Array(except)
30
+ }
31
+ end
32
+
33
+ def before_actions
34
+ @before_actions || []
35
+ end
36
+ end
37
+
38
+ def call_with_filter_actions(method, *args)
39
+ run_before_actions(method)
40
+ result = send(method, *args)
41
+ run_after_actions(method)
42
+ result
43
+ end
44
+
45
+ protected
46
+
47
+ def after_actions
48
+ self.class.after_actions
49
+ end
50
+
51
+ def before_actions
52
+ self.class.before_actions
53
+ end
54
+
55
+ def run_actions(method, actions)
56
+ actions.each do |action|
57
+ next if skip_filter_action?(method, action)
58
+
59
+ send(action[:symbol])
60
+ end
61
+ end
62
+
63
+ def run_after_actions(method)
64
+ run_actions(method, after_actions)
65
+ end
66
+
67
+ def run_before_actions(method)
68
+ run_actions(method, before_actions)
69
+ end
70
+
71
+ def skip_filter_action?(method, filter_action)
72
+ only = filter_action[:only]
73
+ except = filter_action[:except]
74
+
75
+ return true if only.any? && !only.include?(method)
76
+ return true if except.any? && except.include?(method)
77
+
78
+ false
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ module Concerns
5
+ module ResettableSingleton
6
+ def self.included(base)
7
+ base.include(Singleton)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def reset
13
+ @singleton__instance__ = nil
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ module Concerns
5
+ module YamlConfigLoader
6
+ def load_yaml_files(stage:, fallback: :default, paths:)
7
+ result = HashWithIndifferentAccess.new
8
+ paths.each do |path|
9
+ data = load_yaml_file(stage: stage, fallback: fallback, path: path)
10
+ result.deep_merge!(data)
11
+ end
12
+ result
13
+ end
14
+
15
+ protected
16
+
17
+ def load_yaml_file(stage:, fallback:, path:)
18
+ return {} unless File.exist?(path)
19
+
20
+ data = YAML.load_file(path)
21
+ return {} unless data.is_a?(Hash)
22
+
23
+ data = data.with_indifferent_access
24
+ data[stage] || data[fallback] || {}
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ class Configuration < OpenStruct
5
+ include Concerns::ResettableSingleton
6
+
7
+ attr_writer :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 root
60
+ @root ||= Pathname.new('.')
61
+ end
62
+
63
+ def root=(new_root)
64
+ @root = Pathname.new(new_root)
65
+ end
66
+
67
+ def stage
68
+ @stage || 'development'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ module Inferrers
5
+ class NameInferrer
6
+ class << self
7
+ def from_controller_name(class_name)
8
+ from_name(:controller, class_name)
9
+ end
10
+
11
+ def from_deserializer_name(class_name)
12
+ from_name(:deserializer, class_name)
13
+ end
14
+
15
+ def from_handler_name(class_name)
16
+ from_name(:handler, class_name)
17
+ end
18
+
19
+ def from_model_name(class_name)
20
+ from_name(:model, class_name)
21
+ end
22
+
23
+ def from_serializer_name(class_name)
24
+ from_name(:serializer, class_name)
25
+ end
26
+
27
+ protected
28
+
29
+ def from_name(type, class_name)
30
+ base = class_name.underscore
31
+ base = case type
32
+ when :controller, :deserializer, :handler, :serializer
33
+ base.sub(/_#{type}\z/, '')
34
+ when :model
35
+ base
36
+ end
37
+ new(base)
38
+ end
39
+ end
40
+
41
+ def initialize(base)
42
+ @base = base
43
+ end
44
+
45
+ def controller
46
+ "#{@base}_controller".camelize
47
+ end
48
+
49
+ def deserializer
50
+ "#{@base}_deserializer".camelize
51
+ end
52
+
53
+ def handler
54
+ "#{@base}_handler".camelize
55
+ end
56
+
57
+ def model
58
+ @base.camelize
59
+ end
60
+
61
+ def serializer
62
+ "#{@base}_serializer".camelize
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ module Inferrers
5
+ class SerializerInferrer
6
+ def initialize(object_class)
7
+ @object_class = object_class
8
+ end
9
+
10
+ def serializer_class
11
+ return @serializer_class if defined?(@serializer_class)
12
+
13
+ serializer_class_names.each do |serializer_class_name|
14
+ @serializer_class = serializer_class_name.safe_constantize
15
+ break if @serializer_class.present?
16
+ end
17
+
18
+ @serializer_class
19
+ end
20
+
21
+ protected
22
+
23
+ def object_class
24
+ unless @object_class.is_a?(String) || @object_class.is_a?(Symbol)
25
+ return @object_class
26
+ end
27
+
28
+ @object_class.to_s.camelize.constantize
29
+ end
30
+
31
+ def serializer_class_names
32
+ return @serializer_class_names if defined?(@serializer_class_names)
33
+
34
+ @serializer_class_names = object_class.ancestors.map do |ancestor|
35
+ ancestor_name = ancestor.name
36
+ next if ancestor_name.blank?
37
+
38
+ name_inferrer = NameInferrer.from_model_name(ancestor_name)
39
+ name_inferrer.serializer
40
+ end
41
+ @serializer_class_names.compact! || @serializer_class_names
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SharkOnLambda
4
+ class Secrets < OpenStruct
5
+ include Concerns::ResettableSingleton
6
+
7
+ class << self
8
+ include Concerns::YamlConfigLoader
9
+
10
+ attr_writer :files
11
+
12
+ def load(stage, fallback: :default)
13
+ load_secrets(stage, fallback: fallback)
14
+
15
+ instance
16
+ end
17
+
18
+ def files
19
+ return @files if defined?(@files)
20
+
21
+ @files = paths(%w[config/secrets.yml config/secrets.local.yml])
22
+ end
23
+
24
+ protected
25
+
26
+ def load_secrets(stage, fallback:)
27
+ secrets = load_yaml_files(stage: stage,
28
+ fallback: fallback,
29
+ paths: files)
30
+ secrets.each_pair { |key, value| instance.send("#{key}=", value) }
31
+ end
32
+
33
+ def paths(files)
34
+ files.map { |file| SharkOnLambda.config.root.join(file) }
35
+ end
36
+ end
37
+
38
+ def inspect
39
+ # Do not display all the internals of this object when #inspect is called.
40
+ "#<#{self.class.name}>"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ load 'shark_on_lambda/tasks/build.rake'
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shark-on-lambda'
4
+
5
+ class DockerContainer
6
+ attr_reader :project_dir, :tag
7
+ attr_accessor :params, :commands
8
+
9
+ def initialize(project_dir:, tag: 'latest')
10
+ @project_dir = project_dir
11
+ @tag = tag
12
+ @params = default_params
13
+ @commands = default_commands
14
+ end
15
+
16
+ def build
17
+ sh("cd #{project_dir} && docker build . -t #{name}")
18
+ end
19
+
20
+ def exist?
21
+ system("docker image inspect #{name} &> /dev/null")
22
+ end
23
+
24
+ def mount_dir
25
+ File.join('/src', project_name)
26
+ end
27
+
28
+ def name
29
+ "#{project_name}-builder:#{tag}"
30
+ end
31
+
32
+ def remove
33
+ sh("docker image rm #{name}")
34
+ end
35
+
36
+ def run
37
+ arguments = params.join(' ')
38
+ command = commands.join(' && ')
39
+
40
+ sh(%(docker run #{arguments} #{name} /bin/bash -c "#{command}"))
41
+ end
42
+
43
+ def working_dir
44
+ File.join('/tmp', project_name)
45
+ end
46
+
47
+ protected
48
+
49
+ def default_commands
50
+ [
51
+ "cp -a #{mount_dir} #{working_dir}",
52
+ "cd #{working_dir}"
53
+ ]
54
+ end
55
+
56
+ def default_params
57
+ [
58
+ '--rm',
59
+ "-v #{project_dir}:#{mount_dir}"
60
+ ]
61
+ end
62
+
63
+ def project_name
64
+ File.basename(project_dir)
65
+ end
66
+
67
+ def sh(command)
68
+ puts command
69
+ system(command)
70
+ end
71
+ end
72
+
73
+ def build_stage(stage)
74
+ container = DockerContainer.new(project_dir: SharkOnLambda.root)
75
+ Rake::Task['docker:build'].invoke unless container.exist?
76
+
77
+ container.commands += build_commands(container, stage: stage)
78
+ container.run
79
+ end
80
+
81
+ def deploy_stage(stage)
82
+ deploy_command = deploy_commands(stage: stage).join(' && ')
83
+ sh(deploy_command)
84
+ end
85
+
86
+ def remove_stage(stage)
87
+ sh("sls remove -v -s #{stage}")
88
+ end
89
+
90
+ namespace :docker do
91
+ container = DockerContainer.new(project_dir: SharkOnLambda.root)
92
+
93
+ desc 'Build the Docker container required for... building.'
94
+ task :build do
95
+ container.build
96
+ end
97
+
98
+ desc 'Remove the Docker container required for building.'
99
+ task :remove do
100
+ container.remove
101
+ end
102
+ end
103
+
104
+ %i[integration staging production].each do |stage|
105
+ namespace :build do
106
+ desc "Build this service for the '#{stage}' stage."
107
+ task stage => [:clean] do
108
+ build_stage(stage)
109
+ end
110
+ end
111
+
112
+ namespace :deploy do
113
+ desc "Deploy this service to the '#{stage}' stage."
114
+ task stage => ["build:#{stage}"] do
115
+ deploy_stage(stage)
116
+ end
117
+ end
118
+
119
+ namespace :remove do
120
+ desc "Remove this service from the '#{stage}' stage."
121
+ task stage do
122
+ remove_stage(stage)
123
+ end
124
+ end
125
+ end
126
+
127
+ desc 'Build this service for STAGE.'
128
+ task :build, [:stage] => [:clean] do |_, args|
129
+ build_stage(args.stage)
130
+ end
131
+
132
+ desc 'Remove package build directory.'
133
+ task :clean do
134
+ package_dir = SharkOnLambda.root.join('pkg')
135
+ FileUtils.rm_rf(package_dir)
136
+ end
137
+
138
+ desc 'Deploy this service to STAGE.'
139
+ task :deploy, [:stage] => [:build] do |_, args|
140
+ deploy_stage(args.stage)
141
+ end
142
+
143
+ desc 'Remove this service from STAGE.'
144
+ task :remove, [:stage] do |_, args|
145
+ remove_stage(args.stage)
146
+ end