action_controller-twirp 0.1.1 → 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: 22fa76662fce706d3efefc38981bfab8ed7cd01611e1d4de2bc8c7a8ad919e38
4
- data.tar.gz: 4479f03e25ef53f640feaaa9fb9e8c2e10e5202606f522da1949abbed88dfa5e
3
+ metadata.gz: 17541f1f7bae98a4beb49d20368f0bcbb60b5c0284e6763ead869f13bc333784
4
+ data.tar.gz: 272ddd872c4a7176abcba1b78baf20478e37f93338bc5002ef4537f21e4d8289
5
5
  SHA512:
6
- metadata.gz: b576978ee686292ac1150e876283101981094ce919e2d0c00bc202d7e5177badad3a9f29655aa3f7a6eed158e1ff02bbce89c8fb4998300cf4ba419f8a51729e
7
- data.tar.gz: b28a534f96ace98a8f2daad7bbe2e90f72cbce3533ad5133fadfa49aa0d4ab07006b649e79f0252e43be331868091255af951ae6934103a8659244684602dc4f
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,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 # :nodoc:
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 TwirpErrorRenderer
7
- # @param content [Twirp::Error] given as a value of :twirp_error key of render method.
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?(::Twirp::Error)
11
- raise ArgumentError, 'Only Twirp::Error instance can be specified'
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
- def twirp_error
28
- return @twirp_error if defined? @twirp_error
29
-
30
- code = @content.code
27
+ attr_reader :content, :options
31
28
 
32
- @twirp_error = if ::Twirp::Error.valid_code?(code)
33
- @content
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/twirp_error_renderer'
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 = TwirpErrorRenderer.new(content, options)
12
+ renderer = ErrorRenderer.new(content, options)
13
13
  render(**renderer.to_render_option)
14
14
  end
15
15
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActionController
4
4
  module Twirp
5
- VERSION = '0.1.1'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  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 = twirp_service_class.new(self)&.call(request.env)
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.1.1
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-21 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
@@ -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: