LFA 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +14 -0
- data/LFA.gemspec +34 -0
- data/LICENSE +21 -0
- data/README.md +363 -0
- data/Rakefile +12 -0
- data/config.ru +2 -0
- data/config.yaml +79 -0
- data/data.rb +27 -0
- data/lib/LFA/adapter/environment.rb +36 -0
- data/lib/LFA/adapter/executor.rb +49 -0
- data/lib/LFA/adapter/lambda_rack_bridge.rb +236 -0
- data/lib/LFA/adapter.rb +51 -0
- data/lib/LFA/handler/cors.rb +80 -0
- data/lib/LFA/router/config.rb +185 -0
- data/lib/LFA/router.rb +28 -0
- data/lib/LFA/version.rb +5 -0
- data/lib/LFA.rb +12 -0
- data/myfunc.rb +41 -0
- data/sig/LFA.rbs +4 -0
- data/test.rb +11 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f5b77411891b6b8397a92d8c12dabb238673b7cd1c0d0b63aa8d7c1028f82d5b
|
4
|
+
data.tar.gz: 3d5c792faefa9d0ec321e0b1c18e32c962f1b805cb33e87a50624615f2ff5cfe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3d9d9dbd849df541ee2f35e6f72d4699f925d657213c874b48405fcf004bdfe3be61899ab094afd4dcea3bc6cb6121aa21f05a74e36a816ed71faaac2456d505
|
7
|
+
data.tar.gz: 00e90234cdfadff79c2107e26717e671b39515e7ab301c21cde26e98952a7ada9f7881cdd546e5da9f4e2a75b1e26f7e76d97ec832f3bb74f7de6ca8d04547b9
|
data/Gemfile
ADDED
data/LFA.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/LFA/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "LFA"
|
7
|
+
spec.version = LFA::VERSION
|
8
|
+
spec.authors = ["Satoshi Tagomori"]
|
9
|
+
spec.email = ["tagomoris@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Lambda Function Adapter for web applications"
|
12
|
+
spec.description = "Web application framework to mount AWS Lambda functions as request handlers"
|
13
|
+
spec.homepage = "https://github.com/tagomoris/LFA"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
|
18
|
+
# Specify which files should be added to the gem when it is released.
|
19
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
22
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
23
|
+
end
|
24
|
+
end
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.required_ruby_version = ">= 3.2.0"
|
30
|
+
|
31
|
+
spec.add_dependency "rack"
|
32
|
+
spec.add_development_dependency "test-unit"
|
33
|
+
spec.add_development_dependency "rake"
|
34
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 Satoshi Tagomori
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,363 @@
|
|
1
|
+
# LFA - Lambda Function Adapter
|
2
|
+
|
3
|
+
**LFA is under development, and not released yet.**
|
4
|
+
|
5
|
+
LFA is a web application framework in Ruby (Rack framework), to run AWS Lambda functions (migrated from AWS API Gateway integration) on Ruby's application servers (Unicorn, Puma, etc).
|
6
|
+
|
7
|
+
The main purposes of LFA are:
|
8
|
+
|
9
|
+
* run Lambda functions on EC2 (or container hosting services) **temporarily**
|
10
|
+
* test Lambda function as web request handler on our laptop
|
11
|
+
|
12
|
+
LFA was initially designed to host webapps migrated from AWS Lambda to EC2/ECS/k8s/etc by moving functions to app servers as-is. This provides time to engineers for re-implemention of native stand alone web applications.
|
13
|
+
|
14
|
+
The 2nd purpose (testing on laptop) was found eventually during the development of LFA. We can't test web request handlers of AWS Lambda functions on our laptop directly (because we don't have local API Gateway), but we can do it locally with LFA on laptop. This MAY improve our dev-experience a little, or more.
|
15
|
+
|
16
|
+
## Features
|
17
|
+
|
18
|
+
LFA mounts Lambda functions on request paths, and routes requests to those functions.
|
19
|
+
|
20
|
+
Lambda functions are:
|
21
|
+
|
22
|
+
* mounted on specified paths, by AWS Lambda's handler specification `funcfile.Modname.method_name`
|
23
|
+
* configured by `ENV` environment variables (just like AWS Lambda)
|
24
|
+
* called with `event` and `context` arguments per HTTP request (translated from Rack `env`)
|
25
|
+
|
26
|
+
LFA uses a YAML file to:
|
27
|
+
|
28
|
+
* configure functions with those environment variables
|
29
|
+
* configure resource-function relations (just like AWS API Gateway)
|
30
|
+
|
31
|
+
Functions on LFA will be loaded in (semi-)isolated module spaces. Functions will not effect to other functions (at least, unintentionally). See "Limitations" section below about the function isolation.
|
32
|
+
|
33
|
+
### Features not supported yet
|
34
|
+
|
35
|
+
The features below are not supported yet, but will be implemented eventually.
|
36
|
+
|
37
|
+
* CORS support and built-in `OPTIONS` method request handler
|
38
|
+
* Loading functions from `.zip` archives
|
39
|
+
|
40
|
+
## How to run LFA
|
41
|
+
|
42
|
+
### Installation
|
43
|
+
|
44
|
+
Add LFA to your application's Gemfile:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
gem 'LFA'
|
48
|
+
```
|
49
|
+
|
50
|
+
And then execute:
|
51
|
+
|
52
|
+
$ bundle install
|
53
|
+
|
54
|
+
Or install it yourself as:
|
55
|
+
|
56
|
+
$ gem install LFA
|
57
|
+
|
58
|
+
### Configuration
|
59
|
+
|
60
|
+
Write a YAML configuration file, and a `config.ru` Rack app file.
|
61
|
+
|
62
|
+
Rack app file is just to load LFA with the following YAML file.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
# config.ru
|
66
|
+
require 'LFA'
|
67
|
+
run LFA.ignition!('config.yaml')
|
68
|
+
```
|
69
|
+
|
70
|
+
The YAML configuration file is to describe application resources, and functions to be called per request. The YAML requires 2 child elements:
|
71
|
+
|
72
|
+
* `resources`: the list of nested resources, almost equal to the resources on AWS API Gateway
|
73
|
+
* `functions`: the list of functions, referred from resources
|
74
|
+
|
75
|
+
```yaml
|
76
|
+
# config.yaml
|
77
|
+
---
|
78
|
+
resources:
|
79
|
+
- path: /api
|
80
|
+
resources:
|
81
|
+
- path: /country
|
82
|
+
methods:
|
83
|
+
GET: myfunc-countries
|
84
|
+
- path: /data
|
85
|
+
resources:
|
86
|
+
- path: /csv
|
87
|
+
methods:
|
88
|
+
GET: myfunc-data-csv
|
89
|
+
- path: /json
|
90
|
+
methods:
|
91
|
+
GET: myfunc-data-json
|
92
|
+
- path: /web
|
93
|
+
resources:
|
94
|
+
- path: /blog/{entry_id}
|
95
|
+
methods:
|
96
|
+
GET: blog-entry-get
|
97
|
+
- path: /assets/{asset_path+}
|
98
|
+
methods:
|
99
|
+
ANY: blog-asset-access
|
100
|
+
functions:
|
101
|
+
- name: myfunc-countries
|
102
|
+
handler: myfunc.Countries.process
|
103
|
+
env:
|
104
|
+
DATABASE_HOSTNAME: mydb.local
|
105
|
+
DATABASE_PASSWORD: this-is-the-top-secret
|
106
|
+
- name: myfunc-data-csv
|
107
|
+
handler: myfunc.Data.process
|
108
|
+
env:
|
109
|
+
OUTPUT_DATA_TYPE: csv
|
110
|
+
- name: myfunc-data-json
|
111
|
+
handler: myfunc.Data.process
|
112
|
+
env:
|
113
|
+
OUTPUT_DATA_TYPE: json
|
114
|
+
# omit blog-entry-get and blog-asset-access
|
115
|
+
```
|
116
|
+
|
117
|
+
The actual Lambda functions should be placed on the same directory with those configuration files.
|
118
|
+
LFA will load `Countries` module from `myfunc.rb`, then call its `process` method for the request path `/api/country`.
|
119
|
+
|
120
|
+
### Ignition
|
121
|
+
|
122
|
+
Run your Rack application as usual (for example, with `puma`):
|
123
|
+
|
124
|
+
$ puma config.ru
|
125
|
+
|
126
|
+
Or, using `rackup` (requires `gem i rackup`)
|
127
|
+
|
128
|
+
$ rackup --server puma config.ru
|
129
|
+
|
130
|
+
## Limitation
|
131
|
+
|
132
|
+
The separation of Lambda functions is not perfect. That means:
|
133
|
+
|
134
|
+
* The Ruby script `funcfile` of Lambda handler `funcfile.Modname.method_name` is loaded in an isolated namespace
|
135
|
+
* Libraries `require`-ed from the Lambda file are NOT isolated, and be shared by all Lambda functions in the process
|
136
|
+
* `ENV` access out of Lambda handler context in `require`-ed files will see the original `ENV`, instead of `env` configured
|
137
|
+
|
138
|
+
To avoid those limitations, the Lambda functions loaded by LFA should take care of the following things.
|
139
|
+
|
140
|
+
### Use common set of libraries
|
141
|
+
|
142
|
+
Lambda functions should use the common set of libraries. That means, Lambda functions should:
|
143
|
+
|
144
|
+
* have a common `lib` directory for its internal libraries
|
145
|
+
* have a single `Gemfile` (and `Gemfile.lock`) to use the common set of gems
|
146
|
+
|
147
|
+
If the Lambda functions are of single application, these things will be satisfied usually. Otherwise, don't share a single LFA process.
|
148
|
+
|
149
|
+
### Create handler object/module in function files
|
150
|
+
|
151
|
+
If your Lambda function's handler module is defined in `funcfile`, it's totally OK.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
# func.rb
|
155
|
+
|
156
|
+
module Modname
|
157
|
+
def self.method_name(event:, context:)
|
158
|
+
# ...
|
159
|
+
end
|
160
|
+
end
|
161
|
+
# OK
|
162
|
+
```
|
163
|
+
|
164
|
+
But if the `funcfile` just requires your library and the library defines the handler module or instantiate the handler object, it MAY be NOT OK because that module/object are shared between different functions. It SHOULD be BAD when the handler object has internal states.
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# func.rb
|
168
|
+
require_relative './lib/myapp/handler'
|
169
|
+
# and it provides Modname module
|
170
|
+
|
171
|
+
# MAY be NOT OK
|
172
|
+
```
|
173
|
+
|
174
|
+
If your Lambda handler has to have internal states, you should define a class, and instantiate it in `funcfile`, as following:
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
# lib/myapp/handler.rb
|
178
|
+
class MyHandler
|
179
|
+
def initialize
|
180
|
+
@internal_cache = {}
|
181
|
+
end
|
182
|
+
|
183
|
+
def process(event:, context:)
|
184
|
+
# ...
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# func.rb
|
189
|
+
require_relative './lib/myapp/handler'
|
190
|
+
Handler = MyHandler.new
|
191
|
+
|
192
|
+
# and specify the handler: func.Handler.process
|
193
|
+
# OK!
|
194
|
+
```
|
195
|
+
|
196
|
+
### Refer ENV in dynamic manner
|
197
|
+
|
198
|
+
LFA overrides the `ENV` reference in `funcfile`.
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
# func.rb
|
202
|
+
module HandlerA
|
203
|
+
DB_HOST = ENV['DB_HOST'] # this refers configured `env`
|
204
|
+
|
205
|
+
def process(event:, context:)
|
206
|
+
# ...
|
207
|
+
end
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
But the `ENV` reference in the static context (code not in methods) in required libraries will refer the original environment variables of the LFA process, instead of configured `env` key-value pairs.
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
# func.rb
|
215
|
+
require_relative './lib/myapp/handler'
|
216
|
+
Handler = HandlerB.new
|
217
|
+
|
218
|
+
# lib/myapp/handler.rb
|
219
|
+
class HandlerB
|
220
|
+
DB_HOST = ENV['DB_HOST'] # this refers the process's environment variables
|
221
|
+
|
222
|
+
def process(event:, context:)
|
223
|
+
# ...
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
Even in libraries, code in methods called from `funcfile` will refer the configured `env`. So, `ENV` references should be written in methods, called dynamically.
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
# func.rb
|
232
|
+
require_relative './lib/myapp/handler'
|
233
|
+
Handler = HandlerB.new
|
234
|
+
|
235
|
+
# lib/myapp/handler.rb
|
236
|
+
class HandlerB
|
237
|
+
def initialize
|
238
|
+
@db_host = ENV['DB_HOST'] # this refers the `env` configured
|
239
|
+
end
|
240
|
+
|
241
|
+
def process(event:, context:)
|
242
|
+
# Or, refer ENV in this handler method
|
243
|
+
# ...
|
244
|
+
end
|
245
|
+
end
|
246
|
+
```
|
247
|
+
|
248
|
+
Gem libraries referring `ENV` should be initiated in methods, dynamically, as well.
|
249
|
+
|
250
|
+
## Predefined Handlers
|
251
|
+
|
252
|
+
### CORS: Preflight Request Handler
|
253
|
+
|
254
|
+
Just like enabling `CORS` on AWS API Gateway, LFA has the built-in CORS preflight request handler. It can respond to `OPTIONS` request on resources, instead of Lambda functions.
|
255
|
+
|
256
|
+
For the details of CORS requests/responses, see [MDN documents](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) or any other resources.
|
257
|
+
|
258
|
+
To enable CORS preflight request handler, specify `CORS` as `handler`:
|
259
|
+
|
260
|
+
```yaml
|
261
|
+
resources:
|
262
|
+
- path: /a
|
263
|
+
methods:
|
264
|
+
GET: func1-myapp1-yay
|
265
|
+
OPTIONS: cors-1
|
266
|
+
functions:
|
267
|
+
- name: cors-1
|
268
|
+
handler: CORS
|
269
|
+
params:
|
270
|
+
allowOrigins:
|
271
|
+
- '*'
|
272
|
+
allowMethods:
|
273
|
+
- GET
|
274
|
+
- OPTIONS
|
275
|
+
allowHeaders:
|
276
|
+
- Content-Type
|
277
|
+
- Authorization
|
278
|
+
```
|
279
|
+
|
280
|
+
All parameters of the handler should be under `params` key. Available parameters are:
|
281
|
+
|
282
|
+
#### allowOrigins
|
283
|
+
|
284
|
+
Fixed values for the response header `access-control-allow-origin`. A string, or list of strings.
|
285
|
+
|
286
|
+
```yaml
|
287
|
+
allowOrigins: '*'
|
288
|
+
allowOrigins: 'https://example.com, https://www.example.com'
|
289
|
+
allowOrigins:
|
290
|
+
- 'https://example.com'
|
291
|
+
- 'https://www.example.com'
|
292
|
+
```
|
293
|
+
|
294
|
+
Exclusive with `mirrorAllowOrigin`. One of `allowOrigins` or `mirrorAllowOrigin` should be specified.
|
295
|
+
|
296
|
+
#### mirrorAllowOrigin
|
297
|
+
|
298
|
+
`true` or `false` (or missing) value, which specifies to respond `access-control-allow-origin` value with the value of the request header `Origin`.
|
299
|
+
|
300
|
+
```yaml
|
301
|
+
mirrorAllowOrigin: true
|
302
|
+
```
|
303
|
+
|
304
|
+
The handler with `mirrorAllowOrigin: true` will respond `access-control-allow-origin: https://example.com` for the request with a header `origin: https://example.com`.
|
305
|
+
|
306
|
+
Exclusive with `allowOrigins`. One of `allowOrigins` or `mirrorAllowOrigin` should be specified.
|
307
|
+
|
308
|
+
#### allowMethods
|
309
|
+
|
310
|
+
Fixed values for the response header `access-control-allow-methods`. A string, or list of strings. Should be specified.
|
311
|
+
|
312
|
+
```yaml
|
313
|
+
allowMethods: GET, POST, PUT, DELETE, OPTIONS
|
314
|
+
allowMethods:
|
315
|
+
- GET
|
316
|
+
- POST
|
317
|
+
- OPTIONS
|
318
|
+
```
|
319
|
+
|
320
|
+
#### allowHeaders
|
321
|
+
|
322
|
+
Fixed values for the response header `access-control-allow-headers`. A string, or list of strings. Optional.
|
323
|
+
|
324
|
+
```yaml
|
325
|
+
allowHeaders: x-my-custom-header
|
326
|
+
allowHeaders:
|
327
|
+
- authorization
|
328
|
+
- x-my-custom-header
|
329
|
+
```
|
330
|
+
|
331
|
+
#### allowCredentials
|
332
|
+
|
333
|
+
`true` or `false` (or missing) to specify `access-control-allow-credentials`. Optional.
|
334
|
+
|
335
|
+
```yaml
|
336
|
+
allowCredentials: true
|
337
|
+
```
|
338
|
+
|
339
|
+
#### exposeHeaders
|
340
|
+
|
341
|
+
Fixed values for the response header `access-control-expose-headers`. A string, or list of strings. Optional.
|
342
|
+
|
343
|
+
```yaml
|
344
|
+
exposeHeaders: x-my-custom-header
|
345
|
+
exposeHeaders:
|
346
|
+
- x-my-custom-header
|
347
|
+
```
|
348
|
+
|
349
|
+
#### maxAge
|
350
|
+
|
351
|
+
A fixed number (seconds) for the response header `access-control-max-age`. Optional.
|
352
|
+
|
353
|
+
```yaml
|
354
|
+
maxAge: 86400
|
355
|
+
```
|
356
|
+
|
357
|
+
## Contributing
|
358
|
+
|
359
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/tagomoris/LFA.
|
360
|
+
|
361
|
+
## License
|
362
|
+
|
363
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/config.ru
ADDED
data/config.yaml
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
---
|
2
|
+
resources:
|
3
|
+
- path: /api
|
4
|
+
resources:
|
5
|
+
- path: /country
|
6
|
+
methods:
|
7
|
+
GET: myfunc-countries
|
8
|
+
- path: /language
|
9
|
+
methods:
|
10
|
+
GET: myfunc2-list-language
|
11
|
+
PUT: myfunc2-register-language
|
12
|
+
- path: /data
|
13
|
+
resources:
|
14
|
+
- path: /csv
|
15
|
+
methods:
|
16
|
+
GET: myfunc-data-csv
|
17
|
+
- path: /json
|
18
|
+
methods:
|
19
|
+
GET: myfunc-data-json
|
20
|
+
- path: /city
|
21
|
+
resources:
|
22
|
+
- path: /{city_name}
|
23
|
+
methods:
|
24
|
+
GET: myfunc-cities
|
25
|
+
- path: /town
|
26
|
+
resources:
|
27
|
+
- path: /{town_params+}
|
28
|
+
methods:
|
29
|
+
ANY: myfunc-towns
|
30
|
+
- path: /webapi
|
31
|
+
methods:
|
32
|
+
GET: webapi-func-handler
|
33
|
+
POST: webapi-func-handler
|
34
|
+
OPTIONS: cors-1
|
35
|
+
|
36
|
+
functions:
|
37
|
+
- name: myfunc-countries
|
38
|
+
handler: myfunc.Countries.process
|
39
|
+
env:
|
40
|
+
KEY1: yay
|
41
|
+
KEY2: foooooo
|
42
|
+
- name: myfunc2-list-language
|
43
|
+
handler: myfunc2.Handler::Language.list
|
44
|
+
env:
|
45
|
+
KEY1: yayyay
|
46
|
+
KEY2: foooooooooo
|
47
|
+
- name: myfunc2-register-language
|
48
|
+
handler: myfunc2.Handler::Language.register
|
49
|
+
env:
|
50
|
+
KEY1: yyyyay
|
51
|
+
KEY2: foo?
|
52
|
+
- name: myfunc-data-csv
|
53
|
+
handler: myfunc.Data.process
|
54
|
+
env:
|
55
|
+
OUTPUT_DATA_TYPE: csv
|
56
|
+
- name: myfunc-data-json
|
57
|
+
handler: myfunc.Data.process
|
58
|
+
env:
|
59
|
+
OUTPUT_DATA_TYPE: json
|
60
|
+
- name: myfunc-cities
|
61
|
+
handler: myfunc.City.process
|
62
|
+
- name: myfunc-towns
|
63
|
+
handler: myfunc.Town.process
|
64
|
+
- name: webapi-func-handler
|
65
|
+
handler: webapi.Func.process
|
66
|
+
- name: cors-1
|
67
|
+
handler: CORS
|
68
|
+
params:
|
69
|
+
mirrorAllowOrigin: true
|
70
|
+
# allowOrigin: '*'
|
71
|
+
allowCredentials: true
|
72
|
+
allowMethods:
|
73
|
+
- GET
|
74
|
+
- POST
|
75
|
+
- OPTIONS
|
76
|
+
allowHeaders:
|
77
|
+
- Content-Type
|
78
|
+
- Authorization
|
79
|
+
maxAge: 3600
|
data/data.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Data
|
4
|
+
def self.process(event:, context:)
|
5
|
+
data_type = ENV.fetch("OUTPUT_DATA_TYPE", "txt")
|
6
|
+
case data_type
|
7
|
+
when "json"
|
8
|
+
{
|
9
|
+
statusCode: 200,
|
10
|
+
body: {"data" => "boo", "message" => "foo"}.to_json,
|
11
|
+
headers: {"content-type" => "application/json"},
|
12
|
+
}
|
13
|
+
when "csv"
|
14
|
+
{
|
15
|
+
statusCode: 200,
|
16
|
+
body: "data,yaaaay",
|
17
|
+
headers: {"content-type" => "text/csv"},
|
18
|
+
}
|
19
|
+
else
|
20
|
+
{
|
21
|
+
statusCode: 200,
|
22
|
+
body: "data. yaaaaay!",
|
23
|
+
headers: {"content-type" => "text/plain"},
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module LFA
|
6
|
+
class Adapter
|
7
|
+
class EnvMimic < Delegator
|
8
|
+
def initialize
|
9
|
+
@is_active = false
|
10
|
+
@box = nil
|
11
|
+
@env = ENV
|
12
|
+
end
|
13
|
+
|
14
|
+
def __getobj__
|
15
|
+
if @is_active
|
16
|
+
@box
|
17
|
+
else
|
18
|
+
@env
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def __setobj__(obj)
|
23
|
+
@box = obj
|
24
|
+
end
|
25
|
+
|
26
|
+
def mimic!(env)
|
27
|
+
@box = env.dup
|
28
|
+
@is_active = true
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
@box = nil
|
32
|
+
@is_active = false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../handler/cors'
|
4
|
+
|
5
|
+
module LFA
|
6
|
+
class Adapter
|
7
|
+
class Executor
|
8
|
+
BUILT_IN_HANDLERS = {
|
9
|
+
'CORS' => Handler::CORSPreflight,
|
10
|
+
}
|
11
|
+
|
12
|
+
def self.setup(function)
|
13
|
+
if function.handler.builtin?
|
14
|
+
BUILT_IN_HANDLERS[function.handler.name].new(function.params)
|
15
|
+
else
|
16
|
+
env = Hash[*(function.env.map{|k,v| [k.to_s, v] }.flatten)]
|
17
|
+
Executor.new(function.name, env, function.handler)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(name, env, handler)
|
22
|
+
@name = name
|
23
|
+
@env = env
|
24
|
+
@klass = handler.klass.to_s
|
25
|
+
# @klass must be a string because `const_get(:"A::B")` is not resolved
|
26
|
+
# https://bugs.ruby-lang.org/issues/12319
|
27
|
+
@method = handler.method.to_sym
|
28
|
+
|
29
|
+
@enclosure = Module.new
|
30
|
+
@enclosure.const_set(:ENV, @env)
|
31
|
+
m1 = Module.new
|
32
|
+
m1.const_set(:ENV, @env)
|
33
|
+
path = handler.path
|
34
|
+
ENV.mimic!(@env) do
|
35
|
+
load(path, @enclosure)
|
36
|
+
end
|
37
|
+
@handler_instance = @enclosure.const_get(@klass)
|
38
|
+
raise "failed to load the handler module '#{@klass}'" unless @handler_instance
|
39
|
+
@handler_method = @handler_instance.method(@method)
|
40
|
+
end
|
41
|
+
|
42
|
+
def call(event:, context:)
|
43
|
+
ENV.mimic!(@env) do
|
44
|
+
@handler_method.call(event: event, context: context)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|