action_controller-twirp 0.2.0 → 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 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