rails_twirp 0.5.0 → 0.7.2
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/Gemfile +2 -1
- data/lib/rails_twirp/base.rb +4 -3
- data/lib/rails_twirp/implicit_render.rb +11 -0
- data/lib/rails_twirp/instrumentation.rb +32 -0
- data/lib/rails_twirp/log_subscriber.rb +64 -0
- data/lib/rails_twirp/route_set.rb +3 -3
- data/lib/rails_twirp/testing/integration_test.rb +72 -16
- data/lib/rails_twirp/version.rb +1 -1
- data/lib/rails_twirp.rb +1 -0
- data/rails_twirp.gemspec +1 -1
- data/test/dummy/config/twirp/routes.rb +1 -1
- data/test/ping_controller_test.rb +10 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '054689e120dd41594071de17cd6e8967886699ec4f75bd08aceae475a2e12e03'
|
4
|
+
data.tar.gz: 56b1471b8103f249c9c2d53fe78b2aa31a978c67f3a1b658e15923534a4a3b63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3630e5152913b1bca04ca94ab0d15bdce29f6853a4cafb226c4e5f678042e8a1bbcd6e931b20432d816556ef5f087512e4ad09327e5bbfa3af72e97bcce9a5f
|
7
|
+
data.tar.gz: bace8057362c6085ba5edea873087b5b4459f6c0dc48e5abeea589380abcd64386dfedfcd41cc2a841f69721b3d852a8516aaf06522df843d8b09e902879842e
|
data/Gemfile
CHANGED
data/lib/rails_twirp/base.rb
CHANGED
@@ -10,6 +10,8 @@ require "abstract_controller/callbacks"
|
|
10
10
|
require "action_controller/metal/helpers"
|
11
11
|
require "rails_twirp/rescue"
|
12
12
|
require "rails_twirp/url_for"
|
13
|
+
require "rails_twirp/implicit_render"
|
14
|
+
require "rails_twirp/instrumentation"
|
13
15
|
|
14
16
|
module RailsTwirp
|
15
17
|
class Base < AbstractController::Base
|
@@ -24,15 +26,16 @@ module RailsTwirp
|
|
24
26
|
include UrlFor
|
25
27
|
include AbstractController::AssetPaths
|
26
28
|
include AbstractController::Caching
|
27
|
-
include AbstractController::Logger
|
28
29
|
|
29
30
|
include ActionView::Rendering
|
30
31
|
include RenderPb
|
31
32
|
include Errors
|
33
|
+
include ImplicitRender
|
32
34
|
|
33
35
|
# These need to be last so errors can be handled as early as possible.
|
34
36
|
include AbstractController::Callbacks
|
35
37
|
include Rescue
|
38
|
+
include Instrumentation
|
36
39
|
|
37
40
|
attr_internal :request, :env, :response_class
|
38
41
|
def initialize
|
@@ -55,8 +58,6 @@ module RailsTwirp
|
|
55
58
|
|
56
59
|
process(action)
|
57
60
|
|
58
|
-
# Implicit render
|
59
|
-
self.response_body = render unless response_body
|
60
61
|
response_body
|
61
62
|
end
|
62
63
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RailsTwirp
|
2
|
+
module Instrumentation
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
include AbstractController::Logger
|
6
|
+
|
7
|
+
def process_action(*)
|
8
|
+
raw_payload = {
|
9
|
+
controller: self.class.name,
|
10
|
+
action: action_name,
|
11
|
+
request: request,
|
12
|
+
http_request: http_request,
|
13
|
+
headers: http_request.headers,
|
14
|
+
path: http_request.fullpath
|
15
|
+
}
|
16
|
+
|
17
|
+
ActiveSupport::Notifications.instrument("start_processing.rails_twirp", raw_payload)
|
18
|
+
|
19
|
+
ActiveSupport::Notifications.instrument("process_action.rails_twirp", raw_payload) do |payload|
|
20
|
+
result = super
|
21
|
+
if response_body.is_a?(Twirp::Error)
|
22
|
+
payload[:code] = response_body.code
|
23
|
+
payload[:msg] = response_body.msg
|
24
|
+
else
|
25
|
+
payload[:code] = :success
|
26
|
+
end
|
27
|
+
payload[:response] = response_body
|
28
|
+
result
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "active_support/log_subscriber"
|
2
|
+
|
3
|
+
module RailsTwirp
|
4
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
5
|
+
def start_processing(event)
|
6
|
+
return unless logger.info?
|
7
|
+
|
8
|
+
payload = event.payload
|
9
|
+
|
10
|
+
info "Processing by #{payload[:controller]}##{payload[:action]}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_action(event)
|
14
|
+
payload = event.payload
|
15
|
+
exception_raised(payload[:http_request], payload[:exception_object]) if payload[:exception_object]
|
16
|
+
|
17
|
+
info do
|
18
|
+
code = payload.fetch(:code, :internal)
|
19
|
+
|
20
|
+
message = +"Completed #{code} in #{event.duration.round}ms (Allocations: #{event.allocations})"
|
21
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
22
|
+
|
23
|
+
message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def exception_raised(request, exception)
|
30
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
31
|
+
wrapper = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception)
|
32
|
+
|
33
|
+
log_error(wrapper)
|
34
|
+
end
|
35
|
+
|
36
|
+
def log_error(wrapper)
|
37
|
+
exception = wrapper.exception
|
38
|
+
trace = wrapper.exception_trace
|
39
|
+
|
40
|
+
message = []
|
41
|
+
message << " "
|
42
|
+
message << "#{exception.class} (#{exception.message}):"
|
43
|
+
message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
|
44
|
+
message << " "
|
45
|
+
message.concat(trace)
|
46
|
+
|
47
|
+
log_array(message)
|
48
|
+
end
|
49
|
+
|
50
|
+
def log_array(array)
|
51
|
+
lines = Array(array)
|
52
|
+
|
53
|
+
return if lines.empty?
|
54
|
+
|
55
|
+
if logger.formatter&.respond_to?(:tags_text)
|
56
|
+
fatal { lines.join("\n#{logger.formatter.tags_text}") }
|
57
|
+
else
|
58
|
+
fatal { lines.join("\n") }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
RailsTwirp::LogSubscriber.attach_to :rails_twirp
|
@@ -50,13 +50,13 @@ module RailsTwirp
|
|
50
50
|
method_name = rpc_info[:ruby_method]
|
51
51
|
|
52
52
|
# Stolen from Rails in ActionDispatch::Request#controller_class_for
|
53
|
-
controller_name = mapping.controller.underscore
|
54
|
-
const_name = controller_name.camelize << "Controller"
|
55
53
|
action_name = mapping.action
|
56
54
|
response_class = rpc_info[:output_class]
|
57
55
|
|
58
56
|
handler.define_method(method_name) do |req, env|
|
59
|
-
|
57
|
+
controller_name = mapping.controller.underscore
|
58
|
+
const_name = controller_name.camelize << "Controller"
|
59
|
+
controller_class = const_name.constantize
|
60
60
|
controller_class.dispatch(action_name, req, response_class, env)
|
61
61
|
end
|
62
62
|
end
|
@@ -1,6 +1,13 @@
|
|
1
|
+
require "twirp/encoding"
|
2
|
+
|
1
3
|
module RailsTwirp
|
2
4
|
class IntegrationTest < ActiveSupport::TestCase
|
5
|
+
DEFAULT_HOST = "www.example.com"
|
6
|
+
Response = Struct.new(:status, :body, :headers)
|
7
|
+
|
3
8
|
attr_reader :response, :request, :controller
|
9
|
+
attr_writer :mount_path
|
10
|
+
alias_method :mount_path!, :mount_path=
|
4
11
|
|
5
12
|
def initialize(name)
|
6
13
|
super
|
@@ -8,9 +15,27 @@ module RailsTwirp
|
|
8
15
|
@before_rpc = []
|
9
16
|
end
|
10
17
|
|
18
|
+
def host
|
19
|
+
@host || DEFAULT_HOST
|
20
|
+
end
|
21
|
+
attr_writer :host
|
22
|
+
alias_method :host!, :host=
|
23
|
+
|
24
|
+
def https?
|
25
|
+
@https
|
26
|
+
end
|
27
|
+
|
28
|
+
def https!(value = true)
|
29
|
+
@https = value
|
30
|
+
end
|
31
|
+
|
11
32
|
def reset!
|
12
33
|
@request = nil
|
13
34
|
@response = nil
|
35
|
+
@host = nil
|
36
|
+
@host = nil
|
37
|
+
@https = false
|
38
|
+
@mount_path = "/twirp"
|
14
39
|
end
|
15
40
|
|
16
41
|
def before_rpc(&block)
|
@@ -19,32 +44,63 @@ module RailsTwirp
|
|
19
44
|
|
20
45
|
def rpc(service, rpc, request, headers: nil)
|
21
46
|
@request = request
|
22
|
-
service = app.twirp.routes.services[service].to_service
|
23
|
-
|
24
|
-
rack_env = {
|
25
|
-
"HTTP_HOST" => "localhost"
|
26
|
-
}
|
27
|
-
http_request = ActionDispatch::Request.new(rack_env)
|
28
|
-
http_request.headers.merge! headers if headers.present?
|
29
|
-
env = {rack_env: rack_env}
|
30
47
|
|
48
|
+
env = build_rack_env(service, rpc, request, headers)
|
31
49
|
@before_rpc.each do |hook|
|
32
50
|
hook.call(env)
|
33
51
|
end
|
34
52
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
Twirp::Error.internal_with(e)
|
39
|
-
end
|
53
|
+
status, headers, body = app.call(env)
|
54
|
+
@response = decode_rack_response(service, rpc, status, headers, body)
|
55
|
+
set_controller_from_rack_env(env)
|
40
56
|
|
41
|
-
@response
|
42
|
-
@controller = http_request.controller_instance
|
43
|
-
response
|
57
|
+
@response
|
44
58
|
end
|
45
59
|
|
46
60
|
def app
|
47
61
|
RailsTwirp.test_app
|
48
62
|
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def build_rack_env(service, rpc, request, headers)
|
67
|
+
env = {
|
68
|
+
"CONTENT_TYPE" => request_content_type,
|
69
|
+
"HTTPS" => https? ? "on" : "off",
|
70
|
+
"HTTP_HOST" => host,
|
71
|
+
"PATH_INFO" => "#{@mount_path}/#{service.service_full_name}/#{rpc}",
|
72
|
+
"REQUEST_METHOD" => "POST",
|
73
|
+
"SERVER_NAME" => host,
|
74
|
+
"SERVER_PORT" => https? ? "443" : "80",
|
75
|
+
"rack.url_scheme" => https? ? "https" : "http"
|
76
|
+
}
|
77
|
+
if headers.present?
|
78
|
+
http_request = ActionDispatch::Request.new(env)
|
79
|
+
http_request.headers.merge! headers
|
80
|
+
end
|
81
|
+
|
82
|
+
input_class = service.rpcs[rpc][:input_class]
|
83
|
+
env["rack.input"] = StringIO.new(Twirp::Encoding.encode(request, input_class, request_content_type))
|
84
|
+
env
|
85
|
+
end
|
86
|
+
|
87
|
+
def request_content_type
|
88
|
+
Twirp::Encoding::PROTO
|
89
|
+
end
|
90
|
+
|
91
|
+
def decode_rack_response(service, rpc, status, headers, body)
|
92
|
+
body = body.join # body is an Enumerable
|
93
|
+
|
94
|
+
if status === 200
|
95
|
+
output_class = service.rpcs[rpc][:output_class]
|
96
|
+
Twirp::Encoding.decode(body, output_class, headers["Content-Type"])
|
97
|
+
else
|
98
|
+
Twirp::Client.error_from_response(Response.new(status, body, headers))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_controller_from_rack_env(env)
|
103
|
+
@controller = ActionDispatch::Request.new(env).controller_class
|
104
|
+
end
|
49
105
|
end
|
50
106
|
end
|
data/lib/rails_twirp/version.rb
CHANGED
data/lib/rails_twirp.rb
CHANGED
data/rails_twirp.gemspec
CHANGED
@@ -12,6 +12,6 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.files = `git ls-files`.split("\n")
|
13
13
|
spec.test_files = `git ls-files -- test/*`.split("\n")
|
14
14
|
|
15
|
-
spec.add_dependency "rails", "
|
15
|
+
spec.add_dependency "rails", ">= 6.1.3"
|
16
16
|
spec.add_dependency "twirp", "~> 1.7.2"
|
17
17
|
end
|
@@ -11,7 +11,16 @@ class PingControllerTest < RailsTwirp::IntegrationTest
|
|
11
11
|
req = RPC::DummyAPI::PingRequest.new(name: "Bouke")
|
12
12
|
rpc RPC::DummyAPI::DummyService, "PingRender", req
|
13
13
|
refute_instance_of Twirp::Error, response
|
14
|
-
assert_equal "http://
|
14
|
+
assert_equal "http://www.example.com/twirp BoukeBouke", response.double_name
|
15
|
+
end
|
16
|
+
|
17
|
+
test "you can ping render with host and https" do
|
18
|
+
host! "localhost"
|
19
|
+
https!
|
20
|
+
req = RPC::DummyAPI::PingRequest.new(name: "Bouke")
|
21
|
+
rpc RPC::DummyAPI::DummyService, "PingRender", req
|
22
|
+
refute_instance_of Twirp::Error, response
|
23
|
+
assert_equal "https://localhost/twirp BoukeBouke", response.double_name
|
15
24
|
end
|
16
25
|
|
17
26
|
test "you can ping template" do
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.2
|
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: 2021-
|
11
|
+
date: 2021-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 6.1.3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 6.1.3
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -59,6 +59,9 @@ files:
|
|
59
59
|
- lib/rails_twirp/base.rb
|
60
60
|
- lib/rails_twirp/engine.rb
|
61
61
|
- lib/rails_twirp/errors.rb
|
62
|
+
- lib/rails_twirp/implicit_render.rb
|
63
|
+
- lib/rails_twirp/instrumentation.rb
|
64
|
+
- lib/rails_twirp/log_subscriber.rb
|
62
65
|
- lib/rails_twirp/mapper.rb
|
63
66
|
- lib/rails_twirp/render_pb.rb
|
64
67
|
- lib/rails_twirp/rescue.rb
|
@@ -151,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
154
|
- !ruby/object:Gem::Version
|
152
155
|
version: '0'
|
153
156
|
requirements: []
|
154
|
-
rubygems_version: 3.2.
|
157
|
+
rubygems_version: 3.2.22
|
155
158
|
signing_key:
|
156
159
|
specification_version: 4
|
157
160
|
summary: Integrate Twirp into Rails
|