userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
rich_status.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file userver/ugrpc/rich_status.hpp
4
/// @brief userver wrapper for `google::rpc::Status`
5
6
#
include
<
optional
>
7
8
#
include
<
google
/
protobuf
/
any
.
pb
.
h
>
9
#
include
<
google
/
protobuf
/
message
.
h
>
10
#
include
<
google
/
rpc
/
error_details
.
pb
.
h
>
11
#
include
<
google
/
rpc
/
status
.
pb
.
h
>
12
#
include
<
grpcpp
/
support
/
status
.
h
>
13
14
USERVER_NAMESPACE_BEGIN
15
16
namespace
ugrpc {
17
18
/// @brief A wrapper around `google::rpc::Status` that provides a convenient API
19
/// for creating and managing gRPC status responses with rich error details.
20
///
21
/// Documentation: @see @ref scripts/docs/en/userver/grpc/rich_status.md
22
///
23
/// `RichStatus` allows you to create gRPC status responses with structured error
24
/// information conforming to the Google RPC error model. It supports adding
25
/// multiple error details of various types to provide comprehensive error
26
/// information to clients.
27
///
28
/// @see https://google.aip.dev/193
29
/// @see https://grpc.io/docs/guides/error/
30
/// @see @ref ugrpc::ErrorInfo
31
/// @see @ref ugrpc::RetryInfo
32
/// @see @ref ugrpc::DebugInfo
33
/// @see @ref ugrpc::QuotaFailure
34
/// @see @ref ugrpc::PreconditionFailure
35
/// @see @ref ugrpc::BadRequest
36
/// @see @ref ugrpc::RequestInfo
37
/// @see @ref ugrpc::ResourceInfo
38
/// @see @ref ugrpc::Help
39
/// @see @ref ugrpc::LocalizedMessage
40
///
41
/// ## Example usage:
42
/// @snippet grpc/tests/detailed_error_test.cpp rich_status_usage
43
class
RichStatus
final
{
44
public
:
45
/// @brief Constructs an `OK` status with no error details.
46
RichStatus
() =
default
;
47
48
/// @brief Constructs a `RichStatus` from a `grpc::Status`.
49
/// @param grpc_status The gRPC status to convert. If it contains serialized
50
/// `google::rpc::Status` in `error_details()`, it will be
51
/// parsed and used.
52
explicit
RichStatus
(
const
grpc::Status& grpc_status);
53
54
/// @brief Constructs a `RichStatus` with the given code, message, and details.
55
/// @param code The gRPC status code
56
/// @param message The error message
57
/// @param details Error details conforming to `google.rpc.error_details` types.
58
/// Must have a `ToGoogleErrorDetail()` method.
59
template
<
typename
... TDetails>
60
RichStatus
(grpc::StatusCode code, std::string message, TDetails&&... details);
61
62
/// @brief Adds an error detail to the status.
63
/// @param detail The error detail to add. Must have a `ToGoogleErrorDetail()` method.
64
/// @return Reference to this `RichStatus` for method chaining.
65
template
<
typename
TDetail>
66
RichStatus&
AddDetail
(TDetail&& detail);
67
68
/// @brief Attempts to extract a specific rich error detail from `google::rpc::Status`
69
/// @return The extracted rich error detail if found, `std::nullopt` otherwise
70
///
71
/// According to AIP-193 (https://google.aip.dev/193), each status
72
/// should contain at most one detail of each type. If multiple details
73
/// of the same type exist (violating AIP-193), this function will
74
/// return only the first encountered instance.
75
///
76
/// ## Example usage:
77
/// @snippet grpc/tests/detailed_error_test.cpp try_get_rich_error_detail
78
template
<
typename
TRichErrorDetail>
79
[[nodiscard]]
static
std::optional<TRichErrorDetail>
TryGetDetail
(
const
google::rpc::Status& status);
80
81
[[nodiscard]]
const
google::rpc::Status& GetGoogleStatus()
const
& {
return
google_status_; }
82
google::rpc::Status GetGoogleStatus() && {
return
std::move(google_status_); }
83
84
/// @brief Converts this `RichStatus` to a `grpc::Status`.
85
/// @return A `grpc::Status` with serialized error details.
86
[[nodiscard]] grpc::Status
ToGrpcStatus
()
const
;
87
88
[[nodiscard]]
explicit
operator grpc::Status()
const
{
return
ToGrpcStatus
(
)
; }
89
90
private
:
91
google::rpc::Status google_status_;
92
};
93
94
/// @brief Provides structured error information about the cause of an error.
95
///
96
/// @see https://google.aip.dev/193
97
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
98
/// @see @ref ugrpc::RichStatus
99
///
100
/// Use this detail to provide a machine-readable error reason, domain, and
101
/// additional metadata. This is useful for error handling logic on the client side.
102
///
103
/// Any request-specific information which contributes to the `Status.message` or `LocalizedMessage.message` messages
104
/// must be represented within metadata. This practice is critical so that machine actors do not need to parse error
105
/// messages to extract information.
106
///
107
/// ## Example usage:
108
/// @snippet grpc/tests/rich_status_test.cpp error_info_example
109
struct
ErrorInfo
{
110
/// @brief The reason of the error (e.g., "INVALID_TOKEN", "SERVICE_DISABLED").
111
///
112
/// The reason field is a short snake_case description of the cause of the error. Error reasons are unique within a
113
/// particular domain of errors. The reason must be at most 63 characters and match a regular expression of
114
/// `[A-Z][A-Z0-9_]+[A-Z0-9]`. (This is UPPER_SNAKE_CASE, without leading or trailing underscores, and without
115
/// leading digits.)
116
std::string
reason
;
117
118
/// @brief The logical grouping to which the error belongs (e.g., "auth.example.com").
119
std::string
domain
;
120
121
/// @brief Additional structured details about the error.
122
std::unordered_map<std::string, std::string>
metadata
;
123
124
google::rpc::ErrorInfo ToGoogleErrorDetail()
const
&;
125
google::rpc::ErrorInfo ToGoogleErrorDetail() &&;
126
127
static
std::optional<
ErrorInfo
> TryUnpack(
const
google::protobuf::Any& any);
128
};
129
130
/// @brief Describes when the client can retry a failed request.
131
///
132
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
133
/// @see @ref ugrpc::RichStatus
134
///
135
/// ## Example usage:
136
/// @snippet grpc/tests/rich_status_test.cpp retry_info_example
137
struct
RetryInfo
{
138
/// @brief Clients should wait at least this long between retrying the same request.
139
std::chrono::nanoseconds
retry_delay
;
140
141
google::rpc::RetryInfo ToGoogleErrorDetail()
const
;
142
143
static
std::optional<
RetryInfo
> TryUnpack(
const
google::protobuf::Any& any);
144
};
145
146
/// @brief Provides debugging information such as stack traces.
147
///
148
/// @see @ref ugrpc::RichStatus
149
///
150
/// ## Example usage:
151
/// @snippet grpc/tests/rich_status_test.cpp debug_info_example
152
struct
DebugInfo
{
153
std::vector<std::string> stack_entries;
154
std::string detail;
155
156
google::rpc::DebugInfo ToGoogleErrorDetail()
const
&;
157
google::rpc::DebugInfo ToGoogleErrorDetail() &&;
158
159
static
std::optional<
DebugInfo
> TryUnpack(
const
google::protobuf::Any& any);
160
};
161
162
/// @brief Describes a single quota violation.
163
///
164
/// @see @ref ugrpc::QuotaFailure
165
struct
QuotaViolation
{
166
/// @brief The subject on which the quota check failed.
167
///
168
/// Example: "clientip:<ip address>", "project:<project id>"
169
std::string
subject
;
170
171
/// @brief A description of how the quota check failed.
172
std::string
description
;
173
};
174
175
/// @brief Describes quota violations that caused the request to fail.
176
///
177
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
178
/// @see @ref ugrpc::RichStatus
179
///
180
/// ## Example usage:
181
/// @snippet grpc/tests/rich_status_test.cpp quota_failure_example
182
struct
QuotaFailure
{
183
std::vector<
QuotaViolation
> violations;
184
185
google::rpc::QuotaFailure ToGoogleErrorDetail()
const
&;
186
google::rpc::QuotaFailure ToGoogleErrorDetail() &&;
187
188
static
std::optional<
QuotaFailure
> TryUnpack(
const
google::protobuf::Any& any);
189
};
190
191
/// @brief Describes a single precondition violation.
192
///
193
/// @see @ref ugrpc::PreconditionFailure
194
struct
PreconditionViolation
{
195
/// @brief The type of precondition that failed (e.g., "TOS", "AGE").
196
///
197
/// Google recommends using a service-specific enum type to define the supported
198
/// precondition violation subjects. For example, "TOS" for "Terms of Service violation".
199
std::string
type
;
200
201
/// @brief The subject on which the precondition failed (e.g., "user:123").
202
///
203
/// For example, "google.com/cloud" relative to the "TOS" type would indicate
204
/// which terms of service is being referenced.
205
std::string
subject
;
206
207
/// @brief A description of how the precondition failed.
208
///
209
/// Developers can use this description to understand how to fix the failure.
210
/// For Example usage: "Terms of service not accepted".
211
std::string
description
;
212
};
213
214
/// @brief Describes preconditions that failed during request processing.
215
///
216
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
217
/// @see @ref ugrpc::RichStatus
218
///
219
/// For example, if an RPC failed because it required the Terms of Service to be
220
/// acknowledged, it could list the terms of service violation in the
221
/// PreconditionFailure message.
222
///
223
/// ## Example usage:
224
/// @snippet grpc/tests/rich_status_test.cpp precondition_failure_example
225
struct
PreconditionFailure
{
226
/// @brief The list of precondition violations.
227
std::vector<
PreconditionViolation
>
violations
;
228
229
google::rpc::PreconditionFailure ToGoogleErrorDetail()
const
&;
230
google::rpc::PreconditionFailure ToGoogleErrorDetail() &&;
231
232
static
std::optional<
PreconditionFailure
> TryUnpack(
const
google::protobuf::Any& any);
233
};
234
235
/// @brief Describes a single field validation error.
236
///
237
/// @see @ref ugrpc::BadRequest
238
struct
FieldViolation
{
239
/// @brief The field path (e.g., "user.email", "address.postal_code").
240
std::string
field
;
241
242
/// @brief A description of why the field is invalid.
243
std::string
description
;
244
};
245
246
/// @brief Describes violations in a client request. This error type focuses on the
247
/// syntactic aspects of the request.
248
///
249
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
250
/// @see @ref ugrpc::RichStatus
251
///
252
/// ## Example usage:
253
/// @snippet grpc/tests/rich_status_test.cpp bad_request_example
254
struct
BadRequest
{
255
std::vector<
FieldViolation
> field_violations;
256
257
google::rpc::BadRequest ToGoogleErrorDetail()
const
&;
258
google::rpc::BadRequest ToGoogleErrorDetail() &&;
259
260
static
std::optional<
BadRequest
> TryUnpack(
const
google::protobuf::Any& any);
261
};
262
263
/// @brief Contains metadata about the request for debugging and logging.
264
///
265
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
266
/// @see @ref ugrpc::RichStatus
267
///
268
///
269
/// Use this detail to provide request identification information that can
270
/// help with debugging and tracking requests across services.
271
///
272
/// ## Example usage:
273
/// @snippet grpc/tests/rich_status_test.cpp request_info_example
274
struct
RequestInfo
{
275
std::string request_id;
276
std::string serving_data;
277
278
google::rpc::RequestInfo ToGoogleErrorDetail()
const
&;
279
google::rpc::RequestInfo ToGoogleErrorDetail() &&;
280
281
static
std::optional<
RequestInfo
> TryUnpack(
const
google::protobuf::Any& any);
282
};
283
284
/// @brief Provides information about a resource that is related to the error.
285
///
286
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
287
/// @see @ref ugrpc::RichStatus
288
///
289
/// Use this detail to describe a resource that caused the error or is
290
/// affected by the error. Commonly used with `NOT_FOUND`, `ALREADY_EXISTS`,
291
/// or `PERMISSION_DENIED` status codes.
292
///
293
/// ## Example usage:
294
/// @snippet grpc/tests/rich_status_test.cpp resource_info_example
295
struct
ResourceInfo
{
296
/// @brief The type of resource (e.g., "storage.bucket", "compute.instance").
297
std::string
resource_type
;
298
299
/// @brief The name/identifier of the resource.
300
std::string
resource_name
;
301
302
/// @brief The owner of the resource.
303
std::string
owner
;
304
305
/// @brief The description of the error that is encountered when accessing this resource.
306
std::string
description
;
307
308
google::rpc::ResourceInfo ToGoogleErrorDetail()
const
&;
309
google::rpc::ResourceInfo ToGoogleErrorDetail() &&;
310
311
static
std::optional<
ResourceInfo
> TryUnpack(
const
google::protobuf::Any& any);
312
};
313
314
/// @brief Describes a single help link.
315
///
316
/// @see @ref ugrpc::Help
317
struct
HelpLink
{
318
std::string description;
319
std::string url;
320
};
321
322
/// @brief Provides links to documentation and help resources.
323
///
324
/// @see https://google.aip.dev/193
325
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
326
/// @see https://nda.ya.ru/t/PmNiceWp7NKDXb
327
/// @see @ref ugrpc::RichStatus
328
///
329
/// Use this detail to direct users to relevant documentation, FAQs,
330
/// or support resources that can help them resolve the error.
331
///
332
/// ## Example usage:
333
/// @snippet grpc/tests/rich_status_test.cpp help_example
334
struct
Help
{
335
/// @brief The list of help links.
336
std::vector<
HelpLink
>
links
;
337
338
google::rpc::Help ToGoogleErrorDetail()
const
&;
339
google::rpc::Help ToGoogleErrorDetail() &&;
340
341
static
std::optional<
Help
> TryUnpack(
const
google::protobuf::Any& any);
342
};
343
344
/// @brief Provides a localized error message that is safe to return to the user.
345
///
346
/// @see https://google.aip.dev/193
347
/// @see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
348
/// @see @ref ugrpc::RichStatus
349
///
350
/// Provides a localized error message that is safe to return to the user
351
/// which can be attached to an RPC error.
352
///
353
/// The `LocalizedMessage` payload should contain the complete resolution to the error. If more information is needed
354
/// than can reasonably fit in this payload, then additional resolution information must be provided in a @ref Help
355
/// payload.
356
///
357
/// ## Example usage:
358
/// @snippet grpc/tests/rich_status_test.cpp localized_message_example
359
struct
LocalizedMessage
{
360
/// @brief The locale (e.g., "en-US", "ru-RU", "ja-JP").
361
std::string
locale
;
362
363
/// @brief The localized error message.
364
///
365
/// This should include a brief description of the error and a call to action to resolve the error. The message
366
/// should include contextual information to make the message as specific as possible. Any contextual information in
367
/// the message must be included in @ref ErrorInfo.metadata.
368
std::string
message
;
369
370
google::rpc::LocalizedMessage ToGoogleErrorDetail()
const
&;
371
google::rpc::LocalizedMessage ToGoogleErrorDetail() &&;
372
373
static
std::optional<
LocalizedMessage
> TryUnpack(
const
google::protobuf::Any& any);
374
};
375
376
template
<
typename
... TDetails>
377
RichStatus::
RichStatus
(grpc::StatusCode code, std::string message, TDetails&&... details) {
378
google_status_.set_code(
static_cast
<
int
>(code));
379
google_status_.set_message(std::move(message));
380
(AddDetail(std::forward<TDetails>(details)), ...);
381
}
382
383
template
<
typename
TDetail>
384
RichStatus& RichStatus::
AddDetail
(TDetail&& detail) {
385
const
auto
pb_google_detail = std::forward<TDetail>(detail).ToGoogleErrorDetail();
386
google_status_.add_details()->PackFrom(pb_google_detail);
387
return
*
this
;
388
}
389
390
template
<
typename
TRichErrorDetail>
391
std::optional<TRichErrorDetail> RichStatus::
TryGetDetail
(
const
google::rpc::Status& status) {
392
for
(
const
auto
& detail : status.details()) {
393
const
auto
rich_error_detail_opt = TRichErrorDetail::TryUnpack(detail);
394
if
(rich_error_detail_opt) {
395
return
*rich_error_detail_opt;
396
}
397
}
398
return
std::nullopt;
399
}
400
401
}
// namespace ugrpc
402
403
USERVER_NAMESPACE_END
userver
ugrpc
rich_status.hpp
Generated on
for userver by
Doxygen
1.17.0