aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/ruby/lib/grpc/errors.rb156
-rw-r--r--src/ruby/lib/grpc/generic/active_call.rb3
-rw-r--r--src/ruby/lib/grpc/generic/service.rb3
-rw-r--r--src/ruby/pb/grpc/health/checker.rb4
-rwxr-xr-xsrc/ruby/pb/test/client.rb7
-rw-r--r--src/ruby/spec/error_sanity_spec.rb64
-rw-r--r--src/ruby/spec/generic/client_stub_spec.rb9
-rw-r--r--src/ruby/spec/generic/rpc_server_spec.rb5
-rw-r--r--src/ruby/spec/pb/health/checker_spec.rb8
9 files changed, 237 insertions, 22 deletions
diff --git a/src/ruby/lib/grpc/errors.rb b/src/ruby/lib/grpc/errors.rb
index 23b2bb7e12..f6998e17c4 100644
--- a/src/ruby/lib/grpc/errors.rb
+++ b/src/ruby/lib/grpc/errors.rb
@@ -35,9 +35,18 @@ module GRPC
# either end of a GRPC connection. When raised, it indicates that a status
# error should be returned to the other end of a GRPC connection; when
# caught it means that this end received a status error.
+ #
+ # There is also subclass of BadStatus in this module for each GRPC status.
+ # E.g., the GRPC::Cancelled class corresponds to status CANCELLED.
+ #
+ # See
+ # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h
+ # for detailed descriptions of each status code.
class BadStatus < StandardError
attr_reader :code, :details, :metadata
+ include GRPC::Core::StatusCodes
+
# @param code [Numeric] the status code
# @param details [String] the details of the exception
# @param metadata [Hash] the error's metadata
@@ -55,9 +64,152 @@ module GRPC
def to_status
Struct::Status.new(code, details, @metadata)
end
+
+ def self.new_status_exception(code, details = 'unkown cause', metadata = {})
+ codes = {}
+ codes[OK] = Ok
+ codes[CANCELLED] = Cancelled
+ codes[UNKNOWN] = Unknown
+ codes[INVALID_ARGUMENT] = InvalidArgument
+ codes[DEADLINE_EXCEEDED] = DeadlineExceeded
+ codes[NOT_FOUND] = NotFound
+ codes[ALREADY_EXISTS] = AlreadyExists
+ codes[PERMISSION_DENIED] = PermissionDenied
+ codes[UNAUTHENTICATED] = Unauthenticated
+ codes[RESOURCE_EXHAUSTED] = ResourceExhausted
+ codes[FAILED_PRECONDITION] = FailedPrecondition
+ codes[ABORTED] = Aborted
+ codes[OUT_OF_RANGE] = OutOfRange
+ codes[UNIMPLEMENTED] = Unimplemented
+ codes[INTERNAL] = Internal
+ codes[UNIMPLEMENTED] = Unimplemented
+ codes[UNAVAILABLE] = Unavailable
+ codes[DATA_LOSS] = DataLoss
+
+ if codes[code].nil?
+ BadStatus.new(code, details, metadata)
+ else
+ codes[code].new(details, metadata)
+ end
+ end
+ end
+
+ # GRPC status code corresponding to status OK
+ class Ok < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::OK, details, metadata)
+ end
end
- # Cancelled is an exception class that indicates that an rpc was cancelled.
- class Cancelled < StandardError
+ # GRPC status code corresponding to status CANCELLED
+ class Cancelled < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::CANCELLED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status UNKNOWN
+ class Unknown < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::UNKNOWN, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status INVALID_ARGUMENT
+ class InvalidArgument < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::INVALID_ARGUMENT, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status DEADLINE_EXCEEDED
+ class DeadlineExceeded < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::DEADLINE_EXCEEDED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status NOT_FOUND
+ class NotFound < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::NOT_FOUND, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status ALREADY_EXISTS
+ class AlreadyExists < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::ALREADY_EXISTS, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status PERMISSION_DENIED
+ class PermissionDenied < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::PERMISSION_DENIED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status UNAUTHENTICATED
+ class Unauthenticated < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::UNAUTHENTICATED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status RESOURCE_EXHAUSTED
+ class ResourceExhausted < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::RESOURCE_EXHAUSTED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status FAILED_PRECONDITION
+ class FailedPrecondition < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::FAILED_PRECONDITION, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status ABORTED
+ class Aborted < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::ABORTED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status OUT_OF_RANGE
+ class OutOfRange < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::OUT_OF_RANGE, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status UNIMPLEMENTED
+ class Unimplemented < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::UNIMPLEMENTED, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status INTERNAL
+ class Internal < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::INTERNAL, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status UNAVAILABLE
+ class Unavailable < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::UNAVAILABLE, details, metadata)
+ end
+ end
+
+ # GRPC status code corresponding to status DATA_LOSS
+ class DataLoss < BadStatus
+ def initialize(details = 'unknown cause', metadata = {})
+ super(Core::StatusCodes::DATA_LOSS, details, metadata)
+ end
end
end
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index 5787f53463..01797d13e1 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -43,7 +43,8 @@ class Struct
GRPC.logger.debug("Failing with status #{status}")
# raise BadStatus, propagating the metadata if present.
md = status.metadata
- fail GRPC::BadStatus.new(status.code, status.details, md)
+ fail GRPC::BadStatus.new_status_exception(
+ status.code, status.details, md)
end
status
end
diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb
index 7cb9f1cc99..0dbadcc19e 100644
--- a/src/ruby/lib/grpc/generic/service.rb
+++ b/src/ruby/lib/grpc/generic/service.rb
@@ -111,7 +111,8 @@ module GRPC
marshal_class_method,
unmarshal_class_method)
define_method(name) do
- fail GRPC::BadStatus, GRPC::Core::StatusCodes::UNIMPLEMENTED
+ fail GRPC::BadStatus.new_status_exception(
+ GRPC::Core::StatusCodes::UNIMPLEMENTED)
end
end
diff --git a/src/ruby/pb/grpc/health/checker.rb b/src/ruby/pb/grpc/health/checker.rb
index 4bce1744c4..6b2d852ebf 100644
--- a/src/ruby/pb/grpc/health/checker.rb
+++ b/src/ruby/pb/grpc/health/checker.rb
@@ -52,7 +52,9 @@ module Grpc
@status_mutex.synchronize do
status = @statuses["#{req.service}"]
end
- fail GRPC::BadStatus, StatusCodes::NOT_FOUND if status.nil?
+ if status.nil?
+ fail GRPC::BadStatus.new_status_exception(StatusCodes::NOT_FOUND)
+ end
HealthCheckResponse.new(status: status)
end
diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb
index b9af160e7a..9ee5cdf9fd 100755
--- a/src/ruby/pb/test/client.rb
+++ b/src/ruby/pb/test/client.rb
@@ -338,11 +338,8 @@ class NamedTests
deadline = GRPC::Core::TimeConsts::from_relative_time(1)
resps = @stub.full_duplex_call(enum.each_item, deadline: deadline)
resps.each { } # wait to receive each request (or timeout)
- fail 'Should have raised GRPC::BadStatus(DEADLINE_EXCEEDED)'
- rescue GRPC::BadStatus => e
- assert("#{__callee__}: status was wrong") do
- e.code == GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
- end
+ fail 'Should have raised GRPC::DeadlineExceeded'
+ rescue GRPC::DeadlineExceeded
end
def empty_stream
diff --git a/src/ruby/spec/error_sanity_spec.rb b/src/ruby/spec/error_sanity_spec.rb
new file mode 100644
index 0000000000..77e94a8816
--- /dev/null
+++ b/src/ruby/spec/error_sanity_spec.rb
@@ -0,0 +1,64 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require 'grpc'
+
+StatusCodes = GRPC::Core::StatusCodes
+
+describe StatusCodes do
+ # convert upper snake-case to camel case.
+ # e.g., DEADLINE_EXCEEDED -> DeadlineExceeded
+ def upper_snake_to_camel(name)
+ name.to_s.split('_').map(&:downcase).map(&:capitalize).join('')
+ end
+
+ StatusCodes.constants.each do |status_name|
+ it 'there is a subclass of BadStatus corresponding to StatusCode: ' \
+ "#{status_name} that has code: #{StatusCodes.const_get(status_name)}" do
+ camel_case = upper_snake_to_camel(status_name)
+ error_class = GRPC.const_get(camel_case)
+ # expect the error class to be a subclass of BadStatus
+ expect(error_class < GRPC::BadStatus)
+
+ error_object = error_class.new
+ # check that the code matches the int value of the error's constant
+ status_code = StatusCodes.const_get(status_name)
+ expect(error_object.code).to eq(status_code)
+
+ # check default parameters
+ expect(error_object.details).to eq('unknown cause')
+ expect(error_object.metadata).to eq({})
+
+ # check that the BadStatus factory for creates the correct
+ # exception too
+ from_factory = GRPC::BadStatus.new_status_exception(status_code)
+ expect(from_factory.is_a?(error_class)).to be(true)
+ end
+ end
+end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index 9c4e9cbd07..08a171e299 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -190,15 +190,14 @@ describe 'ClientStub' do
end
creds = GRPC::Core::CallCredentials.new(failing_auth)
- error_occured = false
+ unauth_error_occured = false
begin
get_response(stub, credentials: creds)
- rescue GRPC::BadStatus => e
- error_occured = true
- expect(e.code).to eq(GRPC::Core::StatusCodes::UNAUTHENTICATED)
+ rescue GRPC::Unauthenticated => e
+ unauth_error_occured = true
expect(e.details.include?(error_message)).to be true
end
- expect(error_occured).to eq(true)
+ expect(unauth_error_occured).to eq(true)
# Kill the server thread so tests can complete
th.kill
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index 31157cf161..1689d2df68 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -414,9 +414,8 @@ describe GRPC::RpcServer do
stub = SlowStub.new(alt_host, :this_channel_is_insecure)
begin
stub.an_rpc(req)
- rescue GRPC::BadStatus => e
- one_failed_as_unavailable =
- e.code == StatusCodes::RESOURCE_EXHAUSTED
+ rescue GRPC::ResourceExhausted
+ one_failed_as_unavailable = true
end
end
end
diff --git a/src/ruby/spec/pb/health/checker_spec.rb b/src/ruby/spec/pb/health/checker_spec.rb
index 1b2fa96827..2cc58f845b 100644
--- a/src/ruby/spec/pb/health/checker_spec.rb
+++ b/src/ruby/spec/pb/health/checker_spec.rb
@@ -119,7 +119,7 @@ describe Grpc::Health::Checker do
subject.check(HCReq.new(service: t[:service]), nil)
end
expected_msg = /#{StatusCodes::NOT_FOUND}/
- expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+ expect(&blk).to raise_error GRPC::NotFound, expected_msg
end
end
end
@@ -137,7 +137,7 @@ describe Grpc::Health::Checker do
subject.check(HCReq.new(service: t[:service]), nil)
end
expected_msg = /#{StatusCodes::NOT_FOUND}/
- expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+ expect(&blk).to raise_error GRPC::NotFound, expected_msg
end
end
end
@@ -158,7 +158,7 @@ describe Grpc::Health::Checker do
subject.check(HCReq.new(service: t[:service]), nil)
end
expected_msg = /#{StatusCodes::NOT_FOUND}/
- expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+ expect(&blk).to raise_error GRPC::NotFound, expected_msg
end
end
end
@@ -206,7 +206,7 @@ describe Grpc::Health::Checker do
stub.check(HCReq.new(service: 'unknown'))
end
expected_msg = /#{StatusCodes::NOT_FOUND}/
- expect(&blk).to raise_error GRPC::BadStatus, expected_msg
+ expect(&blk).to raise_error GRPC::NotFound, expected_msg
@srv.stop
t.join
end