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.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.gitlab-ci.yml +13 -0
- data/.rubocop.yml +13 -0
- data/.travis.yml +9 -2
- data/README.md +184 -18
- data/Rakefile +2 -0
- data/bin/console +4 -9
- data/changelog.md +17 -0
- data/gems.locked +92 -0
- data/{Gemfile → gems.rb} +2 -1
- data/lib/shark-on-lambda.rb +1 -5
- data/lib/shark_on_lambda.rb +104 -1
- data/lib/shark_on_lambda/api_gateway/base_controller.rb +76 -0
- data/lib/shark_on_lambda/api_gateway/base_handler.rb +82 -0
- data/lib/shark_on_lambda/api_gateway/concerns/http_response_validation.rb +61 -0
- data/lib/shark_on_lambda/api_gateway/errors.rb +49 -0
- data/lib/shark_on_lambda/api_gateway/headers.rb +37 -0
- data/lib/shark_on_lambda/api_gateway/jsonapi_controller.rb +77 -0
- data/lib/shark_on_lambda/api_gateway/jsonapi_parameters.rb +68 -0
- data/lib/shark_on_lambda/api_gateway/jsonapi_renderer.rb +105 -0
- data/lib/shark_on_lambda/api_gateway/parameters.rb +18 -0
- data/lib/shark_on_lambda/api_gateway/query.rb +69 -0
- data/lib/shark_on_lambda/api_gateway/request.rb +148 -0
- data/lib/shark_on_lambda/api_gateway/response.rb +82 -0
- data/lib/shark_on_lambda/api_gateway/serializers/base_error_serializer.rb +20 -0
- data/lib/shark_on_lambda/concerns/filter_actions.rb +82 -0
- data/lib/shark_on_lambda/concerns/resettable_singleton.rb +18 -0
- data/lib/shark_on_lambda/concerns/yaml_config_loader.rb +28 -0
- data/lib/shark_on_lambda/configuration.rb +71 -0
- data/lib/shark_on_lambda/inferrers/name_inferrer.rb +66 -0
- data/lib/shark_on_lambda/inferrers/serializer_inferrer.rb +45 -0
- data/lib/shark_on_lambda/secrets.rb +43 -0
- data/lib/shark_on_lambda/tasks.rb +3 -0
- data/lib/shark_on_lambda/tasks/build.rake +146 -0
- data/lib/{shark-on-lambda → shark_on_lambda}/version.rb +1 -1
- data/shark-on-lambda.gemspec +21 -6
- metadata +158 -20
- 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,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
|