зеркало из https://github.com/arthurnn/twirp-ruby.git
Twirp service handlers receive the input message and also a Twirp environment that can be used to read headers from the request, environment data from before hooks and also set headers for the response
This commit is contained in:
Родитель
851adf93bc
Коммит
0286c3e057
65
README.md
65
README.md
|
@ -4,85 +4,86 @@ Twirp services and clients in Ruby.
|
|||
|
||||
### Installation
|
||||
Install the `twirp` gem:
|
||||
|
||||
```sh
|
||||
➜ gem install twirp
|
||||
```
|
||||
|
||||
Use `go get` to install the ruby_twirp protoc plugin:
|
||||
|
||||
```sh
|
||||
➜ go get github.com/cyrusaf/ruby-twirp/protoc-gen-twirp_ruby
|
||||
```
|
||||
|
||||
You will also need:
|
||||
|
||||
- [protoc](https://github.com/golang/protobuf), the protobuf compiler. You need
|
||||
version 3+.
|
||||
|
||||
### Haberdasher Example
|
||||
### HelloWorld Example
|
||||
|
||||
See the `example/` folder for the final product.
|
||||
|
||||
First create a basic `.proto` file:
|
||||
|
||||
```protobuf
|
||||
// haberdasher.proto
|
||||
syntax = "proto3";
|
||||
package example;
|
||||
|
||||
service Haberdasher {
|
||||
rpc HelloWorld(HelloWorldRequest) returns (HelloWorldResponse);
|
||||
service HelloWorld {
|
||||
rpc Hello(HelloRequest) returns (HelloResponse);
|
||||
}
|
||||
|
||||
message HelloWorldRequest {
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message HelloWorldResponse {
|
||||
message HelloResponse {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Run the `protoc` binary to generate `gen/haberdasher_pb.rb` and `gen/haberdasher_twirp.rb`.
|
||||
Run the `protoc` binary to auto-generate `helloworld_pb.rb` and `haberdasher_twirp.rb` files:
|
||||
|
||||
```sh
|
||||
➜ protoc --proto_path=. ./haberdasher.proto --ruby_out=gen --twirp_ruby_out=gen
|
||||
```
|
||||
|
||||
Write an implementation of our haberdasher service and attach to a rack server:
|
||||
Write a handler for the auto-generated service, this is your implementation:
|
||||
|
||||
```ruby
|
||||
class HellowWorldHandler
|
||||
def hello(input, env)
|
||||
{message: "Hello #{input.name}"}
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Initialize the service with your handler and mount it as a Rack app:
|
||||
|
||||
```ruby
|
||||
# main.rb
|
||||
require 'rack'
|
||||
require_relative 'gen/haberdasher_pb.rb'
|
||||
require_relative 'gen/haberdasher_twirp.rb'
|
||||
|
||||
class HaberdasherHandler
|
||||
def hello_world(req)
|
||||
return {message: "Hello #{req.name}"}
|
||||
end
|
||||
end
|
||||
|
||||
handler = HaberdasherHandler.new()
|
||||
service = Example::HaberdasherService.new(handler)
|
||||
handler = HellowWorldHandler.new()
|
||||
service = Example::HelloWorld.new(handler)
|
||||
Rack::Handler::WEBrick.run service
|
||||
```
|
||||
|
||||
You can also mount onto a rails service:
|
||||
You can also mount onto a rails app:
|
||||
|
||||
```ruby
|
||||
App::Application.routes.draw do
|
||||
handler = HaberdasherHandler.new()
|
||||
service = Example::HaberdasherService.new(handler)
|
||||
mount service, at: HaberdasherService::PATH_PREFIX
|
||||
mount service, at: service.path_prefix
|
||||
end
|
||||
```
|
||||
|
||||
Run `ruby main.rb` to start the server on port 8080:
|
||||
```sh
|
||||
➜ ruby main.rb
|
||||
```
|
||||
Twirp services accept both Protobuf and JSON messages. It is easy to `curl` your service to get a response:
|
||||
|
||||
`curl` your server to get a response:
|
||||
```sh
|
||||
➜ curl --request POST \
|
||||
--url http://localhost:8080/twirp/examples.Haberdasher/HelloWorld \
|
||||
curl --request POST \
|
||||
--url http://localhost:8080/twirp/example.HelloWorld/Hello \
|
||||
--header 'content-type: application/json' \
|
||||
--data '{
|
||||
"name": "World"
|
||||
}'
|
||||
--data '{"name":"World"}'
|
||||
```
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require_relative 'twirp/version'
|
||||
require_relative 'twirp/error'
|
||||
require_relative 'twirp/exception'
|
||||
require_relative 'twirp/environment'
|
||||
require_relative 'twirp/service'
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
module Twirp
|
||||
|
||||
class Environment
|
||||
|
||||
def initialize(rack_request)
|
||||
@rack_request = rack_request
|
||||
@response_http_headers = {}
|
||||
@data = {}
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@data[key]
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@data[key] = value
|
||||
end
|
||||
|
||||
def get_http_request_header(header)
|
||||
@rack_request.get_header(header)
|
||||
end
|
||||
|
||||
def set_http_response_header(header, value)
|
||||
@response_http_headers[header] = value
|
||||
end
|
||||
|
||||
# Accessing the raw Rack::Request is convenient, but it is
|
||||
# discouraged because it adds extra dependencies to your handler.
|
||||
# Instead of directly accessing the rack_request, it is better to
|
||||
# add a before hook in the service that reads data from the Rack environment
|
||||
# and adds it to the Twirp environment, so all dependencies are clear. Example:
|
||||
# svc.before do |rpc, input, env|
|
||||
# env[:user] = env.rack_request.env['warden'].user
|
||||
# end
|
||||
#
|
||||
def rack_request
|
||||
@rack_request
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -26,7 +26,7 @@ module Twirp
|
|||
rpc_method: rpc_method.to_s,
|
||||
input_class: input_class,
|
||||
output_class: output_class,
|
||||
handler_method: opts[:handler_method].to_s,
|
||||
handler_method: opts[:handler_method].to_sym,
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -68,38 +68,43 @@ module Twirp
|
|||
# Instantiate a new service with a handler.
|
||||
# The handler must implemnt all rpc methods required by this service.
|
||||
def initialize(handler)
|
||||
self.class.rpcs.each do |method_name, rpc|
|
||||
if !handler.respond_to? rpc[:handler_method]
|
||||
raise ArgumentError.new("Handler must respond to .#{rpc[:handler_method]}(input) in order to handle the message #{method_name}.")
|
||||
@handler = handler
|
||||
self.class.rpcs.each do |rpc_method, rpc|
|
||||
m = rpc[:handler_method]
|
||||
if !handler.respond_to?(m)
|
||||
raise ArgumentError.new("Handler must respond to .#{m}(input) in order to handle the rpc method #{rpc_method.inspect}.")
|
||||
end
|
||||
if handler.method(m).arity != 2
|
||||
raise ArgumentError.new("Hanler method #{m} must accept exactly 2 arguments (input, env).")
|
||||
end
|
||||
end
|
||||
@handler = handler
|
||||
end
|
||||
|
||||
# Setup a before hook on this service.
|
||||
# Before hooks are called after the request has been successfully routed to a method.
|
||||
# If multiple hooks are added, they are run in the same order as declared.
|
||||
# The hook is a block that accepts 3 parameters:
|
||||
# * rpc_method: rpc method as defined in the proto file.
|
||||
# The hook is a block that accepts 3 parameters: (rpc, input, request)
|
||||
# * rpc: rpc data for the current method with info like rpc[:rpc_method] and rpc[:input_class].
|
||||
# * input: Protobuf message object that will be passed to the handler method.
|
||||
# * request: the raw Rack::Request
|
||||
# * env: the Twirp environment object that will be passed to the handler method.
|
||||
#
|
||||
# If the before hook returns a Twirp::Error then the request is inmediatly
|
||||
# canceled, the handler method is not called, and that error is returned instead.
|
||||
# Any other return value from the hook is ignored (nil or otherwise).
|
||||
# If an excetion is raised from the hook, it will be handled just like exceptions
|
||||
# raised from handler methods: they will trigger the error hook and wrapped with a Twirp::Error.
|
||||
# If an excetion is raised from the hook the request is also canceled,
|
||||
# and the exception is handled with the error hook (just like exceptions raised from methods).
|
||||
#
|
||||
# Usage Example:
|
||||
#
|
||||
# handler = ExampleHandler.new
|
||||
# svc = ExampleService.new(handler)
|
||||
#
|
||||
# svc.before do |rpc_method, input, request|
|
||||
# if request.get_header "Force-Error"
|
||||
# svc.before do |rpc, input, env|
|
||||
# if env.get_http_request_header "Force-Error"
|
||||
# return Twirp.canceled_error("failed as recuested sir")
|
||||
# end
|
||||
# request.env["example_service.before_succeed"] = true # can be later accessed on the handler method
|
||||
# env[:before_hook_called] = true # can be later accessed on the handler method
|
||||
# env[:easy_access] = env.rack_request.env["rack.data"] # before hooks can be used to read data from the request
|
||||
# end
|
||||
#
|
||||
# svc.before handler.method(:before) # you can also delegate the hook to the handler (to reuse helpers, etc)
|
||||
|
@ -115,20 +120,21 @@ module Twirp
|
|||
end
|
||||
|
||||
# Rack app handler.
|
||||
def call(env)
|
||||
request = Rack::Request.new(env)
|
||||
rpc, content_type, bad_route = route_request(request)
|
||||
def call(rack_env)
|
||||
rack_request = Rack::Request.new(rack_env)
|
||||
rpc, content_type, bad_route = route_request(rack_request)
|
||||
if bad_route
|
||||
return error_response(bad_route)
|
||||
end
|
||||
input = decode_request(rpc[:input_class], content_type, request.body.read)
|
||||
input = decode_request(rpc, content_type, rack_request.body.read)
|
||||
env = Twirp::Environment.new(rack_request)
|
||||
|
||||
begin
|
||||
if twerr = run_before_hooks(rpc[:rpc_method], input, request)
|
||||
if twerr = run_before_hooks(rpc, input, env)
|
||||
error_response(twerr)
|
||||
end
|
||||
|
||||
handler_output = @handler.send(rpc[:handler_method], input)
|
||||
handler_output = @handler.send(rpc[:handler_method], input, env)
|
||||
if handler_output.is_a? Twirp::Error
|
||||
return error_response(handler_output)
|
||||
end
|
||||
|
@ -176,43 +182,45 @@ module Twirp
|
|||
return rpc, content_type, nil
|
||||
end
|
||||
|
||||
def decode_request(input_class, content_type, body)
|
||||
def decode_request(rpc, content_type, body)
|
||||
case content_type
|
||||
when "application/json"
|
||||
input_class.decode_json(body)
|
||||
rpc[:input_class].decode_json(body)
|
||||
when "application/protobuf"
|
||||
input_class.decode(body)
|
||||
rpc[:input_class].decode(body)
|
||||
end
|
||||
end
|
||||
|
||||
# Before hooks are run in order after the request has been successfully routed to a Method.
|
||||
def run_before_hooks(rpc_method, input, request)
|
||||
def run_before_hooks(rpc, input, env)
|
||||
return unless @before_hooks
|
||||
@before_hooks.each do |hook|
|
||||
twerr = hook.call(rpc_method, input, request)
|
||||
twerr = hook.call(rpc, input, env)
|
||||
return twerr if twerr && twerr.is_a?(Twirp::Error)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def encode_response_from_handler(rpc, content_type, resp)
|
||||
proto_class = rpc[:output_class]
|
||||
def encode_response_from_handler(rpc, content_type, output)
|
||||
output_class = rpc[:output_class]
|
||||
|
||||
# Handlers may return just the attributes
|
||||
if resp.is_a? Hash
|
||||
resp = proto_class.new(resp)
|
||||
if output.is_a? Hash
|
||||
output = output_class.new(output)
|
||||
end
|
||||
|
||||
# Handlers may return nil, that should be converted to zero-values
|
||||
if !resp
|
||||
resp = proto_class.new
|
||||
if output == nil
|
||||
output = output_class.new # empty output with zero-values
|
||||
end
|
||||
|
||||
if !output.is_a? output_class # validate return value
|
||||
raise TypeError.new("Return value from .#{rpc[:handler_method]} expected to be an #{output_class.name} or Hash, but it is #{resp.class.name}")
|
||||
end
|
||||
|
||||
case content_type
|
||||
when "application/json"
|
||||
proto_class.encode_json(resp)
|
||||
output_class.encode_json(output)
|
||||
when "application/protobuf"
|
||||
proto_class.encode(resp)
|
||||
output_class.encode(output)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -41,8 +41,8 @@ class HaberdasherHandler
|
|||
@block = block if block_given?
|
||||
end
|
||||
|
||||
def make_hat(size)
|
||||
@block && @block.call(size)
|
||||
def make_hat(input, env)
|
||||
@block && @block.call(input, env)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class ServiceTest < Minitest::Test
|
|||
rpc_method: "MakeHat",
|
||||
input_class: Example::Size,
|
||||
output_class: Example::Hat,
|
||||
handler_method: "make_hat",
|
||||
handler_method: :make_hat,
|
||||
}, Example::Haberdasher.rpcs["MakeHat"])
|
||||
end
|
||||
|
||||
|
@ -54,12 +54,12 @@ class ServiceTest < Minitest::Test
|
|||
err = assert_raises ArgumentError do
|
||||
Example::Haberdasher.new("fake handler")
|
||||
end
|
||||
assert_equal "Handler must respond to .make_hat(input) in order to handle the message MakeHat.", err.message
|
||||
assert_equal 'Handler must respond to .make_hat(input) in order to handle the rpc method "MakeHat".', err.message
|
||||
end
|
||||
|
||||
def test_successful_json_request
|
||||
env = json_req "/twirp/example.Haberdasher/MakeHat", inches: 10
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
rack_env = json_req "/twirp/example.Haberdasher/MakeHat", inches: 10
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 200, status
|
||||
assert_equal 'application/json', headers['Content-Type']
|
||||
|
@ -67,8 +67,8 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_successful_proto_request
|
||||
env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new(inches: 10)
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
rack_env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new(inches: 10)
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 200, status
|
||||
assert_equal 'application/protobuf', headers['Content-Type']
|
||||
|
@ -76,8 +76,8 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_bad_route_with_wrong_rpc_method
|
||||
env = json_req "/twirp/example.Haberdasher/MakeUnicorns", and_rainbows: true
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
rack_env = json_req "/twirp/example.Haberdasher/MakeUnicorns", and_rainbows: true
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 404, status
|
||||
assert_equal 'application/json', headers['Content-Type']
|
||||
|
@ -89,9 +89,9 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_bad_route_with_wrong_http_method
|
||||
env = Rack::MockRequest.env_for "/twirp/example.Haberdasher/MakeHat",
|
||||
rack_env = Rack::MockRequest.env_for "/twirp/example.Haberdasher/MakeHat",
|
||||
method: "GET", input: '{"inches": 10}', "CONTENT_TYPE" => "application/json"
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 404, status
|
||||
assert_equal 'application/json', headers['Content-Type']
|
||||
|
@ -103,9 +103,9 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_bad_route_with_wrong_content_type
|
||||
env = Rack::MockRequest.env_for "/twirp/example.Haberdasher/MakeHat",
|
||||
rack_env = Rack::MockRequest.env_for "/twirp/example.Haberdasher/MakeHat",
|
||||
method: "POST", input: 'free text', "CONTENT_TYPE" => "text/plain"
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 404, status
|
||||
assert_equal 'application/json', headers['Content-Type']
|
||||
|
@ -117,8 +117,8 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_bad_route_with_wrong_path_json
|
||||
env = json_req "/wrongpath", {}
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
rack_env = json_req "/wrongpath", {}
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 404, status
|
||||
assert_equal 'application/json', headers['Content-Type']
|
||||
|
@ -130,8 +130,8 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_bad_route_with_wrong_path_protobuf
|
||||
env = proto_req "/another/wrong.Path/MakeHat", Example::Empty.new()
|
||||
status, headers, body = haberdasher_service.call(env)
|
||||
rack_env = proto_req "/another/wrong.Path/MakeHat", Example::Empty.new()
|
||||
status, headers, body = haberdasher_service.call(rack_env)
|
||||
|
||||
assert_equal 404, status
|
||||
assert_equal 'application/json', headers['Content-Type'] # error responses are always JSON, even for Protobuf requests
|
||||
|
@ -148,8 +148,8 @@ class ServiceTest < Minitest::Test
|
|||
Example::Hat.new(inches: 11)
|
||||
end)
|
||||
|
||||
env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new
|
||||
status, headers, body = svc.call(env)
|
||||
rack_env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new
|
||||
status, headers, body = svc.call(rack_env)
|
||||
|
||||
assert_equal 200, status
|
||||
assert_equal Example::Hat.new(inches: 11, color: ""), Example::Hat.decode(body[0])
|
||||
|
@ -161,8 +161,8 @@ class ServiceTest < Minitest::Test
|
|||
{inches: 11}
|
||||
end)
|
||||
|
||||
env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new
|
||||
status, headers, body = svc.call(env)
|
||||
rack_env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new
|
||||
status, headers, body = svc.call(rack_env)
|
||||
|
||||
assert_equal 200, status
|
||||
assert_equal Example::Hat.new(inches: 11, color: ""), Example::Hat.decode(body[0])
|
||||
|
@ -174,8 +174,8 @@ class ServiceTest < Minitest::Test
|
|||
nil
|
||||
end)
|
||||
|
||||
env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new
|
||||
status, headers, body = svc.call(env)
|
||||
rack_env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new
|
||||
status, headers, body = svc.call(rack_env)
|
||||
|
||||
assert_equal 200, status
|
||||
assert_equal Example::Hat.new(inches: 0, color: ""), Example::Hat.decode(body[0])
|
||||
|
@ -187,8 +187,8 @@ class ServiceTest < Minitest::Test
|
|||
return Twirp.invalid_argument_error "I don't like that size"
|
||||
end)
|
||||
|
||||
env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new(inches: 666)
|
||||
status, headers, body = svc.call(env)
|
||||
rack_env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new(inches: 666)
|
||||
status, headers, body = svc.call(rack_env)
|
||||
assert_equal 400, status
|
||||
assert_equal 'application/json', headers['Content-Type'] # error responses are always JSON, even for Protobuf requests
|
||||
assert_equal({
|
||||
|
@ -203,8 +203,8 @@ class ServiceTest < Minitest::Test
|
|||
raise Twirp::Exception.new(:invalid_argument, "I don't like that size")
|
||||
end)
|
||||
|
||||
env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new(inches: 666)
|
||||
status, headers, body = svc.call(env)
|
||||
rack_env = proto_req "/twirp/example.Haberdasher/MakeHat", Example::Size.new(inches: 666)
|
||||
status, headers, body = svc.call(rack_env)
|
||||
assert_equal 400, status
|
||||
assert_equal 'application/json', headers['Content-Type'] # error responses are always JSON, even for Protobuf requests
|
||||
assert_equal({
|
||||
|
@ -213,12 +213,25 @@ class ServiceTest < Minitest::Test
|
|||
}, JSON.parse(body[0]))
|
||||
end
|
||||
|
||||
# TODO: Error handler
|
||||
# def test_handler_raises_standard_error
|
||||
# svc = Example::Haberdasher.new(HaberdasherHandler.new do |size|
|
||||
# raise "random error"
|
||||
# end)
|
||||
# end
|
||||
def test_handler_method_can_access_request_headers_through_the_env
|
||||
svc = Example::Haberdasher.new(HaberdasherHandler.new do |size, env|
|
||||
inches = env.get_http_request_header("INCHES_FROM_HEADER")
|
||||
{inches: inches.to_i}
|
||||
end)
|
||||
|
||||
rack_env = Rack::MockRequest.env_for "/twirp/example.Haberdasher/MakeHat", method: "POST",
|
||||
input: '{"inches": 666}', # value should be ignored
|
||||
"CONTENT_TYPE" => "application/json",
|
||||
"INCHES_FROM_HEADER" => "7" # this value should be used
|
||||
status, headers, body = svc.call(rack_env)
|
||||
|
||||
assert_equal 200, status
|
||||
assert_equal({"inches" => 7}, JSON.parse(body[0]))
|
||||
end
|
||||
|
||||
# TODO: test_handler_method_can_set_response_headers_through_the_env
|
||||
|
||||
# TODO: test_handler_raises_standard_error
|
||||
|
||||
def test_before_hook_simple
|
||||
handler_method_called = false
|
||||
|
@ -229,22 +242,27 @@ class ServiceTest < Minitest::Test
|
|||
|
||||
called_with = nil
|
||||
svc = Example::Haberdasher.new(handler)
|
||||
svc.before do |rpc_method, input, request|
|
||||
called_with = {rpc_method: rpc_method, input: input, request: request}
|
||||
svc.before do |rpc, input, env|
|
||||
env[:contet_type] = env.rack_request.get_header("CONTENT_TYPE")
|
||||
called_with = {rpc: rpc, input: input, env: env}
|
||||
end
|
||||
|
||||
env = json_req "/twirp/example.Haberdasher/MakeHat", inches: 10
|
||||
status, _, _ = svc.call(env)
|
||||
rack_env = json_req "/twirp/example.Haberdasher/MakeHat", inches: 10
|
||||
status, _, _ = svc.call(rack_env)
|
||||
|
||||
refute_nil called_with, "the before hook was called"
|
||||
assert_equal "MakeHat", called_with[:rpc_method]
|
||||
assert_equal "MakeHat", called_with[:rpc][:rpc_method]
|
||||
assert_equal Example::Size.new(inches: 10), called_with[:input]
|
||||
assert_equal "application/json", called_with[:request].get_header('CONTENT_TYPE') # the request is accessible
|
||||
assert_equal "application/json", called_with[:env][:contet_type]
|
||||
|
||||
assert handler_method_called, "the handler method was called"
|
||||
assert_equal 200, status, "response is successful"
|
||||
end
|
||||
|
||||
# TODO: test_before_hook_add_data_in_env_for_the_handler_method
|
||||
# TODO: test_before_hook_returning_twirp_error_cancels_request
|
||||
# TODO: test_before_hook_raising_exception_cancels_request
|
||||
|
||||
|
||||
|
||||
# Test Helpers
|
||||
|
@ -263,7 +281,7 @@ class ServiceTest < Minitest::Test
|
|||
end
|
||||
|
||||
def haberdasher_service
|
||||
Example::Haberdasher.new(HaberdasherHandler.new do |size|
|
||||
Example::Haberdasher.new(HaberdasherHandler.new do |size, _|
|
||||
{inches: size.inches, color: "white"}
|
||||
end)
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче