yake 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e7f5484562df0ab45af1304729d8f5c6edc2a66082ba4bcd4ca718bcfdba1d96
4
+ data.tar.gz: c63cb492e9a31e9cd29a06c93493bb28f7f1823eae6eb8f06a4d889b204048bc
5
+ SHA512:
6
+ metadata.gz: fec877aefb902caa3b64562c255ac9d8f33dbbe878de88af3bfc985dacac7f4e7f2ad8c02dbc873987defb1c64f85695d2d6da035596ad5e3ee1c168a5303f27
7
+ data.tar.gz: 5641f87b78f5fce7c1eec4cfd8c422afa56247ad27ab936587840563998feb98f1198e291c2b9c508f30414cf282a6f988ed0d79db18c639b21e6560266deba6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Alexander Mancevice
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/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Alexander Mancevice
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # λake
2
+
3
+ Write your AWS Lambda function handlers using a Rake-like declarative syntax:
4
+
5
+ ```ruby
6
+ # ./lambda_function.rb
7
+ require "yake"
8
+
9
+ handler :lambda_handler do |event|
10
+ # Your code here
11
+ end
12
+
13
+ # Handler signature: `lambda_function.lambda_handler`
14
+ ```
15
+
16
+ You can even declare Sinatra-like API Gateway routes for a main entrypoint:
17
+
18
+ ```ruby
19
+ # ./lambda_function.rb
20
+ require "yake/api"
21
+
22
+ header "content-type" => "application/json"
23
+
24
+ get "/fizz" do |handler|
25
+ respond 200, { ok: true }.to_json
26
+ end
27
+
28
+ handler :lambda_handler do |event|
29
+ route event
30
+ rescue => err
31
+ respond 500, { message: err.message }.to_json
32
+ end
33
+
34
+ # Handler signature: `lambda_function.lambda_handler`
35
+ ```
36
+
37
+ ## Installation
38
+
39
+ Add this line to your application's Gemfile:
40
+
41
+ ```ruby
42
+ gem "yake"
43
+ ```
44
+
45
+ And then execute:
46
+
47
+ ```bash
48
+ bundle install
49
+ ```
50
+
51
+ Or install it yourself as:
52
+
53
+ ```bash
54
+ gem install yake
55
+ ```
56
+
57
+ ## Why Use It?
58
+
59
+ So why use `yake` for your Lambda functions?
60
+
61
+ #### Zero Dependencies
62
+
63
+ `yake` does not depend on any other gems, using the Ruby stdlib only. This helps keep your Lambda packages slim & speedy.
64
+
65
+ #### Event Logging
66
+
67
+ By default, the `handler` function wraps its block in log lines formatted to match the style of Amazon's native Lambda logs sent to CloudWatch. Each invocation of the handler logs the input event and the returned value prefixed with the ID of the request.
68
+
69
+ Example log lines:
70
+
71
+ ```
72
+ START RequestId: 149c500f-028a-4b57-8977-0ef568cf8caf Version: $LATEST
73
+ INFO RequestId: 149c500f-028a-4b57-8977-0ef568cf8caf EVENT { … }
74
+
75
+ INFO RequestId: 149c500f-028a-4b57-8977-0ef568cf8caf RETURN { … }
76
+ END RequestId: 149c500f-028a-4b57-8977-0ef568cf8caf
77
+ REPORT RequestId: 149c500f-028a-4b57-8977-0ef568cf8caf Duration: 43.97 ms Billed Duration: 44 ms Memory Size: 128 MB Max Memory Used: 77 MB
78
+ ```
79
+
80
+ This makes gathering logs lines for a particular execution in CloudWatch much easier.
81
+
82
+ This feature can be disabled by adding a declaration in your handler:
83
+
84
+ ```ruby
85
+ logging :off
86
+ ```
87
+
88
+ #### API Routes
89
+
90
+ A common use of Lambda functions is as a proxy for API Gateway. Oftentimes users will deploy a single Lambda function to handle all requests coming from API Gateway.
91
+
92
+ Requiring the `yake/api` module will add the API-specific DSL into your handler.
93
+
94
+ Define API routes using Sinatra-like syntax
95
+
96
+ ```ruby
97
+ # Declare 'DELETE /…' route key
98
+ delete "/…" do |event|
99
+ # …
100
+ end
101
+
102
+ # Declare 'GET /…' route key
103
+ get "/…" do |event|
104
+ # …
105
+ end
106
+
107
+ # Declare 'HEAD /…' route key
108
+ head "/…" do |event|
109
+ # …
110
+ end
111
+
112
+ # Declare 'OPTIONS /…' route key
113
+ options "/…" do |event|
114
+ # …
115
+ end
116
+
117
+ # Declare 'PATCH /…' route key
118
+ patch "/…" do |event|
119
+ # …
120
+ end
121
+
122
+ # Declare 'POST /…' route key
123
+ post "/…" do |event|
124
+ # …
125
+ end
126
+
127
+ # Declare 'PUT /…' route key
128
+ put "/…" do |event|
129
+ # …
130
+ end
131
+
132
+ ```
133
+
134
+ Helper methods are also made available to help produce a response for API Gateway:
135
+
136
+ Set a default header for ALL responses:
137
+
138
+ ```ruby
139
+ header "content-type" => "application/json; charset=utf-8"
140
+ ```
141
+
142
+ Produce an API Gateway-style response object:
143
+
144
+ ```ruby
145
+ respond 200, { ok: true }.to_json, "x-extra-header" => "fizz"
146
+ # {
147
+ # "statusCode" => 200,
148
+ # "body" => '{"ok":true}',
149
+ # "headers" => { "x-extra-header" => "fizz" }
150
+ # }
151
+ ```
152
+
153
+ Route an event to one of the declared routes:
154
+
155
+ ```ruby
156
+ begin
157
+ route event
158
+ rescue Yake::UndeclaredRoute => err
159
+ respond 404, { message: err.message }.to_json
160
+ rescue => err
161
+ respond 500 { message: err.message }.to_json
162
+ end
163
+ ```
164
+
165
+ ## Development
166
+
167
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
168
+
169
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
170
+
171
+ ## Contributing
172
+
173
+ Bug reports and pull requests are welcome on GitHub at https://github.com/amancevice/yake.
174
+
175
+ ## License
176
+
177
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
178
+
179
+ ```
180
+
181
+ ```
data/lib/yake.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "yake/version"
4
+ require_relative "yake/logger"
5
+ require_relative "yake/errors"
6
+ require_relative "yake/dsl"
data/lib/yake/api.rb ADDED
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ require "yake"
6
+ require_relative "errors"
7
+
8
+ module Yake
9
+ module API
10
+ module DSL
11
+ ##
12
+ # Proxy handler for HTTP requests from Slack
13
+ def route(event, context = nil)
14
+ # Extract route method
15
+ method = event["routeKey"]
16
+ raise Yake::Errors::UndeclaredRoute, method unless respond_to?(method)
17
+
18
+ # Normalize headers
19
+ event["headers"]&.transform_keys!(&:downcase)
20
+
21
+ # Decode body if Base64-encoded
22
+ if event["isBase64Encoded"]
23
+ body = Base64.strict_decode64(event["body"])
24
+ event.update("body" => body, "isBase64Encoded" => false)
25
+ end
26
+
27
+ # Execute request
28
+ send(method, event, context)
29
+ end
30
+
31
+ ##
32
+ # Transform to API Gateway response
33
+ def respond(status_code, body = nil, **headers)
34
+ # Log response
35
+ log = "RESPONSE [#{ status_code }] #{ body }"
36
+ Yake.logger&.send(status_code.to_i >= 400 ? :error : :info, log)
37
+
38
+ # Set headers
39
+ content_length = (body&.length || 0).to_s
40
+ to_s_downcase = -> (key) { key.to_s.downcase }
41
+ headers = {
42
+ "content-length" => content_length,
43
+ **(@headers || {}),
44
+ **headers,
45
+ }.transform_keys(&to_s_downcase).compact
46
+
47
+ # Send response
48
+ { statusCode: status_code, headers: headers, body: body }.compact
49
+ end
50
+
51
+ ##
52
+ # Set default header
53
+ def header(headers)
54
+ (@headers ||= {}).update(headers)
55
+ end
56
+
57
+ ##
58
+ # Define DELETE route
59
+ def delete(path, &block)
60
+ define_singleton_method("DELETE #{ path }") { |*args| instance_exec(*args, &block) }
61
+ end
62
+
63
+ ##
64
+ # Define GET route
65
+ def get(path, &block)
66
+ define_singleton_method("GET #{ path }") { |*args| instance_exec(*args, &block) }
67
+ end
68
+
69
+ ##
70
+ # Define HEAD route
71
+ def head(path, &block)
72
+ define_singleton_method("HEAD #{ path }") { |*args| instance_exec(*args, &block) }
73
+ end
74
+
75
+ ##
76
+ # Define OPTIONS route
77
+ def options(path, &block)
78
+ define_singleton_method("OPTIONS #{ path }") { |*args| instance_exec(*args, &block) }
79
+ end
80
+
81
+ ##
82
+ # Define PATCH route
83
+ def patch(path, &block)
84
+ define_singleton_method("PATCH #{ path }") { |*args| instance_exec(*args, &block) }
85
+ end
86
+
87
+ ##
88
+ # Define POST route
89
+ def post(path, &block)
90
+ define_singleton_method("POST #{ path }") { |*args| instance_exec(*args, &block) }
91
+ end
92
+
93
+ ##
94
+ # Define PUT route
95
+ def put(path, &block)
96
+ define_singleton_method("PUT #{ path }") { |*args| instance_exec(*args, &block) }
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ extend Yake::API::DSL
data/lib/yake/dsl.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ require_relative "logger"
6
+
7
+ module Yake
8
+ module DSL
9
+ ##
10
+ # Lambda handler task wrapper
11
+ def handler(name, &block)
12
+ define_method(name) do |event:nil, context:nil|
13
+ Yake.logger.nil? ? yield(event, context) : Yake.logger.wrap(event, context, &block)
14
+ end
15
+ end
16
+
17
+ ##
18
+ # Turn logging on/off
19
+ def logging(switch, **options)
20
+ if switch == :on
21
+ Yake.logger = Yake::Logger.new
22
+ elsif switch == :off
23
+ Yake.logger = nil
24
+ else
25
+ raise Errors::UnknownLoggingSetting, switch
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ extend Yake::DSL
32
+ Yake.logger = Yake::Logger.new
@@ -0,0 +1,61 @@
1
+ module Yake
2
+ module Errors
3
+ class Error < StandardError; end
4
+
5
+ class UndeclaredRoute < Error
6
+ def initialize(method = nil)
7
+ super("No route declared for '#{ method }'")
8
+ end
9
+ end
10
+
11
+ class UnknownLoggingSetting < Error
12
+ def initialize(method = nil)
13
+ super("Unknown logging setting '#{ method }'. Use :on or :off")
14
+ end
15
+ end
16
+
17
+ # HTTP Errors
18
+
19
+ class BadRequest < Error; end # HTTP 400
20
+ class Unauthorized < Error; end # HTTP 401
21
+ class PaymentRequired < Error; end # HTTP 402
22
+ class Forbidden < Error; end # HTTP 403
23
+ class NotFound < Error; end # HTTP 404
24
+ class MethodNotAllowed < Error; end # HTTP 405
25
+ class NotAcceptable < Error; end # HTTP 406
26
+ class ProxyAuthenticationRequired < Error; end # HTTP 407
27
+ class RequestTimeout < Error; end # HTTP 408
28
+ class Conflict < Error; end # HTTP 409
29
+ class Gone < Error; end # HTTP 410
30
+ class LengthRequired < Error; end # HTTP 410
31
+ class PreconditionFailed < Error; end # HTTP 412
32
+ class PayloadTooLarge < Error; end # HTTP 413
33
+ class UriTooLong < Error; end # HTTP 414
34
+ class UnsupportedMediaType < Error; end # HTTP 415
35
+ class RangeNotSatisfiable < Error; end # HTTP 416
36
+ class ExpectationFailed < Error; end # HTTP 417
37
+ class ImATeapot < Error; end # HTTP 418
38
+ class EnhanceYourCalm < Error; end # HTTP 420
39
+ class MisdirectedRequest < Error; end # HTTP 421
40
+ class UnprocessableEntity < Error; end # HTTP 422
41
+ class Locked < Error; end # HTTP 423
42
+ class FailedDependency < Error; end # HTTP 424
43
+ class TooEarly < Error; end # HTTP 425
44
+ class UpgradeRequired < Error; end # HTTP 426
45
+ class PreconditionRequired < Error; end # HTTP 428
46
+ class TooManyRequests < Error; end # HTTP 429
47
+ class RequestHeaderFieldsTooLarge < Error; end # HTTP 431
48
+ class UnavailableForLegalReasons < Error; end # HTTP 451
49
+ class InternalServerError < Error; end # HTTP 500
50
+ class NotImplemented < Error; end # HTTP 501
51
+ class BadGateway < Error; end # HTTP 502
52
+ class ServiceUnavailable < Error; end # HTTP 503
53
+ class GatewayTimeout < Error; end # HTTP 504
54
+ class HttpVersionNotSupported < Error; end # HTTP 505
55
+ class VariantAlsoNegotiates < Error; end # HTTP 506
56
+ class InsufficientStorage < Error; end # HTTP 507
57
+ class LoopDetected < Error; end # HTTP 508
58
+ class NotExtended < Error; end # HTTP 510
59
+ class NetworkAuthenticationRequired < Error; end # HTTP 507
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "logger"
5
+
6
+ module Yake
7
+ class Logger < ::Logger
8
+ def initialize(logdev = $stdout, *)
9
+ super
10
+ @progname = "-"
11
+ @formatter = LambdaFormatter.new
12
+ end
13
+
14
+ def wrap(event = nil, context = nil, &block)
15
+ @progname = "RequestId: #{ context.aws_request_id }" if context.respond_to?(:aws_request_id)
16
+ info("EVENT #{ event.to_json }")
17
+ yield(event, context).tap { |res| info("RETURN #{ res.to_json }") }
18
+ ensure
19
+ @progname = "-"
20
+ end
21
+ end
22
+
23
+ class LambdaFormatter < ::Logger::Formatter
24
+ Format = "%s %s %s\n"
25
+
26
+ def call(severity, time, progname, msg)
27
+ Format % [ severity, progname, msg2str(msg).strip ]
28
+ end
29
+ end
30
+
31
+ module Loggable
32
+ attr_accessor :logger
33
+ end
34
+
35
+ extend Loggable
36
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yake
4
+ VERSION = "0.1.0"
5
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yake
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Mancevice
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - alexander.mancevice@hey.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - LICENSE.txt
22
+ - README.md
23
+ - lib/yake.rb
24
+ - lib/yake/api.rb
25
+ - lib/yake/dsl.rb
26
+ - lib/yake/errors.rb
27
+ - lib/yake/logger.rb
28
+ - lib/yake/version.rb
29
+ homepage: https://github.com/amancevice/yake
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 2.2.0
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubygems_version: 3.1.6
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: Rake-like DSL for declaring AWS Lambda function handlers
52
+ test_files: []