1# Copyright 2015 gRPC authors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15require_relative './structs'
16require_relative './core/status_codes'
17require_relative './google_rpc_status_utils'
18
19# GRPC contains the General RPC module.
20module GRPC
21  # BadStatus is an exception class that indicates that an error occurred at
22  # either end of a GRPC connection.  When raised, it indicates that a status
23  # error should be returned to the other end of a GRPC connection; when
24  # caught it means that this end received a status error.
25  #
26  # There is also subclass of BadStatus in this module for each GRPC status.
27  # E.g., the GRPC::Cancelled class corresponds to status CANCELLED.
28  #
29  # See
30  # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/status.h
31  # for detailed descriptions of each status code.
32  class BadStatus < StandardError
33    attr_reader :code, :details, :metadata, :debug_error_string
34
35    include GRPC::Core::StatusCodes
36
37    # @param code [Numeric] the status code
38    # @param details [String] the details of the exception
39    # @param metadata [Hash] the error's metadata
40    def initialize(code,
41                   details = 'unknown cause',
42                   metadata = {},
43                   debug_error_string = nil)
44      exception_message = "#{code}:#{details}"
45      if debug_error_string
46        exception_message += ". debug_error_string:#{debug_error_string}"
47      end
48      super(exception_message)
49      @code = code
50      @details = details
51      @metadata = metadata
52      @debug_error_string = debug_error_string
53    end
54
55    # Converts the exception to a {Struct::Status} for use in the networking
56    # wrapper layer.
57    #
58    # @return [Struct::Status] with the same code and details
59    def to_status
60      Struct::Status.new(code, details, metadata, debug_error_string)
61    end
62
63    # Converts the exception to a deserialized {Google::Rpc::Status} object.
64    # Returns `nil` if the `grpc-status-details-bin` trailer could not be
65    # converted to a {Google::Rpc::Status} due to the server not providing
66    # the necessary trailers.
67    #
68    # @return [Google::Rpc::Status, nil]
69    def to_rpc_status
70      GoogleRpcStatusUtils.extract_google_rpc_status(to_status)
71    rescue Google::Protobuf::ParseError => parse_error
72      GRPC.logger.warn('parse error: to_rpc_status failed')
73      GRPC.logger.warn(parse_error)
74      nil
75    end
76
77    def self.new_status_exception(code,
78                                  details = 'unknown cause',
79                                  metadata = {},
80                                  debug_error_string = nil)
81      codes = {}
82      codes[OK] = Ok
83      codes[CANCELLED] = Cancelled
84      codes[UNKNOWN] = Unknown
85      codes[INVALID_ARGUMENT] = InvalidArgument
86      codes[DEADLINE_EXCEEDED] = DeadlineExceeded
87      codes[NOT_FOUND] = NotFound
88      codes[ALREADY_EXISTS] = AlreadyExists
89      codes[PERMISSION_DENIED] = PermissionDenied
90      codes[UNAUTHENTICATED] = Unauthenticated
91      codes[RESOURCE_EXHAUSTED] = ResourceExhausted
92      codes[FAILED_PRECONDITION] = FailedPrecondition
93      codes[ABORTED] = Aborted
94      codes[OUT_OF_RANGE] = OutOfRange
95      codes[UNIMPLEMENTED] = Unimplemented
96      codes[INTERNAL] = Internal
97      codes[UNAVAILABLE] = Unavailable
98      codes[DATA_LOSS] = DataLoss
99
100      if codes[code].nil?
101        BadStatus.new(code, details, metadata, debug_error_string)
102      else
103        codes[code].new(details, metadata, debug_error_string)
104      end
105    end
106  end
107
108  # GRPC status code corresponding to status OK
109  class Ok < BadStatus
110    def initialize(details = 'unknown cause',
111                   metadata = {},
112                   debug_error_string = nil)
113      super(Core::StatusCodes::OK,
114            details, metadata, debug_error_string)
115    end
116  end
117
118  # GRPC status code corresponding to status CANCELLED
119  class Cancelled < BadStatus
120    def initialize(details = 'unknown cause',
121                   metadata = {},
122                   debug_error_string = nil)
123      super(Core::StatusCodes::CANCELLED,
124            details, metadata, debug_error_string)
125    end
126  end
127
128  # GRPC status code corresponding to status UNKNOWN
129  class Unknown < BadStatus
130    def initialize(details = 'unknown cause',
131                   metadata = {},
132                   debug_error_string = nil)
133      super(Core::StatusCodes::UNKNOWN,
134            details, metadata, debug_error_string)
135    end
136  end
137
138  # GRPC status code corresponding to status INVALID_ARGUMENT
139  class InvalidArgument < BadStatus
140    def initialize(details = 'unknown cause',
141                   metadata = {},
142                   debug_error_string = nil)
143      super(Core::StatusCodes::INVALID_ARGUMENT,
144            details, metadata, debug_error_string)
145    end
146  end
147
148  # GRPC status code corresponding to status DEADLINE_EXCEEDED
149  class DeadlineExceeded < BadStatus
150    def initialize(details = 'unknown cause',
151                   metadata = {},
152                   debug_error_string = nil)
153      super(Core::StatusCodes::DEADLINE_EXCEEDED,
154            details, metadata, debug_error_string)
155    end
156  end
157
158  # GRPC status code corresponding to status NOT_FOUND
159  class NotFound < BadStatus
160    def initialize(details = 'unknown cause',
161                   metadata = {},
162                   debug_error_string = nil)
163      super(Core::StatusCodes::NOT_FOUND,
164            details, metadata, debug_error_string)
165    end
166  end
167
168  # GRPC status code corresponding to status ALREADY_EXISTS
169  class AlreadyExists < BadStatus
170    def initialize(details = 'unknown cause',
171                   metadata = {},
172                   debug_error_string = nil)
173      super(Core::StatusCodes::ALREADY_EXISTS,
174            details, metadata, debug_error_string)
175    end
176  end
177
178  # GRPC status code corresponding to status PERMISSION_DENIED
179  class PermissionDenied < BadStatus
180    def initialize(details = 'unknown cause',
181                   metadata = {},
182                   debug_error_string = nil)
183      super(Core::StatusCodes::PERMISSION_DENIED,
184            details, metadata, debug_error_string)
185    end
186  end
187
188  # GRPC status code corresponding to status UNAUTHENTICATED
189  class Unauthenticated < BadStatus
190    def initialize(details = 'unknown cause',
191                   metadata = {},
192                   debug_error_string = nil)
193      super(Core::StatusCodes::UNAUTHENTICATED,
194            details, metadata, debug_error_string)
195    end
196  end
197
198  # GRPC status code corresponding to status RESOURCE_EXHAUSTED
199  class ResourceExhausted < BadStatus
200    def initialize(details = 'unknown cause',
201                   metadata = {},
202                   debug_error_string = nil)
203      super(Core::StatusCodes::RESOURCE_EXHAUSTED,
204            details, metadata, debug_error_string)
205    end
206  end
207
208  # GRPC status code corresponding to status FAILED_PRECONDITION
209  class FailedPrecondition < BadStatus
210    def initialize(details = 'unknown cause',
211                   metadata = {},
212                   debug_error_string = nil)
213      super(Core::StatusCodes::FAILED_PRECONDITION,
214            details, metadata, debug_error_string)
215    end
216  end
217
218  # GRPC status code corresponding to status ABORTED
219  class Aborted < BadStatus
220    def initialize(details = 'unknown cause',
221                   metadata = {},
222                   debug_error_string = nil)
223      super(Core::StatusCodes::ABORTED,
224            details, metadata, debug_error_string)
225    end
226  end
227
228  # GRPC status code corresponding to status OUT_OF_RANGE
229  class OutOfRange < BadStatus
230    def initialize(details = 'unknown cause',
231                   metadata = {},
232                   debug_error_string = nil)
233      super(Core::StatusCodes::OUT_OF_RANGE,
234            details, metadata, debug_error_string)
235    end
236  end
237
238  # GRPC status code corresponding to status UNIMPLEMENTED
239  class Unimplemented < BadStatus
240    def initialize(details = 'unknown cause',
241                   metadata = {},
242                   debug_error_string = nil)
243      super(Core::StatusCodes::UNIMPLEMENTED,
244            details, metadata, debug_error_string)
245    end
246  end
247
248  # GRPC status code corresponding to status INTERNAL
249  class Internal < BadStatus
250    def initialize(details = 'unknown cause',
251                   metadata = {},
252                   debug_error_string = nil)
253      super(Core::StatusCodes::INTERNAL,
254            details, metadata, debug_error_string)
255    end
256  end
257
258  # GRPC status code corresponding to status UNAVAILABLE
259  class Unavailable < BadStatus
260    def initialize(details = 'unknown cause',
261                   metadata = {},
262                   debug_error_string = nil)
263      super(Core::StatusCodes::UNAVAILABLE,
264            details, metadata, debug_error_string)
265    end
266  end
267
268  # GRPC status code corresponding to status DATA_LOSS
269  class DataLoss < BadStatus
270    def initialize(details = 'unknown cause',
271                   metadata = {},
272                   debug_error_string = nil)
273      super(Core::StatusCodes::DATA_LOSS,
274            details, metadata, debug_error_string)
275    end
276  end
277end
278