grpc-rest 0.1.19 → 0.1.21

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: 3c9a0fd0dc8a21c8ff8e5103a6bdffe98cc51cb522fb8dac59a65b2e9ceacc7c
4
- data.tar.gz: a42ae7274e7a9db0161e3a40374d050a334312c742d8737652ab8fd8aaa55ec4
3
+ metadata.gz: '090bea208182dd067a0a1f0347e9cb4484610dca9a7a4f1c2f7689fa216d40e8'
4
+ data.tar.gz: 3695746b75dcbba972e88249ad2785551d7e0e4fbd9d8a01140496dfa4f2a002
5
5
  SHA512:
6
- metadata.gz: 624a27f0b5345256376d3a3cbd418543339d9f604ef42866da442a4589fd0732702b61e02f37d14e31b1cb53f5d707d73a8924af45df9be65995942316b75d86
7
- data.tar.gz: d548c211f158b8095ec655691e11c1cb815382ecdfce8a42ed014fdcf82d5286e333d2894a8c5bdb3564b861beba29387b2936a39c6d28108e7d3d472da6ff45
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.19)
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
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_runtime_dependency('rails', '>= 6.0')
23
23
 
24
24
  spec.add_development_dependency('rspec-rails')
25
+ spec.add_development_dependency('gruf')
25
26
  end
@@ -1,3 +1,3 @@
1
1
  module GrpcRest
2
- VERSION = '0.1.19'
2
+ VERSION = '0.1.21'
3
3
  end
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(proto, params)
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
- case descriptor.subtype&.name
90
- when 'google.protobuf.Struct'
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
- if params[field].is_a?(Array)
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
- map_proto_type(request_class.descriptor, params)
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
- handler.send(method.to_sym)
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)
@@ -20,6 +20,7 @@ message TestRequest {
20
20
  repeated SubRecord sub_records = 10;
21
21
  int32 some_int = 11;
22
22
  TestEnum some_enum = 12;
23
+ repeated float repeated_float = 13;
23
24
  }
24
25
 
25
26
  message SubRecord {
@@ -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
@@ -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\"\xbf\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\"/\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"
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.19
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-08-22 00:00:00.000000000 Z
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