action_controller-twirp 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba5d8f3dbfc7dc8099def1e420b3a6c79cc2611df1295fad0c9e5f03ed3b1339
4
- data.tar.gz: f2597df16242abd93b1ff1878b664db3f9e66cf63904e2030a47e000c32ba160
3
+ metadata.gz: 17541f1f7bae98a4beb49d20368f0bcbb60b5c0284e6763ead869f13bc333784
4
+ data.tar.gz: 272ddd872c4a7176abcba1b78baf20478e37f93338bc5002ef4537f21e4d8289
5
5
  SHA512:
6
- metadata.gz: ec99d0597b6d950cbf5f65c6900ef64ac855871520bb682d7f916a70aa1dcb6892152a190f487ab639098f4ba44757db9387a420ef22e603326ec08468964d3c
7
- data.tar.gz: 780bf0fab5393d22940621111dfdf811c78007369c64b9a18cfa1f8e50b0adddf3300e07ef6a592605a936d987c481e73835e115109fccaa2a60e3b8ad6902d4
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, add twirp action in config/routes.rb
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,9 +51,9 @@ 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 # :nodoc:
56
+ class UsersController < ApplicationController
52
57
  include ActionController::Twirp
53
58
 
54
59
  USERS = [
@@ -72,33 +77,47 @@ end
72
77
 
73
78
  ### Error handling
74
79
 
75
- twirp-ruby eventually provides us with a rack app, which handles the exception and returns a response when an exception occurs.
76
- Provide settings for exception handling, as the status code or body may be unexpected when an error occurs.
80
+ We must follow protocol when using Twirp. Specifically,
77
81
 
78
- You can install initializer by rails generate command
79
- ```sh
80
- > bin/rails generate action_controller:twirp:install
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.
81
107
 
108
+ First, modify `exception_codes` config.
109
+ ```sh
82
110
  > cat config/initializers/action_controller_twirp.rb
83
111
  # frozen_string_literal: true
84
112
 
85
113
  ActionController::Twirp::Config.setup do |config|
86
- # Handle exceptions
87
- # true: handling by ActionController::Twirp
88
- # false: handling by Twirp::Service (default)
89
- # config.handle_exceptions = false
90
-
91
- # ---
92
- # The following configurations ignore when handle_exceptions is false
93
- # ---
94
-
95
114
  # Mapping your exception classes and Twirp::Error::ERROR_CODES
96
115
  # String => Symbol
97
- # config.exception_codes = {
98
- # 'ActiveRecord::RecordInvalid' => :invalid_argument,
99
- # 'ActiveRecord::RecordNotFound' => :not_found,
100
- # 'My::Exception' => :aborted,
101
- # }
116
+ config.exception_codes = {
117
+ 'ActiveRecord::RecordInvalid' => :invalid_argument,
118
+ 'ActiveRecord::RecordNotFound' => :not_found,
119
+ 'MyApp::Unauthenticated' => :unauthenticated,
120
+ }
102
121
 
103
122
  # Block to make Twirp::Error message when exception_codes exist
104
123
  # config.build_message = ->(exception) {}
@@ -112,6 +131,32 @@ ActionController::Twirp::Config.setup do |config|
112
131
  end
113
132
  ```
114
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
+
115
160
  ## Contributing
116
161
  Contribution directions go here.
117
162
 
@@ -6,15 +6,6 @@ module ActionController
6
6
  module Twirp
7
7
  # Configration for ActionController::Twirp
8
8
  module Config
9
- # Handle exceptions
10
- # true: handling by ActionController::Twirp
11
- # false: handling by Twirp::Service
12
- mattr_accessor :handle_exceptions, default: false
13
-
14
- # ---
15
- # The following configurations ignore when handle_exceptions is false
16
- # ---
17
-
18
9
  # Mapping your exception classes and Twirp::Error::ERROR_CODES
19
10
  mattr_accessor :exception_codes, default: {}
20
11
 
@@ -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
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionController
4
+ module Twirp
5
+ # Convert Twirp::Error to #render option
6
+ class ErrorRenderer
7
+ # @param content [Exception] given as a value of :twirp_error key of render method.
8
+ # @param options [Hash] options of render method.
9
+ def initialize(content, options)
10
+ unless content.is_a?(Exception)
11
+ raise ArgumentError, 'Instance of Exception class can be specified'
12
+ end
13
+
14
+ @content = content
15
+ @options = options.dup
16
+ end
17
+
18
+ def to_render_option
19
+ {
20
+ json: twirp_error.to_h,
21
+ **@options.merge(status: status, content_type: content_type)
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :content, :options
28
+
29
+ def twirp_error
30
+ @twirp_error ||= Error.new(content).generate
31
+ end
32
+
33
+ def status
34
+ ::Twirp::ERROR_CODES_TO_HTTP_STATUS[twirp_error.code]
35
+ end
36
+
37
+ def content_type
38
+ ::Twirp::Encoding::JSON
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,10 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'action_controller/twirp/error_renderer'
3
4
  require 'action_dispatch/twirp/railtie'
4
5
 
5
6
  module ActionController
6
7
  module Twirp
7
8
  class Railtie < ::Rails::Railtie # :nodoc:
9
+ initializer 'action_controller.twirp' do
10
+ ActiveSupport.on_load(:action_controller) do
11
+ ActionController::Renderers.add :twirp_error do |content, options|
12
+ renderer = ErrorRenderer.new(content, options)
13
+ render(**renderer.to_render_option)
14
+ end
15
+ end
16
+ end
8
17
  end
9
18
  end
10
19
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActionController
4
4
  module Twirp
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -3,6 +3,7 @@
3
3
  require 'action_controller/twirp/version'
4
4
  require 'action_controller/twirp/railtie'
5
5
  require 'action_controller/twirp/config'
6
+ require 'action_controller/twirp/error'
6
7
 
7
8
  module ActionController
8
9
  # Module to execute your implemented methods on Twirp with Rails
@@ -11,7 +12,7 @@ module ActionController
11
12
 
12
13
  # Override it to call a twirp class from request path
13
14
  def send_action(*_args)
14
- twirp_service_class.raise_exceptions = Config.handle_exceptions
15
+ twirp_service_class.raise_exceptions = true
15
16
 
16
17
  status, header, body = twirp_action
17
18
 
@@ -39,22 +40,8 @@ module ActionController
39
40
  end
40
41
 
41
42
  def twirp_error_response(exception)
42
- twerr = twirp_error(exception)
43
+ twerr = Error.new(exception).generate
43
44
  twirp_service_class.error_response(twerr)
44
45
  end
45
-
46
- def twirp_error(exception)
47
- if Config.exception_codes.key?(exception.class.to_s)
48
- code = Config.exception_codes[exception.class.to_s]
49
- message = Config.build_message.call(exception)
50
- metadata = Config.build_metadata.call(exception)
51
-
52
- ::Twirp::Error.public_send(code, message, metadata)
53
- else
54
- ::Twirp::Error.internal_with(exception)
55
- end
56
- rescue StandardError => e
57
- ::Twirp::Error.internal_with(e)
58
- end
59
46
  end
60
47
  end
@@ -1,15 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ActionController::Twirp::Config.setup do |config|
4
- # Handle exceptions
5
- # true: handling by ActionTwirpRails
6
- # false: handling by Twirp::Service (default)
7
- # config.handle_exceptions = false
8
-
9
- # ---
10
- # The following configurations ignore when handle_exceptions is false
11
- # ---
12
-
13
4
  # Mapping your exception classes and Twirp::Error::ERROR_CODES
14
5
  # String => Symbol
15
6
  # config.exception_codes = {
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.2.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-09-29 00:00:00.000000000 Z
11
+ date: 2022-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -64,6 +64,8 @@ files:
64
64
  - Rakefile
65
65
  - lib/action_controller/twirp.rb
66
66
  - lib/action_controller/twirp/config.rb
67
+ - lib/action_controller/twirp/error.rb
68
+ - lib/action_controller/twirp/error_renderer.rb
67
69
  - lib/action_controller/twirp/railtie.rb
68
70
  - lib/action_controller/twirp/version.rb
69
71
  - lib/action_dispatch/routing/mapper/twirp.rb