Skip to content

Commit 69ce71e

Browse files
authoredMar 20, 2023
feat(types): Add gRPC Richer Error Model support (Docs) (#1317)
* types: improve `lib.rs` docs, add `README` file * types: update `Cargo.toml` file
1 parent ddec9e3 commit 69ce71e

File tree

3 files changed

+283
-3
lines changed

3 files changed

+283
-3
lines changed
 

‎tonic-types/Cargo.toml

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
[package]
2-
authors = ["Lucio Franco <luciofranco14@gmail.com>"]
2+
authors = [
3+
"Lucio Franco <luciofranco14@gmail.com>",
4+
"Rafael Lemos <flemos.rafael.dev@gmail.com>"
5+
]
36
categories = ["web-programming", "network-programming", "asynchronous"]
47
description = """
58
A collection of useful protobuf types that can be used with `tonic`.
69
"""
7-
documentation = "https://docs.rs/tonic-types/0.5.0/tonic-types/"
10+
documentation = "https://docs.rs/tonic-types/0.6.1/tonic_types/"
811
edition = "2021"
912
homepage = "https://github.com/hyperium/tonic"
1013
keywords = ["rpc", "grpc", "protobuf"]
1114
license = "MIT"
1215
name = "tonic-types"
13-
readme = "../README.md"
16+
readme = "README.md"
1417
repository = "https://github.com/hyperium/tonic"
1518
version = "0.6.1"
1619

‎tonic-types/README.md

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# tonic-types
2+
3+
A collection of useful protobuf types that can be used with `tonic`.
4+
5+
This crate also introduces the [`StatusExt`] trait and implements it in
6+
[`tonic::Status`], allowing the implementation of the [gRPC Richer Error Model]
7+
with [`tonic`] in a convenient way.
8+
9+
## Usage
10+
11+
Useful protobuf types are available through the [`pb`] module. They can be
12+
imported and worked with directly.
13+
14+
The [`StatusExt`] trait adds associated functions to [`tonic::Status`] that can
15+
be used on the server side to create a status with error details, which can then
16+
be returned to gRPC clients. Moreover, the trait also adds methods to
17+
[`tonic::Status`] that can be used by a tonic client to extract error details,
18+
and handle them with ease.
19+
20+
## Getting Started
21+
22+
```toml
23+
[dependencies]
24+
tonic = <tonic-version>
25+
tonic-types = <tonic-types-version>
26+
```
27+
28+
## Examples
29+
30+
The examples bellow cover a basic use case of the [gRPC Richer Error Model].
31+
More complete server and client implementations are provided in the
32+
**Richer Error example**, located in the main repo [examples] directory.
33+
34+
### Server Side: Generating [`tonic::Status`] with an [`ErrorDetails`] struct
35+
36+
```rust
37+
use tonic::{Code, Status};
38+
use tonic_types::{ErrorDetails, StatusExt};
39+
40+
// ...
41+
// Inside a gRPC server endpoint that returns `Result<Response<T>, Status>`
42+
43+
// Create empty `ErrorDetails` struct
44+
let mut err_details = ErrorDetails::new();
45+
46+
// Add error details conditionally
47+
if some_condition {
48+
err_details.add_bad_request_violation(
49+
"field_a",
50+
"description of why the field_a is invalid"
51+
);
52+
}
53+
54+
if other_condition {
55+
err_details.add_bad_request_violation(
56+
"field_b",
57+
"description of why the field_b is invalid",
58+
);
59+
}
60+
61+
// Check if any error details were set and return error status if so
62+
if err_details.has_bad_request_violations() {
63+
// Add additional error details if necessary
64+
err_details
65+
.add_help_link("description of link", "https://resource.example.local")
66+
.set_localized_message("en-US", "message for the user");
67+
68+
let status = Status::with_error_details(
69+
Code::InvalidArgument,
70+
"bad request",
71+
err_details,
72+
);
73+
return Err(status);
74+
}
75+
76+
// Handle valid request
77+
// ...
78+
```
79+
80+
### Client Side: Extracting an [`ErrorDetails`] struct from [`tonic::Status`]
81+
82+
```rust
83+
use tonic::{Response, Status};
84+
use tonic_types::StatusExt;
85+
86+
// ...
87+
// Where `req_result` was returned by a gRPC client endpoint method
88+
fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
89+
match req_result {
90+
Ok(response) => {
91+
// Handle successful response
92+
},
93+
Err(status) => {
94+
let err_details = status.get_error_details();
95+
if let Some(bad_request) = err_details.bad_request() {
96+
// Handle bad_request details
97+
}
98+
if let Some(help) = err_details.help() {
99+
// Handle help details
100+
}
101+
if let Some(localized_message) = err_details.localized_message() {
102+
// Handle localized_message details
103+
}
104+
}
105+
};
106+
}
107+
```
108+
109+
## Working with different error message types
110+
111+
Multiple examples are provided at the [`ErrorDetails`] doc. Instructions about
112+
how to use the fields of the standard error message types correctly are provided
113+
at [error_details.proto].
114+
115+
## Alternative `tonic::Status` associated functions and methods
116+
117+
In the [`StatusExt`] doc, an alternative way of interacting with
118+
[`tonic::Status`] is presented, using vectors of error details structs wrapped
119+
with the [`ErrorDetail`] enum. This approach can provide more control over the
120+
vector of standard error messages that will be generated or that was received,
121+
if necessary. To see how to adopt this approach, please check the
122+
[`StatusExt::with_error_details_vec`] and [`StatusExt::get_error_details_vec`]
123+
docs, and also the main repo's [Richer Error example] directory.
124+
125+
Besides that, multiple examples with alternative error details extraction
126+
methods are provided in the [`StatusExt`] doc, which can be specially
127+
useful if only one type of standard error message is being handled by the
128+
client. For example, using [`StatusExt::get_details_bad_request`] is a
129+
more direct way of extracting a [`BadRequest`] error message from
130+
[`tonic::Status`].
131+
132+
[`tonic::Status`]: https://docs.rs/tonic/latest/tonic/struct.Status.html
133+
[`tonic`]: https://docs.rs/tonic/latest/tonic/
134+
[gRPC Richer Error Model]: https://www.grpc.io/docs/guides/error/
135+
[`pb`]: https://docs.rs/tonic-types/latest/tonic_types/pb/index.html
136+
[`StatusExt`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html
137+
[examples]: https://github.com/hyperium/tonic/tree/master/examples
138+
[`ErrorDetails`]: https://docs.rs/tonic-types/latest/tonic_types/struct.ErrorDetails.html
139+
[error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
140+
[`ErrorDetail`]: https://docs.rs/tonic-types/latest/tonic_types/enum.ErrorDetail.html
141+
[`StatusExt::with_error_details_vec`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html#tymethod.with_error_details_vec
142+
[`StatusExt::get_error_details_vec`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html#tymethod.get_error_details_vec
143+
[Richer Error example]: https://github.com/hyperium/tonic/tree/master/examples/src/richer-error
144+
[`StatusExt::get_details_bad_request`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html#tymethod.get_details_bad_request
145+
[`BadRequest`]: https://docs.rs/tonic-types/latest/tonic_types/struct.BadRequest.html

‎tonic-types/src/lib.rs

+132
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,141 @@
44
//! [`tonic::Status`], allowing the implementation of the
55
//! [gRPC Richer Error Model] with [`tonic`] in a convenient way.
66
//!
7+
//! # Usage
8+
//!
9+
//! Useful protobuf types are available through the [`pb`] module. They can be
10+
//! imported and worked with directly.
11+
//!
12+
//! The [`StatusExt`] trait adds associated functions to [`tonic::Status`] that
13+
//! can be used on the server side to create a status with error details, which
14+
//! can then be returned to gRPC clients. Moreover, the trait also adds methods
15+
//! to [`tonic::Status`] that can be used by a tonic client to extract error
16+
//! details, and handle them with ease.
17+
//!
18+
//! # Getting Started
19+
//!
20+
//! ```toml
21+
//! [dependencies]
22+
//! tonic = <tonic-version>
23+
//! tonic-types = <tonic-types-version>
24+
//! ```
25+
//!
26+
//! # Examples
27+
//!
28+
//! The examples bellow cover a basic use case of the [gRPC Richer Error Model].
29+
//! More complete server and client implementations are provided in the
30+
//! **Richer Error example**, located in the main repo [examples] directory.
31+
//!
32+
//! ## Server Side: Generating [`tonic::Status`] with an [`ErrorDetails`] struct
33+
//!
34+
//! ```
35+
//! use tonic::{Code, Status};
36+
//! use tonic_types::{ErrorDetails, StatusExt};
37+
//!
38+
//! # async fn endpoint() -> Result<tonic::Response<()>, Status> {
39+
//! // ...
40+
//! // Inside a gRPC server endpoint that returns `Result<Response<T>, Status>`
41+
//!
42+
//! // Create empty `ErrorDetails` struct
43+
//! let mut err_details = ErrorDetails::new();
44+
//!
45+
//! // Add error details conditionally
46+
//! # let some_condition = true;
47+
//! if some_condition {
48+
//! err_details.add_bad_request_violation(
49+
//! "field_a",
50+
//! "description of why the field_a is invalid"
51+
//! );
52+
//! }
53+
//!
54+
//! # let other_condition = true;
55+
//! if other_condition {
56+
//! err_details.add_bad_request_violation(
57+
//! "field_b",
58+
//! "description of why the field_b is invalid",
59+
//! );
60+
//! }
61+
//!
62+
//! // Check if any error details were set and return error status if so
63+
//! if err_details.has_bad_request_violations() {
64+
//! // Add additional error details if necessary
65+
//! err_details
66+
//! .add_help_link("description of link", "https://resource.example.local")
67+
//! .set_localized_message("en-US", "message for the user");
68+
//!
69+
//! let status = Status::with_error_details(
70+
//! Code::InvalidArgument,
71+
//! "bad request",
72+
//! err_details,
73+
//! );
74+
//! return Err(status);
75+
//! }
76+
//!
77+
//! // Handle valid request
78+
//! // ...
79+
//! # Ok(tonic::Response::new(()))
80+
//! # }
81+
//! ```
82+
//!
83+
//! ## Client Side: Extracting an [`ErrorDetails`] struct from `tonic::Status`
84+
//!
85+
//! ```
86+
//! use tonic::{Response, Status};
87+
//! use tonic_types::StatusExt;
88+
//!
89+
//! // ...
90+
//! // Where `req_result` was returned by a gRPC client endpoint method
91+
//! fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
92+
//! match req_result {
93+
//! Ok(response) => {
94+
//! // Handle successful response
95+
//! },
96+
//! Err(status) => {
97+
//! let err_details = status.get_error_details();
98+
//! if let Some(bad_request) = err_details.bad_request() {
99+
//! // Handle bad_request details
100+
//! }
101+
//! if let Some(help) = err_details.help() {
102+
//! // Handle help details
103+
//! }
104+
//! if let Some(localized_message) = err_details.localized_message() {
105+
//! // Handle localized_message details
106+
//! }
107+
//! }
108+
//! };
109+
//! }
110+
//! ```
111+
//!
112+
//! # Working with different error message types
113+
//!
114+
//! Multiple examples are provided at the [`ErrorDetails`] doc. Instructions
115+
//! about how to use the fields of the standard error message types correctly
116+
//! are provided at [error_details.proto].
117+
//!
118+
//! # Alternative `tonic::Status` associated functions and methods
119+
//!
120+
//! In the [`StatusExt`] doc, an alternative way of interacting with
121+
//! [`tonic::Status`] is presented, using vectors of error details structs
122+
//! wrapped with the [`ErrorDetail`] enum. This approach can provide more
123+
//! control over the vector of standard error messages that will be generated or
124+
//! that was received, if necessary. To see how to adopt this approach, please
125+
//! check the [`StatusExt::with_error_details_vec`] and
126+
//! [`StatusExt::get_error_details_vec`] docs, and also the main repo's
127+
//! [Richer Error example] directory.
128+
//!
129+
//! Besides that, multiple examples with alternative error details extraction
130+
//! methods are provided in the [`StatusExt`] doc, which can be specially
131+
//! useful if only one type of standard error message is being handled by the
132+
//! client. For example, using [`StatusExt::get_details_bad_request`] is a
133+
//! more direct way of extracting a [`BadRequest`] error message from
134+
//! [`tonic::Status`].
135+
//!
7136
//! [`tonic::Status`]: https://docs.rs/tonic/latest/tonic/struct.Status.html
8137
//! [`tonic`]: https://docs.rs/tonic/latest/tonic/
9138
//! [gRPC Richer Error Model]: https://www.grpc.io/docs/guides/error/
139+
//! [examples]: https://github.com/hyperium/tonic/tree/master/examples
140+
//! [error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
141+
//! [Richer Error example]: https://github.com/hyperium/tonic/tree/master/examples/src/richer-error
10142
11143
#![warn(
12144
missing_debug_implementations,

0 commit comments

Comments
 (0)
Please sign in to comment.