modulator 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +0 -4
- data/Gemfile.lock +26 -22
- data/README.md +243 -7
- data/lib/modulator.rb +82 -46
- data/lib/modulator/gateway/gateway.rb +4 -6
- data/lib/modulator/gateway/routes/console.rb +32 -34
- data/lib/modulator/gateway_event.json +60 -0
- data/lib/modulator/{lambda/aws_lambda_handler.rb → lambda_handler.rb} +2 -15
- data/lib/modulator/stack/builder.rb +256 -0
- data/lib/modulator/stack/policies.rb +99 -0
- data/lib/modulator/{lambda/aws_stack_uploader.rb → stack/uploader.rb} +59 -38
- data/lib/modulator/version.rb +1 -1
- data/lib/utils.rb +7 -1
- data/modulator.gemspec +1 -4
- metadata +9 -20
- data/lib/modulator/lambda/aws_stack_builder.rb +0 -225
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1a99c918b27ea88351705275d5657942b07efb8b9e87662318e4e15e2e5da08
|
4
|
+
data.tar.gz: f78e0d6e3cd7ac9e1d0767e4e91b2b3bfa475dc3d0569d32f94c4349cd249dda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3177cbde9f6c198c23dec0216cbbe8b8acc6b74a866c3225e0cf11a1494f1752da82d3011cc990d7fb0e4715a2b47f7c9b87f9da60d9d7f0f389d3c17cc26e79
|
7
|
+
data.tar.gz: 3e89b602c5a48491f267efc3dd88b8f6b5a6d98ac154936dc2c1763bde164fa9358c5126f6687862c5c2f78d61b4bb80c2a988b1539cb18e00a8948a06ed30b2
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## [0.3.0] - 2019-06-07
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- This file
|
14
|
+
- Modulator#wrap_with
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- Use symbolize_names when parsing json instead custom helper
|
19
|
+
- Updated readme file with examples
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,15 +1,8 @@
|
|
1
|
-
GIT
|
2
|
-
remote: https://github.com/damir/humidifier
|
3
|
-
revision: 81a75bbd4b47aadaaed5f967fb3527a2175dec82
|
4
|
-
specs:
|
5
|
-
humidifier (2.15.0)
|
6
|
-
|
7
1
|
PATH
|
8
2
|
remote: .
|
9
3
|
specs:
|
10
|
-
modulator (0.
|
11
|
-
|
12
|
-
aws-sdk-s3
|
4
|
+
modulator (0.3.0)
|
5
|
+
humidifier
|
13
6
|
puma
|
14
7
|
rerun
|
15
8
|
roda
|
@@ -19,26 +12,34 @@ GEM
|
|
19
12
|
remote: https://rubygems.org/
|
20
13
|
specs:
|
21
14
|
aws-eventstream (1.0.3)
|
22
|
-
aws-partitions (1.
|
23
|
-
aws-sdk-cloudformation (1.
|
24
|
-
aws-sdk-core (~> 3, >= 3.
|
15
|
+
aws-partitions (1.173.0)
|
16
|
+
aws-sdk-cloudformation (1.22.0)
|
17
|
+
aws-sdk-core (~> 3, >= 3.53.0)
|
25
18
|
aws-sigv4 (~> 1.1)
|
26
|
-
aws-sdk-core (3.
|
19
|
+
aws-sdk-core (3.54.2)
|
27
20
|
aws-eventstream (~> 1.0, >= 1.0.2)
|
28
21
|
aws-partitions (~> 1.0)
|
29
22
|
aws-sigv4 (~> 1.1)
|
30
23
|
jmespath (~> 1.0)
|
31
|
-
aws-sdk-kms (1.
|
32
|
-
aws-sdk-core (~> 3, >= 3.
|
24
|
+
aws-sdk-kms (1.21.0)
|
25
|
+
aws-sdk-core (~> 3, >= 3.53.0)
|
33
26
|
aws-sigv4 (~> 1.1)
|
34
|
-
aws-sdk-s3 (1.
|
35
|
-
aws-sdk-core (~> 3, >= 3.
|
27
|
+
aws-sdk-s3 (1.42.0)
|
28
|
+
aws-sdk-core (~> 3, >= 3.53.0)
|
36
29
|
aws-sdk-kms (~> 1)
|
37
|
-
aws-sigv4 (~> 1.
|
30
|
+
aws-sigv4 (~> 1.1)
|
38
31
|
aws-sigv4 (1.1.0)
|
39
32
|
aws-eventstream (~> 1.0, >= 1.0.2)
|
40
33
|
diff-lcs (1.3)
|
41
|
-
|
34
|
+
fast_underscore (0.3.1)
|
35
|
+
ffi (1.11.1)
|
36
|
+
hollaback (0.1.0)
|
37
|
+
humidifier (3.3.0)
|
38
|
+
aws-sdk-cloudformation (~> 1.19)
|
39
|
+
aws-sdk-s3 (~> 1.36)
|
40
|
+
fast_underscore (~> 0.3)
|
41
|
+
thor (~> 0.20)
|
42
|
+
thor-hollaback (~> 0.1)
|
42
43
|
jmespath (1.4.0)
|
43
44
|
listen (3.1.5)
|
44
45
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
@@ -54,7 +55,7 @@ GEM
|
|
54
55
|
ffi (~> 1.0)
|
55
56
|
rerun (0.13.0)
|
56
57
|
listen (~> 3.0)
|
57
|
-
roda (3.
|
58
|
+
roda (3.20.0)
|
58
59
|
rack
|
59
60
|
rspec (3.8.0)
|
60
61
|
rspec-core (~> 3.8.0)
|
@@ -70,14 +71,17 @@ GEM
|
|
70
71
|
rspec-support (~> 3.8.0)
|
71
72
|
rspec-support (3.8.0)
|
72
73
|
ruby_dep (1.5.0)
|
73
|
-
rubyzip (1.2.
|
74
|
+
rubyzip (1.2.3)
|
75
|
+
thor (0.20.3)
|
76
|
+
thor-hollaback (0.1.2)
|
77
|
+
hollaback (~> 0.1.0)
|
78
|
+
thor (~> 0.20, >= 0.19.1)
|
74
79
|
|
75
80
|
PLATFORMS
|
76
81
|
ruby
|
77
82
|
|
78
83
|
DEPENDENCIES
|
79
84
|
bundler (~> 2.0)
|
80
|
-
humidifier!
|
81
85
|
modulator!
|
82
86
|
rack-test (~> 1.1)
|
83
87
|
rake (~> 10.0)
|
data/README.md
CHANGED
@@ -1,28 +1,264 @@
|
|
1
1
|
# Modulator
|
2
2
|
|
3
|
-
|
3
|
+
Modulator is a tool for adding HTTP layer on top of your application using AWS Lambda and API Gateway services. You register the methods you want to publish and run the deploy script. CloudFormation engine will then provision the necessary infrastructure and deploy your application in seconds.
|
4
4
|
|
5
|
-
|
5
|
+
Because your application is isolated form HTTP handling you will write regular Ruby code without polluting it with framework or HTTP specific details. This is possible by reflecting on method signatures to construct API Gateway endpoints and consuming its events in predictable way.
|
6
|
+
|
7
|
+
Code is deployed in two lambda layers, one for the gems and one for the application code. You will need a writable bucket to store the files and ability to manage CloudFormation stacks.
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
9
11
|
Add this line to your application's Gemfile:
|
10
12
|
|
11
13
|
```ruby
|
12
|
-
gem 'modulator'
|
14
|
+
gem 'modulator', group: :development
|
13
15
|
```
|
14
16
|
|
15
17
|
And then execute:
|
16
18
|
|
17
19
|
$ bundle
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
$ gem install modulator
|
21
|
+
NOTE: do not put modulator entry outside the group, the tool will bundle default group for deployment and the gem is not required in lambda runtime
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
25
|
+
### Quick example
|
26
|
+
|
27
|
+
Write Ruby application:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# calculator/algebra.rb:
|
31
|
+
|
32
|
+
module Calculator
|
33
|
+
module Algebra
|
34
|
+
def self.sum(x, y)
|
35
|
+
{
|
36
|
+
x: x,
|
37
|
+
y: y,
|
38
|
+
sum: x + y
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.square(x, ip = nil)
|
43
|
+
{
|
44
|
+
x: x,
|
45
|
+
ip: ip,
|
46
|
+
square: x * x
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Add deploy script to working directory:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
# stack.rb
|
57
|
+
|
58
|
+
require 'modulator'
|
59
|
+
require_relative 'calculator/algebra'
|
60
|
+
|
61
|
+
# register module methods
|
62
|
+
Modulator.register(Calculator::Algebra)
|
63
|
+
|
64
|
+
# initialize and deploy the stack
|
65
|
+
stack = Modulator.init_stack s3_bucket: 'my-modulator-apps' # bucket for code and gems layers
|
66
|
+
puts stack.valid?
|
67
|
+
puts stack.to_cf(:yaml)
|
68
|
+
puts stack.deploy_and_wait capabilities: ['CAPABILITY_IAM'], parameters: [
|
69
|
+
{parameter_key: 'AppEnvironment', parameter_value: 'development'},
|
70
|
+
{parameter_key: 'ApiGatewayStageName', parameter_value: 'v1'}
|
71
|
+
]
|
72
|
+
```
|
73
|
+
|
74
|
+
Run the script then visit CloudFormation page in AWS console and navigate to created stack, click on Outputs tab and copy ApiGatewayInvokeURL:
|
75
|
+
|
76
|
+
- https://some-api-id.execute-api.us-east-1.amazonaws.com/v1
|
77
|
+
|
78
|
+
Then Invoke your methods using the browser or postman:
|
79
|
+
|
80
|
+
- https://some-api-id.execute-api.us-east-1.amazonaws.com/v1/algebra/2/square
|
81
|
+
- https://some-api-id.execute-api.us-east-1.amazonaws.com/v1/algebra/2/3/sum
|
82
|
+
|
83
|
+
These URLs are also available in lambda page when clicking on API Gateway icon:
|
84
|
+
|
85
|
+
```
|
86
|
+
ModulatorGatewayApp
|
87
|
+
arn:aws:execute-api:us-east-1:your-account-id:some-api-id/*/GET/calculator/algebra/*/square
|
88
|
+
|
89
|
+
Details
|
90
|
+
API endpoint: https://some-api-id.execute-api.us-east-1.amazonaws.com/v1/calculator/algebra/{x}/square
|
91
|
+
Authorization: NONE
|
92
|
+
Method: GET
|
93
|
+
Resource path: /calculator/algebra/{x}/square
|
94
|
+
Stage: v1
|
95
|
+
```
|
96
|
+
|
97
|
+
You can save CF template to a file by capturing output of stack.to\_cf(:yaml) or stack.to\_cf(:json.)
|
98
|
+
|
99
|
+
### Wrapping the method to get data from lambda event and context
|
100
|
+
|
101
|
+
Data from the request can be extracted using wrapper method which will pass the values to wrapped method as optional arguments. This example will wrap Calculator::Algebra#square with Wrappers::Authorizer#call to autorize request and provide optional ip argument:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# wrappers/authorizer.rb
|
105
|
+
|
106
|
+
module Wrappers
|
107
|
+
module Authorizer
|
108
|
+
module_function
|
109
|
+
|
110
|
+
def call(event:, context:)
|
111
|
+
token = event.dig('headers', 'Authorization').to_s.split(' ').last
|
112
|
+
if token == 'block'
|
113
|
+
{status: 401, body: {error: 'Blocking token'}}
|
114
|
+
elsif token == 'pass'
|
115
|
+
{ip: event.dig('requestContext', 'identity', 'sourceIp')}
|
116
|
+
else
|
117
|
+
# block with generic 403
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
```
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
# stack.rb
|
126
|
+
require_relative 'wrappers/authorizer'
|
127
|
+
Modulator.register(Calculator::Algebra).wrap_with(Wrappers::Authorizer, only: :square)
|
128
|
+
```
|
129
|
+
|
130
|
+
This method can be invoked only when Aurhorization header is set to 'pass', otherwise it will print explcit 401 with custom message when the value is 'block', or will default to 403 with generic message.
|
131
|
+
|
132
|
+
Available options are :only and :except where value is the method name or an array of names.
|
133
|
+
|
134
|
+
### Registering and configuring methods
|
135
|
+
|
136
|
+
Registering module will add configuration entry to Modulator::LAMBDAS. Each entry is a plain hash which can be overriden. From this configuration a CloudFormation template is generated with all necessary resources for API Gateway endpoints and their lambdas, including function policies and execution roles.
|
137
|
+
|
138
|
+
Consider this example:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
Modulator
|
142
|
+
.register(Calculator::Algebra, sum: {
|
143
|
+
gateway: {path: 'calc/:x/add/:y'},
|
144
|
+
settings: {timeout: 1, memory_size: 256},
|
145
|
+
env: {custom_var: 123}
|
146
|
+
}
|
147
|
+
)
|
148
|
+
.wrap_with(Wrappers::Authorizer, only: [:square])
|
149
|
+
```
|
150
|
+
|
151
|
+
For that example Modulator::LAMBDAS will print this configuration:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
{"calculator-algebra-square"=>
|
155
|
+
{:name=>"calculator-algebra-square",
|
156
|
+
:gateway=>{:verb=>"GET", :path=>"calculator/algebra/:x/square"},
|
157
|
+
:module=>
|
158
|
+
{:name=>"Calculator::Algebra",
|
159
|
+
:method=>"square",
|
160
|
+
:path=>"calculator/algebra"},
|
161
|
+
:wrapper=>
|
162
|
+
{:name=>"Wrappers::Authorizer",
|
163
|
+
:path=>"wrappers/authorizer",
|
164
|
+
:method=>"call"},
|
165
|
+
:env=>{},
|
166
|
+
:settings=>{}},
|
167
|
+
"calculator-algebra-sum"=>
|
168
|
+
{:name=>"calculator-algebra-sum",
|
169
|
+
:gateway=>{:verb=>"GET", :path=>"calc/:x/add/:y"},
|
170
|
+
:module=>
|
171
|
+
{:name=>"Calculator::Algebra",
|
172
|
+
:method=>"sum",
|
173
|
+
:path=>"calculator/algebra"},
|
174
|
+
:wrapper=>{},
|
175
|
+
:env=>{:custom_var=>123},
|
176
|
+
:settings=>{:timeout=>1, :memory_size=>256}}}
|
177
|
+
```
|
178
|
+
|
179
|
+
- :gateway is used to construct API Gateway endpoint, it has :path key from which the URL is constructed and :verb which sets the HTTP method for that URL
|
180
|
+
- :wrapper defines wrapping method, :name is the module namespace, :method is the method name from that namespace and :path is the relative file path where the code is
|
181
|
+
- :settings holds lambda settings values, :timeout and :memory_size
|
182
|
+
- :env will add extra environment variables to lambda runtime
|
183
|
+
|
184
|
+
Any value can be changed manualy during or after the config is generated if you want to override defaults. For example you can change :verb from GET to DELETE or you can rearrange static and dynamic URL path fragments.
|
185
|
+
|
186
|
+
### Rules for mapping URL paths and HTTP methods to method signatures
|
187
|
+
|
188
|
+
- Methods will be invoked with GET unless:
|
189
|
+
- the method name is delete, remove, or destroy for which DELETE is set
|
190
|
+
- the method has optional key paramater for which POST is set and payload is passed as its value
|
191
|
+
- Required positional parameters are mapped as dinamic URL fragments, numbers are type casted to ruby classes
|
192
|
+
- Module namespace and method name are mapped as static URL fragments
|
193
|
+
|
194
|
+
For examples please check the spec folder and the sample application code there.
|
195
|
+
|
196
|
+
### Local API gateway for development
|
197
|
+
|
198
|
+
It is possible to run code locally as it would run in the cloud. You need to add config.ru and register some modules:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
# config.ru
|
202
|
+
|
203
|
+
require 'modulator/gateway/gateway'
|
204
|
+
require_relative 'calculator/algebra'
|
205
|
+
require_relative 'wrappers/authorizer'
|
206
|
+
Modulator.register(Calculator::Algebra).wrap_with(Wrappers::Authorizer, only: [:square])
|
207
|
+
```
|
208
|
+
|
209
|
+
Then start the server with this command:
|
210
|
+
|
211
|
+
rerun -- puma gateway.ru
|
212
|
+
|
213
|
+
NOTE: rerun will restart the server when code changes.
|
214
|
+
|
215
|
+
Visiting localhost:9292/calculator/algebra/2/square should give this result:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
{
|
219
|
+
"x": 2,
|
220
|
+
"ip": "127.0.0.1",
|
221
|
+
"square": 4
|
222
|
+
}
|
223
|
+
```
|
224
|
+
|
225
|
+
Server log will print detailed information about request and method invocation:
|
226
|
+
|
227
|
+
```
|
228
|
+
Method: GET
|
229
|
+
Path: /calculator/algebra/2/square
|
230
|
+
Headers: {
|
231
|
+
"Accept"=>"*/*", "Accept-Encoding"=>"gzip, deflate", "Authorization"=>"pass",
|
232
|
+
"Cache-Control"=>"no-cache", "Connection"=>"keep-alive", "Host"=>"localhost:9292",
|
233
|
+
"Postman-Token"=>"8708ee60-a156-4ca2-9937-f1f408a568e2",
|
234
|
+
"User-Agent"=>"PostmanRuntime/7.13.0", "Version"=>"HTTP/1.1"}
|
235
|
+
Path params: {"x"=>"2"}
|
236
|
+
Calling wrapper Wrappers::Authorizer.call
|
237
|
+
Resolving GET calculator/algebra/:x/square to Calculator::Algebra.square with [[:req, :x], [:opt, :ip]]
|
238
|
+
Matched path: /calculator/algebra/2/square
|
239
|
+
Status: 200
|
240
|
+
Took: 0.00117 seconds
|
241
|
+
```
|
242
|
+
|
243
|
+
Local gateway is implemented with [Roda](https://github.com/jeremyevans/roda).
|
244
|
+
|
245
|
+
### Manipulating generated CloudFormation template
|
246
|
+
|
247
|
+
Modulator#init_stack will return [Humidifier](https://github.com/kddeisz/humidifier) instance which allows for easy manipulation of generated CF template. If you need to add more resources or tweak existing ones please consult its documentation.
|
248
|
+
|
249
|
+
One example of extending the template is Modulator#add_policy which adds extra policies to lambdas by passing optional values to init method:
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
Modulator.init_stack(
|
253
|
+
lambda_policies: [{name: :dynamo_db, prefixes: ['my-app']}],
|
254
|
+
)
|
255
|
+
```
|
256
|
+
|
257
|
+
This will give lambdas an access to DynamoDB tables prefixed by 'my-app'. Alternatively you could do it directly by providing your own policy:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
stack.resources['LambdaRole'].properties['policies'] << my_policy_template
|
261
|
+
```
|
26
262
|
|
27
263
|
## Development
|
28
264
|
|
data/lib/modulator.rb
CHANGED
@@ -1,38 +1,56 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
|
-
require 'modulator/
|
4
|
-
require 'modulator/
|
3
|
+
require 'modulator/lambda_handler'
|
4
|
+
require 'modulator/stack/builder'
|
5
5
|
require 'utils'
|
6
6
|
|
7
7
|
module Modulator
|
8
8
|
module_function
|
9
|
-
LAMBDAS
|
9
|
+
LAMBDAS = {}
|
10
10
|
|
11
|
-
|
11
|
+
class << self
|
12
|
+
attr_accessor :stack, :registering_module
|
13
|
+
end
|
14
|
+
|
15
|
+
def register(lambda_def, **opts) # opts are for overrides
|
12
16
|
if lambda_def.is_a?(Hash)
|
13
|
-
|
17
|
+
register_from_hash(lambda_def)
|
14
18
|
else
|
15
|
-
|
19
|
+
register_from_module(lambda_def, **opts)
|
20
|
+
self.registering_module = lambda_def
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def wrap_with(wrapper_mod, only: nil, except: nil)
|
26
|
+
registering_module.singleton_methods.sort.each do |module_method|
|
27
|
+
next if only && !Array(only).include?(module_method)
|
28
|
+
next if except && Array(except).include?(module_method)
|
29
|
+
LAMBDAS[calculate_registry_key(registering_module, module_method)][:wrapper] = {
|
30
|
+
name: wrapper_mod.to_s,
|
31
|
+
path: wrapper_mod.to_s.split('::').map(&:downcase).join('/'), # file name
|
32
|
+
method: 'call'
|
33
|
+
}
|
16
34
|
end
|
17
35
|
end
|
18
36
|
|
19
|
-
def
|
37
|
+
def register_from_hash(hash)
|
20
38
|
LAMBDAS[hash[:name]] = {
|
21
|
-
name:
|
22
|
-
gateway:
|
23
|
-
module:
|
24
|
-
wrapper:
|
25
|
-
env:
|
39
|
+
name: hash[:name],
|
40
|
+
gateway: hash[:gateway],
|
41
|
+
module: hash[:module],
|
42
|
+
wrapper: hash[:wrapper] || {},
|
43
|
+
env: hash[:env] || {},
|
26
44
|
settings: hash[:settings] || {}
|
27
45
|
}
|
28
46
|
end
|
29
47
|
|
30
|
-
def
|
48
|
+
def register_from_module(mod, **opts)
|
31
49
|
mod.singleton_methods.sort.each do |module_method|
|
32
|
-
module_name
|
33
|
-
module_names
|
34
|
-
verb
|
35
|
-
path_fragments
|
50
|
+
module_name = mod.to_s
|
51
|
+
module_names = module_name.split('::').map(&:downcase)
|
52
|
+
verb = 'GET'
|
53
|
+
path_fragments = module_names.dup
|
36
54
|
|
37
55
|
# process parameters
|
38
56
|
# method(a, b, c = 1, *args, d:, e: 2, **opts)
|
@@ -48,67 +66,85 @@ module Modulator
|
|
48
66
|
verb = 'POST' if param_type == :key
|
49
67
|
end
|
50
68
|
|
51
|
-
# delete is special case based on method name
|
69
|
+
# delete is a special case based on method name
|
52
70
|
verb = 'DELETE' if %w[destroy delete remove implode].include? module_method.to_s
|
53
71
|
|
54
72
|
# finalize path
|
55
73
|
path_fragments << module_method
|
56
74
|
path = path_fragments.join('/')
|
75
|
+
registry_key = calculate_registry_key(mod, module_method)
|
57
76
|
|
58
|
-
|
77
|
+
register_from_hash(
|
59
78
|
{
|
60
|
-
name:
|
79
|
+
name: registry_key,
|
61
80
|
gateway: {
|
62
81
|
verb: opts.dig(module_method, :gateway, :verb) || verb,
|
63
82
|
path: opts.dig(module_method, :gateway, :path) || path
|
64
83
|
},
|
65
84
|
module: {
|
66
|
-
name:
|
85
|
+
name: module_name,
|
67
86
|
method: module_method.to_s,
|
68
|
-
path:
|
87
|
+
path: module_names.join('/') # file name
|
69
88
|
},
|
70
|
-
wrapper:
|
71
|
-
env:
|
89
|
+
wrapper: opts.dig(module_method, :wrapper) || opts[:wrapper],
|
90
|
+
env: opts.dig(module_method, :env),
|
72
91
|
settings: opts.dig(module_method, :settings)
|
73
92
|
}
|
74
93
|
)
|
75
94
|
end
|
76
95
|
end
|
77
96
|
|
78
|
-
|
97
|
+
# lambda definition reference
|
98
|
+
def calculate_registry_key(mod, module_method)
|
99
|
+
module_names = mod.to_s.split('::').map(&:downcase)
|
100
|
+
"#{module_names.join('-')}-#{module_method}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# local gateway helper
|
104
|
+
def set_env_values(lambda_def)
|
79
105
|
# remove wrapper if already set
|
80
|
-
%
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
106
|
+
%i(name method path).each{|key| ENV.delete("wrapper_#{key}")}
|
107
|
+
|
108
|
+
# set env for each group
|
109
|
+
%i(module gateway wrapper env).each do |group_key|
|
110
|
+
lambda_def[group_key]&.each do |key, value|
|
111
|
+
key = "#{group_key}_#{key}" if group_key != :env
|
112
|
+
ENV[key.to_s] = value.to_s
|
113
|
+
end
|
114
|
+
end
|
86
115
|
end
|
87
116
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
117
|
+
# init humidifier stack instance
|
118
|
+
def init_stack(app_name: Pathname.getwd.basename.to_s, s3_bucket:, **stack_opts)
|
119
|
+
stack = StackBuilder.init({
|
120
|
+
app_name: app_name.camelize,
|
121
|
+
s3_bucket: s3_bucket,
|
92
122
|
}.merge(stack_opts))
|
93
123
|
|
124
|
+
# validate stack
|
125
|
+
# puts 'Validating stack'
|
126
|
+
# puts '- it is valid' if stack.valid?
|
127
|
+
|
128
|
+
self.stack = stack
|
129
|
+
generate_endoints if LAMBDAS.any?
|
130
|
+
|
131
|
+
# return instance
|
132
|
+
stack
|
133
|
+
end
|
134
|
+
|
135
|
+
# delegate to stack instance
|
136
|
+
def generate_endoints
|
94
137
|
# add lambdas to stack
|
95
138
|
puts 'Generating endpoints'
|
96
139
|
LAMBDAS.each do |name, config|
|
97
140
|
puts "- adding #{config.dig(:module, :name)}.#{config.dig(:module, :method)} to #{config.dig(:gateway, :path)}"
|
98
141
|
stack.add_lambda_endpoint(
|
99
|
-
gateway:
|
100
|
-
mod:
|
101
|
-
wrapper:
|
102
|
-
env:
|
142
|
+
gateway: config[:gateway],
|
143
|
+
mod: config[:module],
|
144
|
+
wrapper: config[:wrapper] || {},
|
145
|
+
env: config[:env] || {},
|
103
146
|
settings: config[:settings] || {}
|
104
147
|
)
|
105
148
|
end
|
106
|
-
|
107
|
-
# validate stack
|
108
|
-
# puts 'Validating stack'
|
109
|
-
# puts '- it is valid' if stack.valid?
|
110
|
-
|
111
|
-
# return humidifier instance
|
112
|
-
stack
|
113
149
|
end
|
114
150
|
end
|