twirp 1.7.2 → 1.9.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/lib/twirp/client.rb +25 -0
- data/lib/twirp/client_json.rb +9 -0
- data/lib/twirp/service.rb +3 -1
- data/lib/twirp/version.rb +1 -1
- data/test/client_json_test.rb +51 -0
- data/test/client_test.rb +70 -1
- data/test/service_test.rb +3 -4
- data/twirp.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2564512dae1e9dcb9345119a0e09ffa0a65bba37c3800a9c6ac6c77c900701cf
|
4
|
+
data.tar.gz: da232ae6c1526bb650fbc32b106191c533e8aa6c11b682d6d28044d612f0def8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41cbdba9e68278362f8674d6b28336c97a13da955535abbbc5b148f006cee1e1413f76df3daa9e93fb1c80297fcfa7d6c1828591a44680bcd4025ca340f0aacc
|
7
|
+
data.tar.gz: b381e1be9c43e0836bf47f60b98e14dc14cdf5ceb444597abd8cc7587e8162b4f7c8feff4f821e50cafa9a68e5db060359c474d8c2d30a5903a16d6e5db24454
|
data/lib/twirp/client.rb
CHANGED
@@ -160,6 +160,31 @@ module Twirp
|
|
160
160
|
body = Encoding.encode(input, rpcdef[:input_class], content_type)
|
161
161
|
|
162
162
|
resp = self.class.make_http_request(@conn, @service_full_name, rpc_method, content_type, req_opts, body)
|
163
|
+
|
164
|
+
rpc_response_thennable(resp) do |resp|
|
165
|
+
rpc_response_to_clientresp(resp, content_type, rpcdef)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# Executes the post-processing on 'resp' in the provided block,
|
172
|
+
# either in a call to ".then { ... }" as a promise-like interface,
|
173
|
+
# or just execute it directly if the object doesn't support "then".
|
174
|
+
# On Ruby 2.6+ all objects have ".then", but they behave the same as
|
175
|
+
# a promise-like object, so this behaves identically.
|
176
|
+
#
|
177
|
+
# This allows extensions of Faraday that return promises to work
|
178
|
+
# natively with twirp-ruby. For normal Faraday, this is a noop.
|
179
|
+
def rpc_response_thennable(resp)
|
180
|
+
return yield resp unless resp.respond_to?(:then)
|
181
|
+
|
182
|
+
resp.then do |resp|
|
183
|
+
yield resp
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def rpc_response_to_clientresp(resp, content_type, rpcdef)
|
163
188
|
if resp.status != 200
|
164
189
|
return ClientResp.new(nil, self.class.error_from_response(resp))
|
165
190
|
end
|
data/lib/twirp/client_json.rb
CHANGED
@@ -36,6 +36,15 @@ module Twirp
|
|
36
36
|
|
37
37
|
encoding = @strict ? Encoding::JSON_STRICT : Encoding::JSON
|
38
38
|
resp = self.class.make_http_request(@conn, @service_full_name, rpc_method, encoding, req_opts, body)
|
39
|
+
|
40
|
+
rpc_response_thennable(resp) do |resp|
|
41
|
+
rpc_response_to_clientresp(resp)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def rpc_response_to_clientresp(resp)
|
39
48
|
if resp.status != 200
|
40
49
|
return ClientResp.new(nil, self.class.error_from_response(resp))
|
41
50
|
end
|
data/lib/twirp/service.rb
CHANGED
@@ -136,7 +136,9 @@ module Twirp
|
|
136
136
|
|
137
137
|
input = nil
|
138
138
|
begin
|
139
|
-
|
139
|
+
body_str = rack_request.body.read
|
140
|
+
rack_request.body.rewind # allow other middleware to read again (https://github.com/twitchtv/twirp-ruby/issues/50)
|
141
|
+
input = Encoding.decode(body_str, env[:input_class], content_type)
|
140
142
|
rescue => e
|
141
143
|
error_msg = "Invalid request body for rpc method #{method_name.inspect} with Content-Type=#{content_type}"
|
142
144
|
if e.is_a?(Google::Protobuf::ParseError)
|
data/lib/twirp/version.rb
CHANGED
data/test/client_json_test.rb
CHANGED
@@ -29,6 +29,30 @@ class ClientJSONTest < Minitest::Test
|
|
29
29
|
assert_equal 3, resp.data["blah_resp"]
|
30
30
|
end
|
31
31
|
|
32
|
+
def test_client_json_thennable
|
33
|
+
c = Twirp::ClientJSON.new(conn_stub_thennable("/my.pkg.Talking/Blah") {|req|
|
34
|
+
assert_equal "application/json", req.request_headers['Content-Type']
|
35
|
+
assert_equal '{"blah1":1,"blah2":2}', req.body # body is json
|
36
|
+
|
37
|
+
[200, {}, '{"blah_resp": 3}']
|
38
|
+
}, package: "my.pkg", service: "Talking")
|
39
|
+
|
40
|
+
resp_thennable = c.rpc :Blah, blah1: 1, blah2: 2
|
41
|
+
# the final `.then {}` call will yield a ClientResp
|
42
|
+
assert resp_thennable.is_a?(Thennable)
|
43
|
+
resp = resp_thennable.value
|
44
|
+
assert resp.is_a?(Twirp::ClientResp)
|
45
|
+
|
46
|
+
# the final Thennable will have come from one with a faraday response
|
47
|
+
assert resp_thennable.parent.is_a?(Thennable)
|
48
|
+
assert resp_thennable.parent.value.is_a?(Faraday::Response)
|
49
|
+
|
50
|
+
# the final ClientResp should look the same as when then isn't used
|
51
|
+
assert_nil resp.error
|
52
|
+
refute_nil resp.data
|
53
|
+
assert_equal 3, resp.data["blah_resp"]
|
54
|
+
end
|
55
|
+
|
32
56
|
def test_client_json_strict_encoding
|
33
57
|
c = Twirp::ClientJSON.new(conn_stub("/my.pkg.Talking/Blah") {|req|
|
34
58
|
assert_equal "application/json; strict=true", req.request_headers['Content-Type']
|
@@ -77,4 +101,31 @@ class ClientJSONTest < Minitest::Test
|
|
77
101
|
end
|
78
102
|
end
|
79
103
|
|
104
|
+
# mock of a promise-like thennable, allowing a call to ".then" to get the real value
|
105
|
+
class Thennable
|
106
|
+
attr_reader :value, :parent
|
107
|
+
|
108
|
+
def initialize(value, parent = nil)
|
109
|
+
@value = value
|
110
|
+
@parent = parent
|
111
|
+
end
|
112
|
+
|
113
|
+
def then(&block)
|
114
|
+
# similar to a promise, but runs immediately
|
115
|
+
Thennable.new(block.call(@value), self)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
module ThennableFaraday
|
120
|
+
def post(*)
|
121
|
+
Thennable.new(super)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def conn_stub_thennable(path, &block)
|
126
|
+
s = conn_stub(path, &block)
|
127
|
+
s.extend(ThennableFaraday)
|
128
|
+
s
|
129
|
+
end
|
130
|
+
|
80
131
|
end
|
data/test/client_test.rb
CHANGED
@@ -17,7 +17,7 @@ class ClientTest < Minitest::Test
|
|
17
17
|
|
18
18
|
def test_new_with_invalid_url
|
19
19
|
assert_raises URI::InvalidURIError do
|
20
|
-
EmptyClient.new("
|
20
|
+
EmptyClient.new("invalid uri with unescaped spaces")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -72,6 +72,27 @@ class ClientTest < Minitest::Test
|
|
72
72
|
assert_equal "red", resp.data.color
|
73
73
|
end
|
74
74
|
|
75
|
+
def test_proto_thennable
|
76
|
+
c = Example::HaberdasherClient.new(conn_stub_thennable("/example.Haberdasher/MakeHat") {|req|
|
77
|
+
[200, protoheader, proto(Example::Hat, inches: 99, color: "red")]
|
78
|
+
})
|
79
|
+
resp_thennable = c.make_hat({})
|
80
|
+
|
81
|
+
# the final `.then {}` call will yield a ClientResp
|
82
|
+
assert resp_thennable.is_a?(Thennable)
|
83
|
+
resp = resp_thennable.value
|
84
|
+
assert resp.is_a?(Twirp::ClientResp)
|
85
|
+
|
86
|
+
# the final Thennable will have come from one with a faraday response
|
87
|
+
assert resp_thennable.parent.is_a?(Thennable)
|
88
|
+
assert resp_thennable.parent.value.is_a?(Faraday::Response)
|
89
|
+
|
90
|
+
# the final ClientResp should look the same as when then isn't used
|
91
|
+
assert_nil resp.error
|
92
|
+
assert_equal 99, resp.data.inches
|
93
|
+
assert_equal "red", resp.data.color
|
94
|
+
end
|
95
|
+
|
75
96
|
def test_proto_send_headers
|
76
97
|
c = Example::HaberdasherClient.new(conn_stub("/example.Haberdasher/MakeHat") {|req|
|
77
98
|
assert_equal "Bar", req.request_headers['My-Foo-Header']
|
@@ -196,6 +217,27 @@ class ClientTest < Minitest::Test
|
|
196
217
|
assert_equal "red", resp.data.color
|
197
218
|
end
|
198
219
|
|
220
|
+
def test_json_thennable
|
221
|
+
c = Example::HaberdasherClient.new(conn_stub_thennable("/example.Haberdasher/MakeHat") {|req|
|
222
|
+
[200, jsonheader, '{"inches": 99, "color": "red"}']
|
223
|
+
}, content_type: "application/json")
|
224
|
+
|
225
|
+
resp_thennable = c.make_hat({})
|
226
|
+
# the final `.then {}` call will yield a ClientResp
|
227
|
+
assert resp_thennable.is_a?(Thennable)
|
228
|
+
resp = resp_thennable.value
|
229
|
+
assert resp.is_a?(Twirp::ClientResp)
|
230
|
+
|
231
|
+
# the final Thennable will have come from one with a faraday response
|
232
|
+
assert resp_thennable.parent.is_a?(Thennable)
|
233
|
+
assert resp_thennable.parent.value.is_a?(Faraday::Response)
|
234
|
+
|
235
|
+
# the final ClientResp should look the same as when then isn't used
|
236
|
+
assert_nil resp.error
|
237
|
+
assert_equal 99, resp.data.inches
|
238
|
+
assert_equal "red", resp.data.color
|
239
|
+
end
|
240
|
+
|
199
241
|
def test_json_send_headers
|
200
242
|
c = Example::HaberdasherClient.new(conn_stub("/example.Haberdasher/MakeHat") {|req|
|
201
243
|
assert_equal "Bar", req.request_headers['My-Foo-Header']
|
@@ -337,4 +379,31 @@ class ClientTest < Minitest::Test
|
|
337
379
|
end
|
338
380
|
end
|
339
381
|
|
382
|
+
# mock of a promise-like thennable, allowing a call to ".then" to get the real value
|
383
|
+
class Thennable
|
384
|
+
attr_reader :value, :parent
|
385
|
+
|
386
|
+
def initialize(value, parent = nil)
|
387
|
+
@value = value
|
388
|
+
@parent = parent
|
389
|
+
end
|
390
|
+
|
391
|
+
def then(&block)
|
392
|
+
# similar to a promise, but runs immediately
|
393
|
+
Thennable.new(block.call(@value), self)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
module ThennableFaraday
|
398
|
+
def post(*)
|
399
|
+
Thennable.new(super)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def conn_stub_thennable(path, &block)
|
404
|
+
s = conn_stub(path, &block)
|
405
|
+
s.extend(ThennableFaraday)
|
406
|
+
s
|
407
|
+
end
|
408
|
+
|
340
409
|
end
|
data/test/service_test.rb
CHANGED
@@ -168,7 +168,7 @@ class ServiceTest < Minitest::Test
|
|
168
168
|
assert_equal({
|
169
169
|
"code" => 'malformed',
|
170
170
|
"msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/json: ' +
|
171
|
-
"Error occurred during parsing:
|
171
|
+
"Error occurred during parsing: Error parsing JSON @1:0: Expected: '{'",
|
172
172
|
"meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
|
173
173
|
}, JSON.parse(body[0]))
|
174
174
|
end
|
@@ -183,7 +183,7 @@ class ServiceTest < Minitest::Test
|
|
183
183
|
assert_equal({
|
184
184
|
"code" => 'malformed',
|
185
185
|
"msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/protobuf: ' +
|
186
|
-
'Error occurred during parsing
|
186
|
+
'Error occurred during parsing',
|
187
187
|
"meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
|
188
188
|
}, JSON.parse(body[0]))
|
189
189
|
end
|
@@ -206,7 +206,7 @@ class ServiceTest < Minitest::Test
|
|
206
206
|
assert_equal({
|
207
207
|
"code" => 'malformed',
|
208
208
|
"msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/json; strict=true: ' +
|
209
|
-
"Error occurred during parsing: No such field: fake",
|
209
|
+
"Error occurred during parsing: Error parsing JSON @1:20: No such field: fake",
|
210
210
|
"meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
|
211
211
|
}, JSON.parse(body[0]))
|
212
212
|
end
|
@@ -850,4 +850,3 @@ class ServiceTest < Minitest::Test
|
|
850
850
|
end)
|
851
851
|
end
|
852
852
|
end
|
853
|
-
|
data/twirp.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_runtime_dependency 'google-protobuf', '~> 3.0', '>= 3.7.0'
|
23
23
|
spec.add_runtime_dependency 'faraday', '< 2' # for clients
|
24
24
|
|
25
|
-
spec.add_development_dependency 'bundler', '~>
|
25
|
+
spec.add_development_dependency 'bundler', '~> 2'
|
26
26
|
spec.add_development_dependency 'minitest', '>= 5'
|
27
27
|
spec.add_development_dependency 'rake'
|
28
28
|
spec.add_development_dependency 'rack', '>= 2.2.3'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twirp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyrus A. Forbes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-10-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: google-protobuf
|
@@ -51,14 +51,14 @@ dependencies:
|
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '2'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
57
|
version_requirements: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '2'
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: minitest
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|