grpc-rest 0.1.19 → 0.1.21
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/CHANGELOG +6 -0
- data/Gemfile.lock +13 -1
- data/README.md +4 -0
- data/grpc-rest.gemspec +1 -0
- data/lib/grpc_rest/version.rb +1 -1
- data/lib/grpc_rest.rb +50 -40
- data/protoc-gen-rails/testdata/test_service.proto +1 -0
- data/spec/grpc_rest_spec.rb +3 -2
- data/spec/gruf_spec.rb +51 -0
- data/spec/test_service_pb.rb +1 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '090bea208182dd067a0a1f0347e9cb4484610dca9a7a4f1c2f7689fa216d40e8'
|
4
|
+
data.tar.gz: 3695746b75dcbba972e88249ad2785551d7e0e4fbd9d8a01140496dfa4f2a002
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 963b26c2d6141a20a3739341bd564005000a7dfdbe42a0bee3becffd04bc1f7d455a692135f21037399e12431c0bba91e73c17b4aa0d4bef6c75944fef4d2782
|
7
|
+
data.tar.gz: eaad993e480c9a9e7d05c55dedd4a7de06712666b3cd5601b6566bcd5da955a5b30e7528562b117b35297862cc4a6ad3930c8aabcc5eb81bbba6417ab3babae5
|
data/CHANGELOG
CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
+
# 0.1.21 - 2024-09-12
|
11
|
+
- Feature: Support gruf Interceptors.
|
12
|
+
|
13
|
+
# 0.1.20 - 2024-09-10
|
14
|
+
- Fix: Repeated float values weren't being treated as arrays and were crashing.
|
15
|
+
|
10
16
|
# 0.1.19 - 2024-08-22
|
11
17
|
|
12
18
|
- Add `emit_defaults` extension for JSON responses.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grpc-rest (0.1.
|
4
|
+
grpc-rest (0.1.21)
|
5
5
|
grpc
|
6
6
|
rails (>= 6.0)
|
7
7
|
|
@@ -91,8 +91,18 @@ GEM
|
|
91
91
|
grpc (1.62.0-x86_64-linux)
|
92
92
|
google-protobuf (~> 3.25)
|
93
93
|
googleapis-common-protos-types (~> 1.0)
|
94
|
+
grpc-tools (1.64.0)
|
95
|
+
gruf (2.20.0)
|
96
|
+
activesupport (> 4)
|
97
|
+
concurrent-ruby (> 1)
|
98
|
+
grpc (~> 1.10)
|
99
|
+
grpc-tools (~> 1.10)
|
100
|
+
json (>= 2.3)
|
101
|
+
slop (>= 4.6)
|
102
|
+
zeitwerk (>= 2)
|
94
103
|
i18n (1.14.4)
|
95
104
|
concurrent-ruby (~> 1.0)
|
105
|
+
json (2.7.2)
|
96
106
|
loofah (2.22.0)
|
97
107
|
crass (~> 1.0.2)
|
98
108
|
nokogiri (>= 1.12.0)
|
@@ -169,6 +179,7 @@ GEM
|
|
169
179
|
rspec-mocks (~> 3.13)
|
170
180
|
rspec-support (~> 3.13)
|
171
181
|
rspec-support (3.13.1)
|
182
|
+
slop (4.10.1)
|
172
183
|
thor (1.3.1)
|
173
184
|
timeout (0.4.1)
|
174
185
|
tzinfo (2.0.6)
|
@@ -184,6 +195,7 @@ PLATFORMS
|
|
184
195
|
|
185
196
|
DEPENDENCIES
|
186
197
|
grpc-rest!
|
198
|
+
gruf
|
187
199
|
rspec-rails
|
188
200
|
|
189
201
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -152,6 +152,10 @@ service MyService {
|
|
152
152
|
}
|
153
153
|
```
|
154
154
|
|
155
|
+
## Gruf Interceptors
|
156
|
+
|
157
|
+
grpc-rest supports [gruf](https://github.com/bigcommerce/gruf) Interceptors. As long as you're not using a custom interceptor
|
158
|
+
registry, your interceptors will be called normally around the controller.
|
155
159
|
|
156
160
|
## To Do
|
157
161
|
|
data/grpc-rest.gemspec
CHANGED
data/lib/grpc_rest/version.rb
CHANGED
data/lib/grpc_rest.rb
CHANGED
@@ -63,56 +63,55 @@ module GrpcRest
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
def map_proto_type(
|
66
|
+
def map_proto_type(descriptor, val)
|
67
|
+
if descriptor.subtype.is_a?(Google::Protobuf::EnumDescriptor)
|
68
|
+
return handle_enum_values(descriptor, val)
|
69
|
+
end
|
70
|
+
|
71
|
+
case descriptor.type
|
72
|
+
when *%i(int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64)
|
73
|
+
return val.to_i
|
74
|
+
when *%i(float double)
|
75
|
+
return val.to_f
|
76
|
+
when :bool
|
77
|
+
return !!val
|
78
|
+
end
|
79
|
+
|
80
|
+
case descriptor.subtype&.name
|
81
|
+
when 'google.protobuf.Struct'
|
82
|
+
return Google::Protobuf::Struct.from_hash(val)
|
83
|
+
when 'google.protobuf.Timestamp'
|
84
|
+
if val.is_a?(Numeric)
|
85
|
+
return Google::Protobuf::Timestamp.from_time(Time.at(val))
|
86
|
+
else
|
87
|
+
return Google::Protobuf::Timestamp.from_time(Time.new(val))
|
88
|
+
end
|
89
|
+
when 'google.protobuf.Value'
|
90
|
+
return Google::Protobuf::Value.from_ruby(val)
|
91
|
+
when 'google.protobuf.ListValue'
|
92
|
+
return Google::Protobuf::ListValue.from_a(val)
|
93
|
+
else
|
94
|
+
return map_proto_record(descriptor.subtype, val)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def map_proto_record(proto, params)
|
67
99
|
proto.to_a.each do |descriptor|
|
68
100
|
field = descriptor.name
|
69
101
|
val = params[field]
|
70
102
|
next if val.nil?
|
71
|
-
if descriptor.subtype.is_a?(Google::Protobuf::EnumDescriptor)
|
72
|
-
if descriptor.label == :repeated
|
73
|
-
params[field] = val.map { |v| handle_enum_values(descriptor, v)}
|
74
|
-
else
|
75
|
-
params[field] = handle_enum_values(descriptor, val)
|
76
|
-
end
|
77
|
-
next
|
78
|
-
end
|
79
|
-
|
80
|
-
case descriptor.type
|
81
|
-
when *%i(int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64)
|
82
|
-
params[field] = val.to_i
|
83
|
-
when *%i(float double)
|
84
|
-
params[field] = val.to_f
|
85
|
-
when :bool
|
86
|
-
params[field] = !!val
|
87
|
-
end
|
88
103
|
|
89
|
-
|
90
|
-
|
91
|
-
params[field] = Google::Protobuf::Struct.from_hash(val)
|
92
|
-
when 'google.protobuf.Timestamp'
|
93
|
-
if val.is_a?(Numeric)
|
94
|
-
params[field] = Google::Protobuf::Timestamp.from_time(Time.at(val))
|
95
|
-
else
|
96
|
-
params[field] = Google::Protobuf::Timestamp.from_time(Time.new(val))
|
97
|
-
end
|
98
|
-
when 'google.protobuf.Value'
|
99
|
-
params[field] = Google::Protobuf::Value.from_ruby(val)
|
100
|
-
when 'google.protobuf.ListValue'
|
101
|
-
params[field] = Google::Protobuf::ListValue.from_a(val)
|
104
|
+
if descriptor.label == :repeated
|
105
|
+
params[field] = val.map { |v| map_proto_type(descriptor, v) }
|
102
106
|
else
|
103
|
-
|
104
|
-
params[field].each do |item|
|
105
|
-
map_proto_type(descriptor.subtype, item)
|
106
|
-
end
|
107
|
-
else
|
108
|
-
map_proto_type(descriptor.subtype, params[field])
|
109
|
-
end
|
107
|
+
params[field] = map_proto_type(descriptor, val)
|
110
108
|
end
|
111
109
|
end
|
110
|
+
params
|
112
111
|
end
|
113
112
|
|
114
113
|
def init_request(request_class, params)
|
115
|
-
|
114
|
+
map_proto_record(request_class.descriptor, params)
|
116
115
|
request_class.decode_json(JSON.generate(params), ignore_unknown_fields: true)
|
117
116
|
end
|
118
117
|
|
@@ -204,7 +203,18 @@ module GrpcRest
|
|
204
203
|
active_call: nil,
|
205
204
|
message: request
|
206
205
|
)
|
207
|
-
|
206
|
+
Gruf::Interceptors::Context.new(gruf_interceptors(request)).intercept! do
|
207
|
+
handler.send(method.to_sym)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# @param request [Google::Protobuf::AbstractMessage]
|
212
|
+
# @return [Array<Gruf::Interceptors::Base>]
|
213
|
+
def gruf_interceptors(request)
|
214
|
+
error = Gruf::Error.new
|
215
|
+
interceptors = Gruf.interceptors.prepare(request, error)
|
216
|
+
interceptors.delete_if { |k| k.class.name.split('::').first == 'Gruf' }
|
217
|
+
interceptors
|
208
218
|
end
|
209
219
|
|
210
220
|
def send_grpc_request(service, method, request)
|
data/spec/grpc_rest_spec.rb
CHANGED
@@ -67,6 +67,7 @@ RSpec.describe MyServiceController, type: :request do
|
|
67
67
|
let(:params) do
|
68
68
|
{
|
69
69
|
test_id: 'abc',
|
70
|
+
repeated_float: [1.0, 2.0],
|
70
71
|
some_int: "65",
|
71
72
|
foobar: 'xyz',
|
72
73
|
repeated_string: ['W', 'T', 'F'],
|
@@ -101,7 +102,7 @@ RSpec.describe MyServiceController, type: :request do
|
|
101
102
|
expect(response).to be_successful
|
102
103
|
expect(response.parsed_body).to eq({
|
103
104
|
'someInt' => 4,
|
104
|
-
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,"someInt":65,"someEnum":"TEST_ENUM_FOO"})
|
105
|
+
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,"someInt":65,"someEnum":"TEST_ENUM_FOO","repeatedFloat":[1,2]})
|
105
106
|
})
|
106
107
|
end
|
107
108
|
|
@@ -111,7 +112,7 @@ RSpec.describe MyServiceController, type: :request do
|
|
111
112
|
expect(response).to be_successful
|
112
113
|
expect(response.parsed_body).to eq({
|
113
114
|
'someInt' => 4,
|
114
|
-
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,"someInt":65,"someEnum":"TEST_ENUM_FOO"})
|
115
|
+
'fullResponse' => %({"testId":"abc","foobar":"xyz","repeatedString":["W","T","F"],"subRecord":{"subId":"id1","anotherId":"id2"},"secondRecord":{"subId":"id3","anotherId":"id4"},"structField":{"bool_key":true,"str_key":"val","nil_key":null,"list_key":[{"inner_key":"inner_val"}],"int_key":123},"timestampField":"2024-04-03T01:02:03Z","listValue":["F","Y","I"],"bareValue":45,"someInt":65,"someEnum":"TEST_ENUM_FOO","repeatedFloat":[1,2]})
|
115
116
|
})
|
116
117
|
end
|
117
118
|
end
|
data/spec/gruf_spec.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative './spec_helper'
|
2
|
+
require_relative './test_service_services_pb'
|
3
|
+
require 'gruf'
|
4
|
+
|
5
|
+
class GrufServerImpl < Gruf::Controllers::Base
|
6
|
+
|
7
|
+
bind ::Testdata::MyService::Service
|
8
|
+
|
9
|
+
def test
|
10
|
+
Testdata::TestResponse.new(some_int: 1, full_response: request.message.to_json)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_2
|
14
|
+
Testdata::TestResponse.new(some_int: 2, full_response: request.message.to_json)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_3
|
18
|
+
Testdata::TestResponse.new(some_int: 3, full_response: request.message.to_json)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_4
|
22
|
+
Testdata::TestResponse.new(some_int: 4, full_response: request.message.to_json)
|
23
|
+
end
|
24
|
+
|
25
|
+
cattr_accessor :intercepted
|
26
|
+
end
|
27
|
+
|
28
|
+
class TestInterceptor < ::Gruf::Interceptors::ServerInterceptor
|
29
|
+
def call
|
30
|
+
GrufServerImpl.intercepted = true
|
31
|
+
yield
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Gruf::Server.new.add_interceptor(TestInterceptor, option_foo: 'value 123')
|
36
|
+
|
37
|
+
RSpec.describe MyServiceController, type: :request do
|
38
|
+
describe 'using get' do
|
39
|
+
it 'should be successful and call interceptors' do
|
40
|
+
GrufServerImpl.intercepted = false
|
41
|
+
get "/test/blah/xyz?test_id=abc"
|
42
|
+
expect(response).to be_successful
|
43
|
+
expect(response.parsed_body).to eq({
|
44
|
+
'someInt' => 1,
|
45
|
+
'fullResponse' => %({"testId":"abc","foobar":"xyz"}),
|
46
|
+
"ignoredKey" => ''
|
47
|
+
})
|
48
|
+
expect(GrufServerImpl.intercepted).to eq(true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/spec/test_service_pb.rb
CHANGED
@@ -10,7 +10,7 @@ require 'google/protobuf/timestamp_pb'
|
|
10
10
|
require 'protoc-gen-openapiv2/options/annotations_pb'
|
11
11
|
|
12
12
|
|
13
|
-
descriptor_data = "\n\x12test_service.proto\x12\x08testdata\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a.protoc-gen-openapiv2/options/annotations.proto\"\
|
13
|
+
descriptor_data = "\n\x12test_service.proto\x12\x08testdata\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a.protoc-gen-openapiv2/options/annotations.proto\"\xd7\x03\n\x0bTestRequest\x12\x0f\n\x07test_id\x18\x01 \x01(\t\x12\x0e\n\x06\x66oobar\x18\x02 \x01(\t\x12\x17\n\x0frepeated_string\x18\x03 \x03(\t\x12\'\n\nsub_record\x18\x04 \x01(\x0b\x32\x13.testdata.SubRecord\x12*\n\rsecond_record\x18\x05 \x01(\x0b\x32\x13.testdata.SubRecord\x12-\n\x0cstruct_field\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x33\n\x0ftimestamp_field\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nlist_value\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.ListValue\x12*\n\nbare_value\x18\t \x01(\x0b\x32\x16.google.protobuf.Value\x12(\n\x0bsub_records\x18\n \x03(\x0b\x32\x13.testdata.SubRecord\x12\x10\n\x08some_int\x18\x0b \x01(\x05\x12%\n\tsome_enum\x18\x0c \x01(\x0e\x32\x12.testdata.TestEnum\x12\x16\n\x0erepeated_float\x18\r \x03(\x02\"/\n\tSubRecord\x12\x0e\n\x06sub_id\x18\x01 \x01(\t\x12\x12\n\nanother_id\x18\x02 \x01(\t\"L\n\x0cTestResponse\x12\x10\n\x08some_int\x18\x01 \x01(\x05\x12\x15\n\rfull_response\x18\x02 \x01(\t\x12\x13\n\x0bignored_key\x18\x03 \x01(\t*K\n\x08TestEnum\x12\x19\n\x15TEST_ENUM_UNSPECIFIED\x10\x00\x12\x11\n\rTEST_ENUM_FOO\x10\x01\x12\x11\n\rTEST_ENUM_BAR\x10\x02\x32\x83\x03\n\tMyService\x12x\n\x04Test\x12\x15.testdata.TestRequest\x1a\x16.testdata.TestResponse\"A\x92\x41!j\x1f\n\x19x-grpc-rest-emit-defaults\x12\x02 \x01\x82\xd3\xe4\x93\x02\x17\x12\x15/test/{foobar=blah/*}\x12U\n\x05Test2\x12\x15.testdata.TestRequest\x1a\x16.testdata.TestResponse\"\x1d\x82\xd3\xe4\x93\x02\x17\"\x06/test2:\rsecond_record\x12Z\n\x05Test3\x12\x15.testdata.TestRequest\x1a\x16.testdata.TestResponse\"\"\x82\xd3\xe4\x93\x02\x1c\"\x1a/test3/{sub_record.sub_id}\x12I\n\x05Test4\x12\x15.testdata.TestRequest\x1a\x16.testdata.TestResponse\"\x11\x82\xd3\xe4\x93\x02\x0b\"\x06/test4:\x01*b\x06proto3"
|
14
14
|
|
15
15
|
pool = Google::Protobuf::DescriptorPool.generated_pool
|
16
16
|
pool.add_serialized_file(descriptor_data)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grpc-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Orner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grpc
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: gruf
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description:
|
56
70
|
email:
|
57
71
|
- daniel.orner@flipp.com
|
@@ -88,6 +102,7 @@ files:
|
|
88
102
|
- protoc-gen-rails/testdata/test.proto
|
89
103
|
- protoc-gen-rails/testdata/test_service.proto
|
90
104
|
- spec/grpc_rest_spec.rb
|
105
|
+
- spec/gruf_spec.rb
|
91
106
|
- spec/protoc-gen-openapiv2/options/annotations_pb.rb
|
92
107
|
- spec/protoc-gen-openapiv2/options/openapiv2_pb.rb
|
93
108
|
- spec/spec_helper.rb
|
@@ -118,6 +133,7 @@ specification_version: 4
|
|
118
133
|
summary: Generate Rails controllers and routes from gRPC definitions.
|
119
134
|
test_files:
|
120
135
|
- spec/grpc_rest_spec.rb
|
136
|
+
- spec/gruf_spec.rb
|
121
137
|
- spec/protoc-gen-openapiv2/options/annotations_pb.rb
|
122
138
|
- spec/protoc-gen-openapiv2/options/openapiv2_pb.rb
|
123
139
|
- spec/spec_helper.rb
|