tonic-rails 0.1.0 → 0.3.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -2
  3. data/Cargo.lock +5 -5
  4. data/README.md +204 -13
  5. data/ext/tonic_rails_native/Cargo.toml +2 -2
  6. data/ext/tonic_rails_native/src/body.rs +113 -49
  7. data/ext/tonic_rails_native/src/dispatch.rs +67 -8
  8. data/ext/tonic_rails_native/src/framing.rs +50 -0
  9. data/ext/tonic_rails_native/src/gvl.rs +109 -21
  10. data/ext/tonic_rails_native/src/lib.rs +6 -1
  11. data/ext/tonic_rails_native/src/log.rs +131 -0
  12. data/ext/tonic_rails_native/src/net.rs +7 -1
  13. data/ext/tonic_rails_native/src/server.rs +67 -6
  14. data/lib/generators/tonic_rails/install/install_generator.rb +45 -0
  15. data/lib/generators/tonic_rails/install/templates/tonic_rails.rb.tt +21 -0
  16. data/lib/generators/tonic_rails/service/service_generator.rb +110 -0
  17. data/lib/generators/tonic_rails/service/templates/handler.rb.tt +38 -0
  18. data/lib/generators/tonic_rails/service/templates/service.proto.tt +16 -0
  19. data/lib/generators/tonic_rails/service/templates/service_spec.rb.tt +17 -0
  20. data/lib/generators/tonic_rails/service/templates/service_test.rb.tt +14 -0
  21. data/lib/puma/plugin/tonic_rails.rb +116 -0
  22. data/lib/tonic/rails/base.rb +169 -23
  23. data/lib/tonic/rails/configuration.rb +27 -4
  24. data/lib/tonic/rails/health.rb +105 -0
  25. data/lib/tonic/rails/instrumentation.rb +78 -0
  26. data/lib/tonic/rails/metadata.rb +50 -0
  27. data/lib/tonic/rails/peer.rb +60 -0
  28. data/lib/tonic/rails/railtie.rb +2 -2
  29. data/lib/tonic/rails/router.rb +1 -0
  30. data/lib/tonic/rails/rpc_error.rb +73 -6
  31. data/lib/tonic/rails/server.rb +82 -12
  32. data/lib/tonic/rails/tasks/tonic-rails.rake +13 -1
  33. data/lib/tonic/rails/testing/errors.rb +47 -0
  34. data/lib/tonic/rails/testing/minitest.rb +44 -0
  35. data/lib/tonic/rails/testing/rspec.rb +53 -0
  36. data/lib/tonic/rails/testing.rb +35 -4
  37. data/lib/tonic/rails/version.rb +1 -1
  38. data/lib/tonic-rails.rb +4 -0
  39. metadata +17 -1
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tonic/rails/testing"
4
+ require "tonic/rails/testing/errors"
5
+
6
+ module Tonic
7
+ module Rails
8
+ module Testing
9
+ # Minitest assertions for gRPC statuses:
10
+ #
11
+ # class ItemServiceTest < ActiveSupport::TestCase
12
+ # include Tonic::Rails::Testing::Assertions
13
+ #
14
+ # test "missing items" do
15
+ # error = assert_grpc_error(:not_found) { Tonic::Rails::Testing.call(ItemService, :GetItem, { id: 0 }) }
16
+ # assert_match(/0/, error.message)
17
+ # end
18
+ # end
19
+ module Assertions
20
+ # Asserts the block raises a gRPC status (Tonic::Rails::RpcError or
21
+ # GRPC::BadStatus) with the given code and, optionally, message
22
+ # (String or Regexp). Returns the error.
23
+ def assert_grpc_error(code = nil, message = nil, &)
24
+ errors = Testing::Errors
25
+ error = assert_raises(*errors.classes, &)
26
+ if code
27
+ expected = Tonic::Rails::RpcError.normalize_code(code)
28
+ assert_equal(
29
+ errors.code_name(expected), errors.code_name(errors.code(error)),
30
+ "Expected gRPC status #{errors.code_name(expected).to_s.upcase}, got #{errors.describe(error)}"
31
+ )
32
+ end
33
+ if message
34
+ assert(
35
+ errors.message_matches?(message, errors.message(error)),
36
+ "Expected gRPC message #{message.inspect}, got #{errors.message(error).inspect}",
37
+ )
38
+ end
39
+ error
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/expectations"
4
+ require "tonic/rails/testing"
5
+ require "tonic/rails/testing/errors"
6
+
7
+ # Matches a block that ends with a gRPC status, raised as a
8
+ # Tonic::Rails::RpcError (Testing.call) or a GRPC::BadStatus (the official
9
+ # client):
10
+ #
11
+ # expect { Tonic::Rails::Testing.call(ItemService, :GetItem, { id: 0 }) }
12
+ # .to raise_grpc_error(:not_found)
13
+ # expect { stub.get_item(request) }.to raise_grpc_error(:invalid_argument).with_message(/name/)
14
+ # expect { ... }.to raise_grpc_error # any non-OK status
15
+ RSpec::Matchers.define :raise_grpc_error do |expected_code = nil|
16
+ errors = Tonic::Rails::Testing::Errors
17
+
18
+ supports_block_expectations
19
+
20
+ chain(:with_message) { |message| @expected_message = message }
21
+
22
+ match do |block|
23
+ @expected = expected_code && Tonic::Rails::RpcError.normalize_code(expected_code)
24
+ begin
25
+ block.call
26
+ @error = nil
27
+ rescue Exception => e # rubocop:disable Lint/RescueException
28
+ raise unless errors.status?(e)
29
+
30
+ @error = e
31
+ end
32
+ return false unless @error
33
+
34
+ (@expected.nil? || errors.code(@error) == @expected) &&
35
+ (!defined?(@expected_message) || errors.message_matches?(@expected_message, errors.message(@error)))
36
+ end
37
+
38
+ description do
39
+ text = +"raise a gRPC error"
40
+ text << " #{errors.code_name(@expected).to_s.upcase}" if @expected
41
+ text << " with message #{@expected_message.inspect}" if defined?(@expected_message)
42
+ text
43
+ end
44
+
45
+ failure_message do
46
+ actual = @error ? "got #{errors.describe(@error)}" : "nothing was raised"
47
+ "expected block to #{description}, but #{actual}"
48
+ end
49
+
50
+ failure_message_when_negated do
51
+ "expected block not to #{description}, but got #{errors.describe(@error)}"
52
+ end
53
+ end
@@ -28,19 +28,35 @@ module Tonic
28
28
  attr_reader :error
