rails_twirp 0.9.0 → 0.12.0
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/.github/dependabot.yml +12 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +10 -1
- data/README.md +58 -3
- data/lib/rails_twirp/base.rb +2 -0
- data/lib/rails_twirp/exception_handling.rb +36 -0
- data/lib/rails_twirp/route_set.rb +2 -0
- data/lib/rails_twirp/testing/integration_test.rb +1 -1
- data/lib/rails_twirp/version.rb +1 -1
- data/lib/rails_twirp.rb +1 -0
- data/rails_twirp.gemspec +1 -1
- data/test/ping_controller_test.rb +51 -1
- metadata +15 -7
- data/test/dummy/config/initializers/assets.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09e97be65f570cd7a2cecaaf0ef83d2ead287fd1fc8c556b833ae45395ee1c2b'
|
4
|
+
data.tar.gz: 1b30d280c8c724747b0b71ef7996ad23fd0a8e189818156602f284677ac9c637
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 858ea0680c3efd03f2f1fdc766f6097b6e0a7b19ff39974412207139cd28a432d4c620a7a4f9243b63b9f70eff1919cb5a92d0c3dc6b5cd73ba99eb1349ac870
|
7
|
+
data.tar.gz: ba6311d0b7cb64755380eff9d1168d24458d5804f36acf21c7fceab2a8b0db305722353d8c5f493bf1692225f973342e3194f825d995df286802da6958631e3a
|
@@ -0,0 +1,12 @@
|
|
1
|
+
version: 2
|
2
|
+
updates:
|
3
|
+
- package-ecosystem: "bundler"
|
4
|
+
directory: "/"
|
5
|
+
schedule:
|
6
|
+
interval: "weekly"
|
7
|
+
|
8
|
+
- package-ecosystem: "github-actions"
|
9
|
+
# Checks for workflow files stored in the default location of `.github/workflows`
|
10
|
+
directory: "/"
|
11
|
+
schedule:
|
12
|
+
interval: "weekly"
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.3
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
### 0.12.0
|
2
|
+
|
3
|
+
* Allow a custom exception handling proc to be assigned using `RailsTwirp.unhandled_exception_handler`
|
4
|
+
|
5
|
+
### 0.11.0
|
6
|
+
|
7
|
+
* Update configuration and tests for Rails 7 compatibility
|
8
|
+
|
9
|
+
### 0.10.0
|
10
|
+
|
11
|
+
* Handle exceptions more like the Rails controllers do it, do not capture all of them as if they were Twirp exceptions
|
data/Gemfile
CHANGED
@@ -4,6 +4,15 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
gem "sqlite3"
|
7
|
-
gem "pbbuilder", "~> 0.
|
7
|
+
gem "pbbuilder", "~> 0.12.0"
|
8
8
|
gem "standard"
|
9
9
|
gem "pry"
|
10
|
+
|
11
|
+
# HACK(bouk): Overwrite Bundler's platform matcher to ignore universal CPU
|
12
|
+
# The protobuf and gRPC 'universal' macOS gems break on M1
|
13
|
+
module Bundler::MatchPlatform
|
14
|
+
def match_platform(p)
|
15
|
+
return false if ::Gem::Platform === platform && platform.cpu == "universal"
|
16
|
+
Bundler::MatchPlatform.platforms_match?(platform, p)
|
17
|
+
end
|
18
|
+
end
|
data/README.md
CHANGED
@@ -1,8 +1,56 @@
|
|
1
1
|
# RailsTwirp
|
2
|
-
|
2
|
+
|
3
|
+
Allows for effortless integration of [Twirp](https://github.com/twitchtv/twirp) endpoints into your Rails application. RailsTwirp permits you to define your Twirp endpoints using the familiar vocabulary of Rails routes, controllers, actions and views.
|
4
|
+
|
5
|
+
Using an extra routes file you map your Twirp RPCs to controllers, and define your views using [pbbuilder.](https://github.com/cheddar-me/pbbuilder) Even though you are now serving Twirp RPC endpoints instead of HTML or JSON, all the skills and tools you have for working your usual Rails infrastructure apply. `TwirpRails` provides a customised controller superclass called `RailsTwirp::Base` which includes all the relevant ActionController components which still make sense when using Protobufs.
|
3
6
|
|
4
7
|
## Usage
|
5
|
-
|
8
|
+
|
9
|
+
Add the gem to your project (see "Installation" below). Then add a file called `config/twirp/routes.rb` to your application, and map your RPCs inside of that file. We will map a `GreetWorld` RPC call as an example:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Rails.application.twirp.routes.draw do
|
13
|
+
service HelloService, module: :api do
|
14
|
+
rpc "GreetWorld", to: "greetings#greet"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
The `module` defines the module which will contain your `TwirpRails` controller. The `HelloService` module is your Protobuf mapping generated using Twirp, it inherits from `Twirp::Service` and could look like this:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class HelloService < Twirp::Service
|
23
|
+
package 'api'
|
24
|
+
service 'App'
|
25
|
+
rpc :GreetWorld, GreetWorldRequest, GreetWorldResponse, :ruby_method => :greet_world
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
The `GreetWorldRequest` in this case is the autogenerated protobuf request class, the `GreetWorldResponse` is the response class.
|
30
|
+
|
31
|
+
Then you define your controller, which looks like a standard Rails controller - just with a different base class:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class API::GreetingsController < RailsTwirp::Base
|
35
|
+
def greet
|
36
|
+
render pb: API::GreetResponse.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
Note that the controller is routed using the same naming conventions as Rails. Inside of a `TwirpRails` controller you can do most of the things you are used to from Rails controllers, such as renders and implicit renders - which have to be `pbbuilder` views. If our `GreetWorldResponse` has a `greeting` field, it can be filled inside the `pbbuilder` view like so:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
pb.greeting "Hello stranger"
|
45
|
+
```
|
46
|
+
|
47
|
+
Inside the controllers you have access to the call parameters of your RPC call using `request`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
def greet
|
51
|
+
request.name #=> # Returns the value of the "name" field from the request Protobuf
|
52
|
+
end
|
53
|
+
```
|
6
54
|
|
7
55
|
## Installation
|
8
56
|
Add this line to your application's Gemfile:
|
@@ -37,7 +85,14 @@ end
|
|
37
85
|
```
|
38
86
|
|
39
87
|
## Contributing
|
40
|
-
|
88
|
+
|
89
|
+
* Check out the latest `main` to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
90
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
91
|
+
* Fork the project.
|
92
|
+
* Start a feature/bugfix branch.
|
93
|
+
* Commit and push until you are happy with your contribution.
|
94
|
+
* Make sure to add tests for your change. This is important so we don't break it in a future version unintentionally.
|
95
|
+
* Please try not to mess with the Rakefile, version, or history.
|
41
96
|
|
42
97
|
## License
|
43
98
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/rails_twirp/base.rb
CHANGED
@@ -12,6 +12,7 @@ require "rails_twirp/rescue"
|
|
12
12
|
require "rails_twirp/url_for"
|
13
13
|
require "rails_twirp/implicit_render"
|
14
14
|
require "rails_twirp/instrumentation"
|
15
|
+
require "rails_twirp/exception_handling"
|
15
16
|
|
16
17
|
module RailsTwirp
|
17
18
|
class Base < AbstractController::Base
|
@@ -36,6 +37,7 @@ module RailsTwirp
|
|
36
37
|
include AbstractController::Callbacks
|
37
38
|
include Rescue
|
38
39
|
include Instrumentation
|
40
|
+
include ExceptionHandling
|
39
41
|
|
40
42
|
attr_internal :request, :env, :response_class, :rpc_name
|
41
43
|
def initialize
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "twirp/error"
|
2
|
+
|
3
|
+
module RailsTwirp
|
4
|
+
module ExceptionHandling
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
include AbstractController::Logger
|
8
|
+
|
9
|
+
def process_action(*)
|
10
|
+
super
|
11
|
+
rescue Exception => e
|
12
|
+
# Only the exceptions which are not captured by ActionController-like "rescue_from" end up here.
|
13
|
+
# The idea is that any exception which is rescued by the controller is treated as part of the business
|
14
|
+
# logic, and thus taking action on it is the responsibility of the controller which uses "rescue_from".
|
15
|
+
# If an exception ends up here it means it wasn't captured by the handlers defined in the controller.
|
16
|
+
|
17
|
+
# We adopt the same error handling logic as Rails' standard middlewares:
|
18
|
+
# 1. When we 'show exceptions' we make the exception bubble up—this is useful for testing
|
19
|
+
# If the exception gets raised here error reporting will happen in the middleware of the APM package
|
20
|
+
# higher in the call stack.
|
21
|
+
raise e unless http_request.show_exceptions?
|
22
|
+
|
23
|
+
# 2. We report the error to the error tracking service, this needs to be configured.
|
24
|
+
RailsTwirp.unhandled_exception_handler&.call(e)
|
25
|
+
|
26
|
+
# 3. When we want to show detailed exceptions we include the exception message in the error
|
27
|
+
if http_request.get_header("action_dispatch.show_detailed_exceptions")
|
28
|
+
self.response_body = Twirp::Error.internal_with(e)
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
# 4. Otherwise we just return a vague internal error message
|
33
|
+
self.response_body = Twirp::Error.internal("Internal error")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/rails_twirp/version.rb
CHANGED
data/lib/rails_twirp.rb
CHANGED
data/rails_twirp.gemspec
CHANGED
@@ -13,6 +13,6 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.test_files = `git ls-files -- test/*`.split("\n")
|
14
14
|
|
15
15
|
spec.add_dependency "rails", ">= 6.1.3"
|
16
|
-
spec.add_dependency "twirp", "
|
16
|
+
spec.add_dependency "twirp", ">= 1.7.2", "~> 1.9.0"
|
17
17
|
spec.required_ruby_version = ">= 3"
|
18
18
|
end
|
@@ -46,12 +46,56 @@ class PingControllerTest < RailsTwirp::IntegrationTest
|
|
46
46
|
assert_equal :not_found, response.code
|
47
47
|
end
|
48
48
|
|
49
|
-
test "uncaught
|
49
|
+
test "uncaught errors should bubble up to the test" do
|
50
|
+
req = RPC::DummyAPI::PingRequest.new
|
51
|
+
assert_raises StandardError, "Uncaught" do
|
52
|
+
rpc RPC::DummyAPI::DummyService, "UncaughtError", req
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
test "uncaught errors should return an internal error with details if show_exceptions is true" do
|
57
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = true
|
58
|
+
|
59
|
+
req = RPC::DummyAPI::PingRequest.new
|
60
|
+
rpc RPC::DummyAPI::DummyService, "UncaughtError", req
|
61
|
+
assert_instance_of Twirp::Error, response
|
62
|
+
assert_equal :internal, response.code
|
63
|
+
assert_equal "Uncaught", response.msg
|
64
|
+
assert_equal "StandardError", response.meta["cause"]
|
65
|
+
ensure
|
66
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = false
|
67
|
+
end
|
68
|
+
|
69
|
+
test "uncaught errors should be fanned out to the exception handler proc if one is defined" do
|
70
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = true
|
71
|
+
|
72
|
+
captured_exception = nil
|
73
|
+
RailsTwirp.unhandled_exception_handler = ->(e) { captured_exception = e }
|
74
|
+
|
50
75
|
req = RPC::DummyAPI::PingRequest.new
|
51
76
|
rpc RPC::DummyAPI::DummyService, "UncaughtError", req
|
52
77
|
assert_instance_of Twirp::Error, response
|
78
|
+
assert_equal :internal, response.code
|
53
79
|
assert_equal "Uncaught", response.msg
|
80
|
+
assert_equal "StandardError", response.meta["cause"]
|
81
|
+
assert_kind_of StandardError, captured_exception
|
82
|
+
ensure
|
83
|
+
RailsTwirp.unhandled_exception_handler = nil
|
84
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = false
|
85
|
+
end
|
86
|
+
|
87
|
+
test "uncaught errors should return an internal error without if show_exceptions is true and show_detailed_exceptions is false" do
|
88
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = true
|
89
|
+
Rails.application.env_config["action_dispatch.show_detailed_exceptions"] = false
|
90
|
+
|
91
|
+
req = RPC::DummyAPI::PingRequest.new
|
92
|
+
rpc RPC::DummyAPI::DummyService, "UncaughtError", req
|
93
|
+
assert_instance_of Twirp::Error, response
|
54
94
|
assert_equal :internal, response.code
|
95
|
+
assert_equal "Internal error", response.msg
|
96
|
+
ensure
|
97
|
+
Rails.application.env_config["action_dispatch.show_detailed_exceptions"] = true
|
98
|
+
Rails.application.env_config["action_dispatch.show_exceptions"] = false
|
55
99
|
end
|
56
100
|
|
57
101
|
test "before error" do
|
@@ -61,4 +105,10 @@ class PingControllerTest < RailsTwirp::IntegrationTest
|
|
61
105
|
assert_equal "yOuR ReQuEsT Is mAlFoRmEd", response.msg
|
62
106
|
assert_equal :malformed, response.code
|
63
107
|
end
|
108
|
+
|
109
|
+
test "controller is set to the controller that handled the request" do
|
110
|
+
req = RPC::DummyAPI::PingRequest.new(name: "Bouke")
|
111
|
+
rpc RPC::DummyAPI::DummyService, "Ping", req
|
112
|
+
assert_instance_of PingsController, controller
|
113
|
+
end
|
64
114
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bouke van der Bijl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -28,16 +28,22 @@ dependencies:
|
|
28
28
|
name: twirp
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.7.2
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 1.9.0
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
39
42
|
- !ruby/object:Gem::Version
|
40
43
|
version: 1.7.2
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.9.0
|
41
47
|
description:
|
42
48
|
email:
|
43
49
|
- bouke@cheddar.me
|
@@ -45,9 +51,12 @@ executables: []
|
|
45
51
|
extensions: []
|
46
52
|
extra_rdoc_files: []
|
47
53
|
files:
|
54
|
+
- ".github/dependabot.yml"
|
48
55
|
- ".github/workflows/test.yml"
|
49
56
|
- ".gitignore"
|
57
|
+
- ".ruby-version"
|
50
58
|
- ".standard.yml"
|
59
|
+
- CHANGELOG.md
|
51
60
|
- Gemfile
|
52
61
|
- MIT-LICENSE
|
53
62
|
- README.md
|
@@ -59,6 +68,7 @@ files:
|
|
59
68
|
- lib/rails_twirp/base.rb
|
60
69
|
- lib/rails_twirp/engine.rb
|
61
70
|
- lib/rails_twirp/errors.rb
|
71
|
+
- lib/rails_twirp/exception_handling.rb
|
62
72
|
- lib/rails_twirp/implicit_render.rb
|
63
73
|
- lib/rails_twirp/instrumentation.rb
|
64
74
|
- lib/rails_twirp/log_subscriber.rb
|
@@ -108,7 +118,6 @@ files:
|
|
108
118
|
- test/dummy/config/environments/production.rb
|
109
119
|
- test/dummy/config/environments/test.rb
|
110
120
|
- test/dummy/config/initializers/application_controller_renderer.rb
|
111
|
-
- test/dummy/config/initializers/assets.rb
|
112
121
|
- test/dummy/config/initializers/backtrace_silencers.rb
|
113
122
|
- test/dummy/config/initializers/content_security_policy.rb
|
114
123
|
- test/dummy/config/initializers/cookies_serializer.rb
|
@@ -157,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
166
|
- !ruby/object:Gem::Version
|
158
167
|
version: '0'
|
159
168
|
requirements: []
|
160
|
-
rubygems_version: 3.2.
|
169
|
+
rubygems_version: 3.2.32
|
161
170
|
signing_key:
|
162
171
|
specification_version: 4
|
163
172
|
summary: Integrate Twirp into Rails
|
@@ -200,7 +209,6 @@ test_files:
|
|
200
209
|
- test/dummy/config/environments/production.rb
|
201
210
|
- test/dummy/config/environments/test.rb
|
202
211
|
- test/dummy/config/initializers/application_controller_renderer.rb
|
203
|
-
- test/dummy/config/initializers/assets.rb
|
204
212
|
- test/dummy/config/initializers/backtrace_silencers.rb
|
205
213
|
- test/dummy/config/initializers/content_security_policy.rb
|
206
214
|
- test/dummy/config/initializers/cookies_serializer.rb
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# Be sure to restart your server when you modify this file.
|
2
|
-
|
3
|
-
# Version of your assets, change this if you want to expire all your assets.
|
4
|
-
Rails.application.config.assets.version = "1.0"
|
5
|
-
|
6
|
-
# Add additional assets to the asset load path.
|
7
|
-
# Rails.application.config.assets.paths << Emoji.images_path
|
8
|
-
|
9
|
-
# Precompile additional assets.
|
10
|
-
# application.js, application.css, and all non-JS/CSS in the app/assets
|
11
|
-
# folder are already added.
|
12
|
-
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
|