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 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