29
29
  end
30
30
 
31
- # Response side of a call: collects encoded messages.
31
+ # Response side of a call: collects encoded messages and metadata.
32
32
  class Sender
33
- attr_reader :messages
33
+ attr_reader :messages, :headers, :trailers
34
34
 
35
35
  def initialize
36
36
  @messages = []
37
+ @headers = {}
38
+ @trailers = {}
37
39
  end
38
40
 
39
41
  def push(bytes)
40
42
  @messages << bytes
41
43
  end
44
+
45
+ def send_headers(headers)
46
+ raise "response headers must be sent before any response message" unless @messages.empty?
47
+
48
+ @headers.merge!(headers)
49
+ end
50
+
51
+ def add_trailers(trailers)
52
+ @trailers.merge!(trailers)
53
+ end
42
54
  end
43
55
 
56
+ # What a call produced: the response (or responses, for streaming),
57
+ # plus the response headers and trailers as they'd go on the wire.
58
+ Result = Struct.new(:response, :headers, :trailers)
59
+
44
60
  module_function
45
61
 
46
62
  # @param service_class [Class] handler class
@@ -49,10 +65,23 @@ module Tonic
49
65
  # request; an Array of them for client- and bidi-streaming RPCs
50
66
  # @param metadata [Hash{String=>String}]
51
67
  # @param timeout_ms [Integer, nil] simulated client deadline
68
+ # @param peer [Tonic::Rails::Peer, nil] simulated client (address, certificates)
52
69
  # @return [Google::Protobuf::AbstractMessage, Array] the response, or
53
70
  # an Array of responses for server- and bidi-streaming RPCs
54
71
  # @raise [RpcError] when the handler ends the call with a status
