action_controller-twirp 0.1.1 → 0.3.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/README.md +91 -11
- data/lib/action_controller/twirp/config.rb +26 -0
- data/lib/action_controller/twirp/error.rb +41 -0
- data/lib/action_controller/twirp/{twirp_error_renderer.rb → error_renderer.rb} +7 -13
- data/lib/action_controller/twirp/railtie.rb +2 -2
- data/lib/action_controller/twirp/version.rb +1 -1
- data/lib/action_controller/twirp.rb +19 -1
- data/lib/generators/action_controller/twirp/install_generator.rb +16 -0
- data/lib/generators/templates/action_controller_twirp.rb +21 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17541f1f7bae98a4beb49d20368f0bcbb60b5c0284e6763ead869f13bc333784
|
4
|
+
data.tar.gz: 272ddd872c4a7176abcba1b78baf20478e37f93338bc5002ef4537f21e4d8289
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b8ea6ac39900c875734119e47552d0425bb18e49e8ca878d30901e38ff5293cf2f59729220bef4184670dc80086d951cf055cfbdfdaa0d5f42d562139acde74
|
7
|
+
data.tar.gz: 3314d20c05db3d93fd07d75b840baec2b60f3d5a581e3e11e76df1da87d15e520a015cd4dbd426a6c3b5e3eba5f89da5c5ddffbd6a6ce4e6787c3cde740822ea
|
data/README.md
CHANGED
@@ -29,7 +29,12 @@ It provides routing method `twirp` that maps twirp service class and Rails contr
|
|
29
29
|
|
30
30
|
First, you should generate twirp service class. see: [example in twirp-ruby](https://github.com/twitchtv/twirp-ruby/wiki#usage-example)
|
31
31
|
|
32
|
-
Next,
|
32
|
+
Next, install initializer.
|
33
|
+
```sh
|
34
|
+
bin/rails g action_controller:twirp:install
|
35
|
+
```
|
36
|
+
|
37
|
+
Next, add twirp action in config/routes.rb.
|
33
38
|
```ruby
|
34
39
|
Rails.application.routes.draw do
|
35
40
|
scope :twirp do
|
@@ -38,7 +43,7 @@ Rails.application.routes.draw do
|
|
38
43
|
end
|
39
44
|
```
|
40
45
|
|
41
|
-
Result of `bin/rails routes
|
46
|
+
Result of `bin/rails routes`.
|
42
47
|
```sh
|
43
48
|
> bin/rails routes
|
44
49
|
Prefix Verb URI Pattern Controller#Action
|
@@ -46,18 +51,11 @@ Result of `bin/rails routes`
|
|
46
51
|
POST /twirp/example.v1.UserApi/GetUser(.:format) users#get_user
|
47
52
|
```
|
48
53
|
|
49
|
-
Finally, implement rpc method your controller
|
54
|
+
Finally, implement rpc method your controller.
|
50
55
|
```ruby
|
51
|
-
class UsersController < ApplicationController
|
56
|
+
class UsersController < ApplicationController
|
52
57
|
include ActionController::Twirp
|
53
58
|
|
54
|
-
rescue_from ActiveRecord::RecordNotFound do |e|
|
55
|
-
twerr = Twirp::Error.not_found('The message',
|
56
|
-
reason: e.class.name.demodulize,
|
57
|
-
id: params[:id].to_s)
|
58
|
-
render twirp_error: twerr
|
59
|
-
end
|
60
|
-
|
61
59
|
USERS = [
|
62
60
|
{ id: 1, name: 'Anna' },
|
63
61
|
{ id: 2, name: 'Reina' }
|
@@ -77,6 +75,88 @@ class UsersController < ApplicationController # :nodoc:
|
|
77
75
|
end
|
78
76
|
```
|
79
77
|
|
78
|
+
### Error handling
|
79
|
+
|
80
|
+
We must follow protocol when using Twirp. Specifically,
|
81
|
+
|
82
|
+
- It accepts request parameters specified in proto file, and must return response body.
|
83
|
+
- If an exception occurs, a response body of Twirp::Error's JSON must be returned.
|
84
|
+
|
85
|
+
This section describes when an exception occurs.
|
86
|
+
|
87
|
+
The following blocks are in the order of method calls when requested to the Rails app. (It's quite simplified)
|
88
|
+
```
|
89
|
+
:
|
90
|
+
ActionController::Metal#dispatch
|
91
|
+
AbstractController::Base#process
|
92
|
+
:
|
93
|
+
ActionController::Rescue#process_action
|
94
|
+
ActionController::Callbacks#process_action
|
95
|
+
:
|
96
|
+
(Some module's #process_action is called)
|
97
|
+
:
|
98
|
+
AbstractController::Base#process_action
|
99
|
+
AbstractController::Base#send_action
|
100
|
+
```
|
101
|
+
|
102
|
+
The gem is only override `#send_action`. It follows twirp protocal in this one.
|
103
|
+
|
104
|
+
For example, if an exception occurs in the callback, Rails app will return a response, so we must be implemented so that the twirp protocol can be followed.
|
105
|
+
|
106
|
+
This is easy to do, see below.
|
107
|
+
|
108
|
+
First, modify `exception_codes` config.
|
109
|
+
```sh
|
110
|
+
> cat config/initializers/action_controller_twirp.rb
|
111
|
+
# frozen_string_literal: true
|
112
|
+
|
113
|
+
ActionController::Twirp::Config.setup do |config|
|
114
|
+
# Mapping your exception classes and Twirp::Error::ERROR_CODES
|
115
|
+
# String => Symbol
|
116
|
+
config.exception_codes = {
|
117
|
+
'ActiveRecord::RecordInvalid' => :invalid_argument,
|
118
|
+
'ActiveRecord::RecordNotFound' => :not_found,
|
119
|
+
'MyApp::Unauthenticated' => :unauthenticated,
|
120
|
+
}
|
121
|
+
|
122
|
+
# Block to make Twirp::Error message when exception_codes exist
|
123
|
+
# config.build_message = ->(exception) {}
|
124
|
+
|
125
|
+
# Block to make Twirp::Error metadata. when exception_codes exist
|
126
|
+
# It MUST return Hash value
|
127
|
+
# config.build_metadata = ->(exception) {}
|
128
|
+
|
129
|
+
# Block to run additional process. e.g. logging
|
130
|
+
# config.on_exceptions = ->(exception) {}
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Next, write a `rescue_from` block as usual and use the `:twirp_error` key on `#render`
|
135
|
+
```ruby
|
136
|
+
class UsersController < ApplicationController
|
137
|
+
include ActionController::Twirp
|
138
|
+
|
139
|
+
before_action :authenticate!
|
140
|
+
|
141
|
+
rescue_from MyApp::Unauthenticated do |e|
|
142
|
+
render twirp_error: e
|
143
|
+
end
|
144
|
+
|
145
|
+
:
|
146
|
+
:
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def authenticate!
|
151
|
+
return if your_logic
|
152
|
+
|
153
|
+
raise MyApp::Unauthenticated
|
154
|
+
end
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
See [dummy app controller](test/dummy/app/controllers/users_controller.rb) and [integration test](test/integration/users_test.rb) for details.
|
159
|
+
|
80
160
|
## Contributing
|
81
161
|
Contribution directions go here.
|
82
162
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
module Twirp
|
7
|
+
# Configration for ActionController::Twirp
|
8
|
+
module Config
|
9
|
+
# Mapping your exception classes and Twirp::Error::ERROR_CODES
|
10
|
+
mattr_accessor :exception_codes, default: {}
|
11
|
+
|
12
|
+
# Block to make Twirp::Error message
|
13
|
+
mattr_accessor :build_message, default: ->(exception) {}
|
14
|
+
|
15
|
+
# Block to make Twirp::Error metadata
|
16
|
+
mattr_accessor :build_metadata, default: ->(exception) {}
|
17
|
+
|
18
|
+
# Block to run additional process. e.g. logging
|
19
|
+
mattr_accessor :on_exceptions, default: ->(exception) {}
|
20
|
+
|
21
|
+
def self.setup
|
22
|
+
yield self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_controller/twirp/config'
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
module Twirp
|
7
|
+
# Class to generate Twirp::Error from an exception class and Config
|
8
|
+
class Error
|
9
|
+
# @param content [Exception] given as a value of :twirp_error key of render method.
|
10
|
+
def initialize(exception)
|
11
|
+
unless exception.is_a?(Exception)
|
12
|
+
raise ArgumentError, 'Instance of Exception class can be specified'
|
13
|
+
end
|
14
|
+
|
15
|
+
@exception = exception
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate
|
19
|
+
if Config.exception_codes.key?(exception_name)
|
20
|
+
code = Config.exception_codes[exception_name]
|
21
|
+
message = Config.build_message.call(exception)
|
22
|
+
metadata = Config.build_metadata.call(exception)
|
23
|
+
|
24
|
+
::Twirp::Error.public_send(code, message, metadata)
|
25
|
+
else
|
26
|
+
::Twirp::Error.internal_with(exception)
|
27
|
+
end
|
28
|
+
rescue StandardError => e
|
29
|
+
::Twirp::Error.internal_with(e)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :exception
|
35
|
+
|
36
|
+
def exception_name
|
37
|
+
exception.class.to_s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -3,12 +3,12 @@
|
|
3
3
|
module ActionController
|
4
4
|
module Twirp
|
5
5
|
# Convert Twirp::Error to #render option
|
6
|
-
class
|
7
|
-
# @param content [
|
6
|
+
class ErrorRenderer
|
7
|
+
# @param content [Exception] given as a value of :twirp_error key of render method.
|
8
8
|
# @param options [Hash] options of render method.
|
9
9
|
def initialize(content, options)
|
10
|
-
unless content.is_a?(
|
11
|
-
raise ArgumentError, '
|
10
|
+
unless content.is_a?(Exception)
|
11
|
+
raise ArgumentError, 'Instance of Exception class can be specified'
|
12
12
|
end
|
13
13
|
|
14
14
|
@content = content
|
@@ -24,16 +24,10 @@ module ActionController
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
|
28
|
-
return @twirp_error if defined? @twirp_error
|
29
|
-
|
30
|
-
code = @content.code
|
27
|
+
attr_reader :content, :options
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
else
|
35
|
-
::Twirp::Error.internal("Invalid code: #{code}", invalid_code: code.to_s)
|
36
|
-
end
|
29
|
+
def twirp_error
|
30
|
+
@twirp_error ||= Error.new(content).generate
|
37
31
|
end
|
38
32
|
|
39
33
|
def status
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'action_controller/twirp/
|
3
|
+
require 'action_controller/twirp/error_renderer'
|
4
4
|
require 'action_dispatch/twirp/railtie'
|
5
5
|
|
6
6
|
module ActionController
|
@@ -9,7 +9,7 @@ module ActionController
|
|
9
9
|
initializer 'action_controller.twirp' do
|
10
10
|
ActiveSupport.on_load(:action_controller) do
|
11
11
|
ActionController::Renderers.add :twirp_error do |content, options|
|
12
|
-
renderer =
|
12
|
+
renderer = ErrorRenderer.new(content, options)
|
13
13
|
render(**renderer.to_render_option)
|
14
14
|
end
|
15
15
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'action_controller/twirp/version'
|
4
4
|
require 'action_controller/twirp/railtie'
|
5
|
+
require 'action_controller/twirp/config'
|
6
|
+
require 'action_controller/twirp/error'
|
5
7
|
|
6
8
|
module ActionController
|
7
9
|
# Module to execute your implemented methods on Twirp with Rails
|
@@ -12,7 +14,7 @@ module ActionController
|
|
12
14
|
def send_action(*_args)
|
13
15
|
twirp_service_class.raise_exceptions = true
|
14
16
|
|
15
|
-
status, header, body =
|
17
|
+
status, header, body = twirp_action
|
16
18
|
|
17
19
|
response.status = status
|
18
20
|
response.header.merge!(header)
|
@@ -25,5 +27,21 @@ module ActionController
|
|
25
27
|
class_name = request.path.split('/')[-2].underscore.gsub('.', '/').classify
|
26
28
|
@twirp_service_class = "#{class_name}Service".constantize
|
27
29
|
end
|
30
|
+
|
31
|
+
def twirp_action
|
32
|
+
twirp_service_class.new(self)&.call(request.env)
|
33
|
+
rescue StandardError => e
|
34
|
+
begin
|
35
|
+
Config.on_exceptions.call(e)
|
36
|
+
rescue StandardError => hook_e
|
37
|
+
e = hook_e
|
38
|
+
end
|
39
|
+
twirp_error_response(e)
|
40
|
+
end
|
41
|
+
|
42
|
+
def twirp_error_response(exception)
|
43
|
+
twerr = Error.new(exception).generate
|
44
|
+
twirp_service_class.error_response(twerr)
|
45
|
+
end
|
28
46
|
end
|
29
47
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionController
|
4
|
+
module Twirp
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < Rails::Generators::Base # :nodoc:
|
7
|
+
source_root File.expand_path('../../templates', __dir__)
|
8
|
+
|
9
|
+
desc 'Creates a ActionController::Twirp initializer'
|
10
|
+
def copy_initializer
|
11
|
+
template 'action_controller_twirp.rb', 'config/initializers/action_controller_twirp.rb'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ActionController::Twirp::Config.setup do |config|
|
4
|
+
# Mapping your exception classes and Twirp::Error::ERROR_CODES
|
5
|
+
# String => Symbol
|
6
|
+
# config.exception_codes = {
|
7
|
+
# 'ActiveRecord::RecordInvalid' => :invalid_argument,
|
8
|
+
# 'ActiveRecord::RecordNotFound' => :not_found,
|
9
|
+
# 'My::Exception' => :aborted,
|
10
|
+
# }
|
11
|
+
|
12
|
+
# Block to make Twirp::Error message when exception_codes exist
|
13
|
+
# config.build_message = ->(exception) {}
|
14
|
+
|
15
|
+
# Block to make Twirp::Error metadata. when exception_codes exist
|
16
|
+
# It MUST return Hash value
|
17
|
+
# config.build_metadata = ->(exception) {}
|
18
|
+
|
19
|
+
# Block to run additional process. e.g. logging
|
20
|
+
# config.on_exceptions = ->(exception) {}
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_controller-twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kosuke Arisawa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -63,11 +63,15 @@ files:
|
|
63
63
|
- README.md
|
64
64
|
- Rakefile
|
65
65
|
- lib/action_controller/twirp.rb
|
66
|
+
- lib/action_controller/twirp/config.rb
|
67
|
+
- lib/action_controller/twirp/error.rb
|
68
|
+
- lib/action_controller/twirp/error_renderer.rb
|
66
69
|
- lib/action_controller/twirp/railtie.rb
|
67
|
-
- lib/action_controller/twirp/twirp_error_renderer.rb
|
68
70
|
- lib/action_controller/twirp/version.rb
|
69
71
|
- lib/action_dispatch/routing/mapper/twirp.rb
|
70
72
|
- lib/action_dispatch/twirp/railtie.rb
|
73
|
+
- lib/generators/action_controller/twirp/install_generator.rb
|
74
|
+
- lib/generators/templates/action_controller_twirp.rb
|
71
75
|
- lib/tasks/action_controller/twirp_tasks.rake
|
72
76
|
homepage: https://github.com/arisawa/action_controller-twirp
|
73
77
|
licenses:
|