twirp 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +141 -62
- data/lib/twirp.rb +1 -0
- data/lib/twirp/client.rb +147 -0
- data/lib/twirp/error.rb +12 -8
- data/lib/twirp/service.rb +42 -76
- data/lib/twirp/service_dsl.rb +61 -0
- data/lib/twirp/version.rb +1 -1
- data/test/client_test.rb +203 -0
- data/test/fake_services.rb +21 -1
- data/test/service_test.rb +27 -28
- data/twirp.gemspec +6 -6
- metadata +30 -15
- data/.gitignore +0 -1
- data/Gemfile.lock +0 -20
- data/example/Gemfile +0 -7
- data/example/Gemfile.lock +0 -17
- data/example/gen/haberdasher_pb.rb +0 -18
- data/example/gen/haberdasher_twirp.rb +0 -10
- data/example/haberdasher.proto +0 -14
- data/example/main.rb +0 -14
- data/protoc-gen-twirp_ruby/main.go +0 -259
data/test/fake_services.rb
CHANGED
@@ -29,7 +29,11 @@ module Example
|
|
29
29
|
class Haberdasher < Twirp::Service
|
30
30
|
package "example"
|
31
31
|
service "Haberdasher"
|
32
|
-
rpc :MakeHat, Size, Hat, :
|
32
|
+
rpc :MakeHat, Size, Hat, :ruby_method => :make_hat
|
33
|
+
end
|
34
|
+
|
35
|
+
class HaberdasherClient < Twirp::Client
|
36
|
+
client_for Haberdasher
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
@@ -48,3 +52,19 @@ end
|
|
48
52
|
# Twirp Service with no package and no rpc methods.
|
49
53
|
class EmptyService < Twirp::Service
|
50
54
|
end
|
55
|
+
class EmptyClient < Twirp::Client
|
56
|
+
end
|
57
|
+
|
58
|
+
# Foo message
|
59
|
+
Google::Protobuf::DescriptorPool.generated_pool.build do
|
60
|
+
add_message "Foo" do
|
61
|
+
optional :foo, :string, 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
Foo = Google::Protobuf::DescriptorPool.generated_pool.lookup("Foo").msgclass
|
65
|
+
|
66
|
+
# Foo Client
|
67
|
+
class FooClient < Twirp::Client
|
68
|
+
service "Foo"
|
69
|
+
rpc :Foo, Foo, Foo, :ruby_method => :foo
|
70
|
+
end
|
data/test/service_test.rb
CHANGED
@@ -13,14 +13,14 @@ class ServiceTest < Minitest::Test
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# The rpc DSL should properly build the base Twirp environment for each rpc method.
|
16
|
-
def
|
17
|
-
assert_equal 1, Example::Haberdasher.
|
16
|
+
def test_rpcs_accessor
|
17
|
+
assert_equal 1, Example::Haberdasher.rpcs.size
|
18
18
|
assert_equal({
|
19
19
|
rpc_method: :MakeHat,
|
20
20
|
input_class: Example::Size,
|
21
21
|
output_class: Example::Hat,
|
22
|
-
|
23
|
-
}, Example::Haberdasher.
|
22
|
+
ruby_method: :make_hat,
|
23
|
+
}, Example::Haberdasher.rpcs["MakeHat"])
|
24
24
|
end
|
25
25
|
|
26
26
|
# DSL package and service define the proper data on the service
|
@@ -33,7 +33,7 @@ class ServiceTest < Minitest::Test
|
|
33
33
|
assert_equal "EmptyService", EmptyService.service_name # defaults to class name
|
34
34
|
assert_equal "EmptyService", EmptyService.service_full_name # with no package is just the service name
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def test_init_service
|
38
38
|
svc = Example::Haberdasher.new(HaberdasherHandler.new)
|
39
39
|
assert svc.respond_to?(:call) # so it is a Proc that can be used as Rack middleware
|
@@ -71,35 +71,35 @@ class ServiceTest < Minitest::Test
|
|
71
71
|
assert_equal 404, status
|
72
72
|
assert_equal 'application/json', headers['Content-Type']
|
73
73
|
assert_equal({
|
74
|
-
"code" => 'bad_route',
|
74
|
+
"code" => 'bad_route',
|
75
75
|
"msg" => 'Invalid rpc method "MakeUnicorns"',
|
76
76
|
"meta" => {"twirp_invalid_route" => "POST /twirp/example.Haberdasher/MakeUnicorns"},
|
77
|
-
}, JSON.parse(body[0]))
|
77
|
+
}, JSON.parse(body[0]))
|
78
78
|
end
|
79
79
|
|
80
80
|
def test_bad_route_with_wrong_http_method
|
81
|
-
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
81
|
+
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
82
82
|
method: "GET", input: '{"inches": 10}', "CONTENT_TYPE" => "application/json"
|
83
83
|
status, headers, body = haberdasher_service.call(rack_env)
|
84
84
|
|
85
85
|
assert_equal 404, status
|
86
86
|
assert_equal 'application/json', headers['Content-Type']
|
87
87
|
assert_equal({
|
88
|
-
"code" => 'bad_route',
|
88
|
+
"code" => 'bad_route',
|
89
89
|
"msg" => 'HTTP request method must be POST',
|
90
90
|
"meta" => {"twirp_invalid_route" => "GET /example.Haberdasher/MakeHat"},
|
91
91
|
}, JSON.parse(body[0]))
|
92
92
|
end
|
93
93
|
|
94
94
|
def test_bad_route_with_wrong_content_type
|
95
|
-
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
95
|
+
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
96
96
|
method: "POST", input: 'free text', "CONTENT_TYPE" => "text/plain"
|
97
97
|
status, headers, body = haberdasher_service.call(rack_env)
|
98
98
|
|
99
99
|
assert_equal 404, status
|
100
100
|
assert_equal 'application/json', headers['Content-Type']
|
101
101
|
assert_equal({
|
102
|
-
"code" => 'bad_route',
|
102
|
+
"code" => 'bad_route',
|
103
103
|
"msg" => 'unexpected Content-Type: "text/plain". Content-Type header must be one of application/json or application/protobuf',
|
104
104
|
"meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
|
105
105
|
}, JSON.parse(body[0]))
|
@@ -112,7 +112,7 @@ class ServiceTest < Minitest::Test
|
|
112
112
|
assert_equal 404, status
|
113
113
|
assert_equal 'application/json', headers['Content-Type']
|
114
114
|
assert_equal({
|
115
|
-
"code" => 'bad_route',
|
115
|
+
"code" => 'bad_route',
|
116
116
|
"msg" => 'Invalid route. Expected format: POST {BaseURL}/example.Haberdasher/{Method}',
|
117
117
|
"meta" => {"twirp_invalid_route" => "POST /wrongpath"},
|
118
118
|
}, JSON.parse(body[0]))
|
@@ -125,35 +125,35 @@ class ServiceTest < Minitest::Test
|
|
125
125
|
assert_equal 404, status
|
126
126
|
assert_equal 'application/json', headers['Content-Type'] # error responses are always JSON, even for Protobuf requests
|
127
127
|
assert_equal({
|
128
|
-
"code" => 'bad_route',
|
128
|
+
"code" => 'bad_route',
|
129
129
|
"msg" => 'Invalid route. Expected format: POST {BaseURL}/example.Haberdasher/{Method}',
|
130
130
|
"meta" => {"twirp_invalid_route" => "POST /another/wrong.Path/MakeHat"},
|
131
131
|
}, JSON.parse(body[0]))
|
132
132
|
end
|
133
133
|
|
134
134
|
def test_bad_route_with_wrong_json_body
|
135
|
-
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
135
|
+
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
136
136
|
method: "POST", input: 'bad json', "CONTENT_TYPE" => "application/json"
|
137
137
|
status, headers, body = haberdasher_service.call(rack_env)
|
138
138
|
|
139
139
|
assert_equal 404, status
|
140
140
|
assert_equal 'application/json', headers['Content-Type']
|
141
141
|
assert_equal({
|
142
|
-
"code" => 'bad_route',
|
142
|
+
"code" => 'bad_route',
|
143
143
|
"msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/json',
|
144
144
|
"meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
|
145
145
|
}, JSON.parse(body[0]))
|
146
146
|
end
|
147
147
|
|
148
148
|
def test_bad_route_with_wrong_protobuf_body
|
149
|
-
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
149
|
+
rack_env = Rack::MockRequest.env_for "example.Haberdasher/MakeHat",
|
150
150
|
method: "POST", input: 'bad protobuf', "CONTENT_TYPE" => "application/protobuf"
|
151
151
|
status, headers, body = haberdasher_service.call(rack_env)
|
152
152
|
|
153
153
|
assert_equal 404, status
|
154
154
|
assert_equal 'application/json', headers['Content-Type']
|
155
155
|
assert_equal({
|
156
|
-
"code" => 'bad_route',
|
156
|
+
"code" => 'bad_route',
|
157
157
|
"msg" => 'Invalid request body for rpc method "MakeHat" with Content-Type=application/protobuf',
|
158
158
|
"meta" => {"twirp_invalid_route" => "POST /example.Haberdasher/MakeHat"},
|
159
159
|
}, JSON.parse(body[0]))
|
@@ -230,7 +230,7 @@ class ServiceTest < Minitest::Test
|
|
230
230
|
assert_equal 400, status
|
231
231
|
assert_equal 'application/json', headers['Content-Type'] # error responses are always JSON, even for Protobuf requests
|
232
232
|
assert_equal({
|
233
|
-
"code" => 'invalid_argument',
|
233
|
+
"code" => 'invalid_argument',
|
234
234
|
"msg" => "I don't like that size",
|
235
235
|
}, JSON.parse(body[0]))
|
236
236
|
end
|
@@ -243,7 +243,7 @@ class ServiceTest < Minitest::Test
|
|
243
243
|
|
244
244
|
rack_env = proto_req "/example.Haberdasher/MakeHat", Example::Size.new
|
245
245
|
status, headers, body = svc.call(rack_env)
|
246
|
-
|
246
|
+
|
247
247
|
assert_equal 200, status
|
248
248
|
assert_equal "public, max-age=60", headers["Cache-Control"] # set by the handler
|
249
249
|
assert_equal "application/protobuf", headers["Content-Type"] # set by Twirp::Service
|
@@ -397,7 +397,7 @@ class ServiceTest < Minitest::Test
|
|
397
397
|
assert_equal 500, status
|
398
398
|
refute handler_called
|
399
399
|
assert_equal({
|
400
|
-
"code" => 'intenal',
|
400
|
+
"code" => 'intenal',
|
401
401
|
"msg" => 'error from before hook',
|
402
402
|
}, JSON.parse(body[0]))
|
403
403
|
end
|
@@ -474,7 +474,7 @@ class ServiceTest < Minitest::Test
|
|
474
474
|
assert_equal 500, status
|
475
475
|
assert_equal 'application/json', headers['Content-Type']
|
476
476
|
assert_equal({
|
477
|
-
"code" => 'intenal',
|
477
|
+
"code" => 'intenal',
|
478
478
|
"msg" => 'hook1 failed',
|
479
479
|
}, JSON.parse(body[0]))
|
480
480
|
|
@@ -517,7 +517,7 @@ class ServiceTest < Minitest::Test
|
|
517
517
|
assert_equal 500, status
|
518
518
|
refute success_called # after hook not called
|
519
519
|
assert_equal({
|
520
|
-
"code" => 'intenal',
|
520
|
+
"code" => 'intenal',
|
521
521
|
"msg" => 'error from handler',
|
522
522
|
}, JSON.parse(body[0]))
|
523
523
|
end
|
@@ -591,7 +591,7 @@ class ServiceTest < Minitest::Test
|
|
591
591
|
|
592
592
|
assert_equal 500, status
|
593
593
|
assert_equal({
|
594
|
-
"code" => 'intenal',
|
594
|
+
"code" => 'intenal',
|
595
595
|
"msg" => 'before failed',
|
596
596
|
}, JSON.parse(body[0]))
|
597
597
|
assert error_called
|
@@ -634,7 +634,7 @@ class ServiceTest < Minitest::Test
|
|
634
634
|
svc = Example::Haberdasher.new(HaberdasherHandler.new do |size, env|
|
635
635
|
return Twirp::Error.internal "handler error"
|
636
636
|
end)
|
637
|
-
|
637
|
+
|
638
638
|
error_called = false
|
639
639
|
svc.on_error do |twerr, env|
|
640
640
|
error_called = true
|
@@ -647,7 +647,7 @@ class ServiceTest < Minitest::Test
|
|
647
647
|
|
648
648
|
assert_equal 500, status
|
649
649
|
assert_equal({
|
650
|
-
"code" => 'intenal',
|
650
|
+
"code" => 'intenal',
|
651
651
|
"msg" => 'handler error',
|
652
652
|
}, JSON.parse(body[0]))
|
653
653
|
assert error_called
|
@@ -771,18 +771,17 @@ class ServiceTest < Minitest::Test
|
|
771
771
|
|
772
772
|
|
773
773
|
|
774
|
-
|
775
774
|
# Test Helpers
|
776
775
|
# ------------
|
777
776
|
|
778
777
|
def json_req(path, attrs)
|
779
|
-
Rack::MockRequest.env_for path, method: "POST",
|
778
|
+
Rack::MockRequest.env_for path, method: "POST",
|
780
779
|
input: JSON.generate(attrs),
|
781
780
|
"CONTENT_TYPE" => "application/json"
|
782
781
|
end
|
783
782
|
|
784
783
|
def proto_req(path, proto_message)
|
785
|
-
Rack::MockRequest.env_for path, method: "POST",
|
784
|
+
Rack::MockRequest.env_for path, method: "POST",
|
786
785
|
input: proto_message.class.encode(proto_message),
|
787
786
|
"CONTENT_TYPE" => "application/protobuf"
|
788
787
|
end
|
data/twirp.gemspec
CHANGED
@@ -10,16 +10,16 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ["Cyrus A. Forbes", "Mario Izquierdo"]
|
11
11
|
spec.email = ["forbescyrus@gmail.com", "tothemario@gmail.com"]
|
12
12
|
spec.summary = %q{Twirp services in Ruby.}
|
13
|
-
spec.description = %q{Twirp is a simple RPC framework with protobuf service definitions. The Twirp gem provides support for Ruby.}
|
13
|
+
spec.description = %q{Twirp is a simple RPC framework with protobuf service definitions. The Twirp gem provides native support for Ruby.}
|
14
14
|
spec.homepage = "https://github.com/cyrusaf/ruby-twirp"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
|
-
spec.files =
|
18
|
-
spec.
|
19
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.files = Dir['lib/**/*'] + %w(Gemfile LICENSE README.md twirp.gemspec)
|
18
|
+
spec.test_files = Dir['test/**/*']
|
20
19
|
spec.require_paths = ["lib"]
|
21
20
|
|
22
|
-
spec.add_runtime_dependency 'google-protobuf', '>= 3.0.0'
|
21
|
+
spec.add_runtime_dependency 'google-protobuf', '~> 3.0', '>= 3.0.0'
|
22
|
+
spec.add_runtime_dependency 'faraday', '~> 0'
|
23
23
|
|
24
|
-
spec.add_development_dependency 'bundler', '
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1'
|
25
25
|
end
|
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: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyrus A. Forbes
|
@@ -9,12 +9,15 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: google-protobuf
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3.0'
|
18
21
|
- - ">="
|
19
22
|
- !ruby/object:Gem::Version
|
20
23
|
version: 3.0.0
|
@@ -22,25 +25,42 @@ dependencies:
|
|
22
25
|
prerelease: false
|
23
26
|
version_requirements: !ruby/object:Gem::Requirement
|
24
27
|
requirements:
|
28
|
+
- - "~>"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.0'
|
25
31
|
- - ">="
|
26
32
|
- !ruby/object:Gem::Version
|
27
33
|
version: 3.0.0
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: faraday
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
28
48
|
- !ruby/object:Gem::Dependency
|
29
49
|
name: bundler
|
30
50
|
requirement: !ruby/object:Gem::Requirement
|
31
51
|
requirements:
|
32
|
-
- - "
|
52
|
+
- - "~>"
|
33
53
|
- !ruby/object:Gem::Version
|
34
54
|
version: '1'
|
35
55
|
type: :development
|
36
56
|
prerelease: false
|
37
57
|
version_requirements: !ruby/object:Gem::Requirement
|
38
58
|
requirements:
|
39
|
-
- - "
|
59
|
+
- - "~>"
|
40
60
|
- !ruby/object:Gem::Version
|
41
61
|
version: '1'
|
42
62
|
description: Twirp is a simple RPC framework with protobuf service definitions. The
|
43
|
-
Twirp gem provides support for Ruby.
|
63
|
+
Twirp gem provides native support for Ruby.
|
44
64
|
email:
|
45
65
|
- forbescyrus@gmail.com
|
46
66
|
- tothemario@gmail.com
|
@@ -48,22 +68,16 @@ executables: []
|
|
48
68
|
extensions: []
|
49
69
|
extra_rdoc_files: []
|
50
70
|
files:
|
51
|
-
- ".gitignore"
|
52
71
|
- Gemfile
|
53
|
-
- Gemfile.lock
|
54
72
|
- LICENSE
|
55
73
|
- README.md
|
56
|
-
- example/Gemfile
|
57
|
-
- example/Gemfile.lock
|
58
|
-
- example/gen/haberdasher_pb.rb
|
59
|
-
- example/gen/haberdasher_twirp.rb
|
60
|
-
- example/haberdasher.proto
|
61
|
-
- example/main.rb
|
62
74
|
- lib/twirp.rb
|
75
|
+
- lib/twirp/client.rb
|
63
76
|
- lib/twirp/error.rb
|
64
77
|
- lib/twirp/service.rb
|
78
|
+
- lib/twirp/service_dsl.rb
|
65
79
|
- lib/twirp/version.rb
|
66
|
-
-
|
80
|
+
- test/client_test.rb
|
67
81
|
- test/error_test.rb
|
68
82
|
- test/fake_services.rb
|
69
83
|
- test/service_test.rb
|
@@ -88,11 +102,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
102
|
version: '0'
|
89
103
|
requirements: []
|
90
104
|
rubyforge_project:
|
91
|
-
rubygems_version: 2.6.
|
105
|
+
rubygems_version: 2.6.8
|
92
106
|
signing_key:
|
93
107
|
specification_version: 4
|
94
108
|
summary: Twirp services in Ruby.
|
95
109
|
test_files:
|
110
|
+
- test/client_test.rb
|
96
111
|
- test/error_test.rb
|
97
112
|
- test/fake_services.rb
|
98
113
|
- test/service_test.rb
|
data/.gitignore
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
*.gem
|
data/Gemfile.lock
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
twirp (0.0.1)
|
5
|
-
google-protobuf (>= 3.0.0)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
google-protobuf (3.5.1.2)
|
11
|
-
|
12
|
-
PLATFORMS
|
13
|
-
ruby
|
14
|
-
|
15
|
-
DEPENDENCIES
|
16
|
-
bundler (>= 1)
|
17
|
-
twirp!
|
18
|
-
|
19
|
-
BUNDLED WITH
|
20
|
-
1.14.6
|
data/example/Gemfile
DELETED
data/example/Gemfile.lock
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
2
|
-
# source: haberdasher.proto
|
3
|
-
|
4
|
-
require 'google/protobuf'
|
5
|
-
|
6
|
-
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
-
add_message "example.HelloWorldRequest" do
|
8
|
-
optional :name, :string, 1
|
9
|
-
end
|
10
|
-
add_message "example.HelloWorldResponse" do
|
11
|
-
optional :message, :string, 1
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
module Example
|
16
|
-
HelloWorldRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("example.HelloWorldRequest").msgclass
|
17
|
-
HelloWorldResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("example.HelloWorldResponse").msgclass
|
18
|
-
end
|
@@ -1,10 +0,0 @@
|
|
1
|
-
# Code generated by protoc-gen-twirp_ruby, DO NOT EDIT.
|
2
|
-
require 'twirp'
|
3
|
-
|
4
|
-
module Example
|
5
|
-
class HaberdasherService < Twirp::Service
|
6
|
-
package "example"
|
7
|
-
service "Haberdasher"
|
8
|
-
rpc :HelloWorld, HelloWorldRequest, HelloWorldResponse, :handler_method => :hello_world
|
9
|
-
end
|
10
|
-
end
|