55
- def call(service_class, rpc_name, request, metadata: {}, timeout_ms: nil)
72
+ def call(service_class, rpc_name, request, metadata: {}, timeout_ms: nil, peer: nil)
73
+ invoke(service_class, rpc_name, request, metadata: metadata, timeout_ms: timeout_ms, peer: peer).response
74
+ end
75
+
76
+ # Like #call, but returns a Result with the response metadata too:
77
+ #
78
+ # result = Tonic::Rails::Testing.invoke(ItemService, :GetItem, { id: 1 })
79
+ # result.response.name # => "Widget"
80
+ # result.headers["x-cache"] # => "hit"
81
+ # result.trailers["x-cost-ms"] # => "3"
82
+ #
83
+ # @return [Result]
84
+ def invoke(service_class, rpc_name, request, metadata: {}, timeout_ms: nil, peer: nil)
56
85
  spec = service_class.grpc_rpcs.fetch(rpc_name.to_s) do
57
86
  raise ArgumentError, "#{service_class} has no RPC #{rpc_name}"
58
87
  end
@@ -67,13 +96,15 @@ module Tonic
67
96
  reader: reader,
68
97
  sender: sender,
69
98
  timeout_ms: timeout_ms,
99
+ peer: peer,
70
100
  )
71
101
 
72
- if spec[:response_stream]
102
+ response = if spec[:response_stream]
73
103
  sender.messages.map { |bytes| spec[:response].decode(bytes) }
74
104
  else
75
105
  spec[:response].decode(result)
76
106
  end
107
+ Result.new(response, sender.headers, sender.trailers)
77
108
  end
78
109
 
79
110
  def encode(klass, message)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Tonic
4
4
  module Rails
5
- VERSION = "0.1.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
data/lib/tonic-rails.rb CHANGED
@@ -10,10 +10,14 @@ module Tonic
10
10
  end
11
11
 
12
12
  require "tonic/rails/version"
13
+ require "tonic/rails/metadata"
14
+ require "tonic/rails/peer"
13
15
  require "tonic/rails/rpc_error"
14
16
  require "tonic/rails/configuration"
15
17
  require "tonic/rails/router"
18
+ require "tonic/rails/instrumentation"
16
19
  require "tonic/rails/base"
20
+ require "tonic/rails/health"
17
21
  require "tonic/rails/reflection"
18
22
  require "tonic/rails/server"
19
23
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tonic-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Tanous
@@ -77,11 +77,24 @@ files:
77
77
  - ext/tonic_rails_native/src/framing.rs
78
78
  - ext/tonic_rails_native/src/gvl.rs
79
79
  - ext/tonic_rails_native/src/lib.rs
80
+ - ext/tonic_rails_native/src/log.rs
80
81
  - ext/tonic_rails_native/src/net.rs
81
82
  - ext/tonic_rails_native/src/server.rs
83
+ - lib/generators/tonic_rails/install/install_generator.rb
84
+ - lib/generators/tonic_rails/install/templates/tonic_rails.rb.tt
85
+ - lib/generators/tonic_rails/service/service_generator.rb
86
+ - lib/generators/tonic_rails/service/templates/handler.rb.tt
87
+ - lib/generators/tonic_rails/service/templates/service.proto.tt
88
+ - lib/generators/tonic_rails/service/templates/service_spec.rb.tt
89
+ - lib/generators/tonic_rails/service/templates/service_test.rb.tt
90
+ - lib/puma/plugin/tonic_rails.rb
82
91
  - lib/tonic-rails.rb
83
92
  - lib/tonic/rails/base.rb
84
93
  - lib/tonic/rails/configuration.rb
94
+ - lib/tonic/rails/health.rb
95
+ - lib/tonic/rails/instrumentation.rb
96
+ - lib/tonic/rails/metadata.rb
97
+ - lib/tonic/rails/peer.rb
85
98
  - lib/tonic/rails/railtie.rb
86
99
  - lib/tonic/rails/reflection.rb
87
100
  - lib/tonic/rails/router.rb
@@ -89,6 +102,9 @@ files:
89
102
  - lib/tonic/rails/server.rb
90
103
  - lib/tonic/rails/tasks/tonic-rails.rake
91
104
  - lib/tonic/rails/testing.rb
105
+ - lib/tonic/rails/testing/errors.rb
106
+ - lib/tonic/rails/testing/minitest.rb
107
+ - lib/tonic/rails/testing/rspec.rb
92
108
  - lib/tonic/rails/version.rb
93
109
  homepage: https://github.com/CodingAnarchy/tonic-rails
94
110
  licenses: