Include README in crate doctests and improve documentation (#8)

* chore: Improve docs and include some examples in doctests
reqwest-tracing-0.2
tl-rodrigo-gryzinski 2021-09-28 19:26:03 +01:00 committed by GitHub
parent e0b01383d6
commit b8645f81eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 124 additions and 71 deletions

View File

@ -8,11 +8,29 @@ to allow for client middleware chains.
[![CI](https://github.com/TrueLayer/reqwest-middleware/workflows/CI/badge.svg)](https://github.com/TrueLayer/reqwest-middleware/actions)
[![Coverage Status](https://coveralls.io/repos/github/TrueLayer/reqwest-middleware/badge.svg?branch=main&t=YKhONc)](https://coveralls.io/github/TrueLayer/reqwest-middleware?branch=main)
This crate provides functionality for building and running middleware but no middleware
implementations. This repository also contains a couple of useful concrete middleware crates:
* [`reqwest-retry`](https://crates.io/crates/reqwest-retry): retry failed requests.
* [`reqwest-trcing`](https://crates.io/crates/reqwest-tracing):
[`tracing`](https://crates.io/crates/tracing) integration, optional opentelemetry support.
## Overview
The `reqwest-middleware` client exposes the same interface as a plain `reqwest` client, but
`ClientBuilder` exposes functionality to attach middleware:
```toml
# Cargo.toml
# ...
[dependencies]
reqwest = "0.11"
reqwest-middleware = "0.1.1"
reqwest-retry = "0.1.1"
reqwest-tracing = "0.1.2"
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
```
```rust
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
@ -20,35 +38,27 @@ use reqwest_tracing::TracingMiddleware;
#[tokio::main]
async fn main() {
    let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
    let client = ClientBuilder::new(reqwest::Client::new())
        .with(TracingMiddleware)
        .with(RetryTransientMiddleware::new_with_policy(retry_policy))
        .build();
    run(client).await;
// Retry up to 3 times with increasing intervals between attempts.
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
let client = ClientBuilder::new(reqwest::Client::new())
// Trace HTTP requests. See the tracing crate to make use of these traces.
.with(TracingMiddleware)
// Retry failed requests.
.with(RetryTransientMiddleware::new_with_policy(retry_policy))
.build();
run(client).await;
}
async fn run(client: ClientWithMiddleware) {
    // free retries!
    client
        .get("https://some-external-service.com")
        .header("foo", "bar")
        .send()
        .await
        .unwrap();
client
.get("https://truelayer.com")
.header("foo", "bar")
.send()
.await
.unwrap();
}
```
## How to install
Add `reqwest-middleware` to your dependencies
```toml
[dependencies]
# ...
reqwest-middleware = "0.1.0"
```
#### License
<sup>

View File

@ -21,5 +21,8 @@ thiserror = "1"
truelayer-extensions = "0.1"
[dev-dependencies]
reqwest = "0.11"
reqwest-retry = { path = "../reqwest-retry" }
reqwest-tracing = { path = "../reqwest-tracing" }
wiremock = "0.5"
tokio = { version = "1", features = ["macros"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

View File

@ -13,7 +13,7 @@ use crate::middleware::{Middleware, Next};
/// A `ClientBuilder` is used to build a [`ClientWithMiddleware`].
///
/// [`ClientWithMiddleware`]: struct.ClientWithMiddleware.html
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
pub struct ClientBuilder {
client: Client,
middleware_stack: Vec<Arc<dyn Middleware>>,
@ -31,7 +31,7 @@ impl ClientBuilder {
///
/// If you need to keep a reference to the middleware after attaching, use [`with_arc`].
///
/// [`with_arc`]: #method.with_arc
/// [`with_arc`]: Self::with_arc
pub fn with<M>(self, middleware: M) -> Self
where
M: Middleware,
@ -41,7 +41,7 @@ impl ClientBuilder {
/// Add middleware to the chain. [`with`] is more ergonomic if you don't need the `Arc`.
///
/// [`with`]: #method.with
/// [`with`]: Self::with
pub fn with_arc(mut self, middleware: Arc<dyn Middleware>) -> Self {
self.middleware_stack.push(middleware);
self
@ -55,8 +55,6 @@ impl ClientBuilder {
/// `ClientWithMiddleware` is a wrapper around [`reqwest::Client`] which runs middleware on every
/// request.
///
/// [`reqwest::Client`]: https://docs.rs/reqwest/0.10.8/reqwest/struct.Client.html
#[derive(Clone)]
pub struct ClientWithMiddleware {
inner: reqwest::Client,
@ -65,8 +63,6 @@ pub struct ClientWithMiddleware {
impl ClientWithMiddleware {
/// See [`ClientBuilder`] for a more ergonomic way to build `ClientWithMiddleware` instances.
///
/// [`ClientBuilder`]: struct.ClientBuilder.html
pub fn new<T>(client: Client, middleware_stack: T) -> Self
where
T: Into<Box<[Arc<dyn Middleware>]>>,
@ -77,40 +73,37 @@ impl ClientWithMiddleware {
}
}
/// See [`Client::get`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.get)
/// See [`Client::get`]
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::GET, url)
}
/// See [`Client::post`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.post)
/// See [`Client::post`]
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::POST, url)
}
/// See [`Client::put`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.put)
/// See [`Client::put`]
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::PUT, url)
}
/// See
/// [`Client::patch`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.patch)
/// See [`Client::patch`]
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::PATCH, url)
}
/// See
/// [`Client::delete`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.delete)
/// See [`Client::delete`]
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::DELETE, url)
}
/// See [`Client::head`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.head)
/// See [`Client::head`]
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::HEAD, url)
}
/// See
/// [`Client::request`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.request)
/// See [`Client::request`]
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
RequestBuilder {
inner: self.inner.request(method, url),
@ -118,8 +111,7 @@ impl ClientWithMiddleware {
}
}
/// See
/// [`Client::execute`](https://docs.rs/reqwest/latest/reqwest/struct.Client.html#method.execute)
/// See [`Client::execute`]
pub async fn execute(&self, req: Request) -> Result<Response> {
let mut ext = Extensions::new();
self.execute_with_extensions(req, &mut ext).await
@ -147,8 +139,6 @@ impl From<Client> for ClientWithMiddleware {
}
/// This is a wrapper around [`reqwest::RequestBuilder`] exposing the same API.
///
/// [`reqwest::RequestBuilder`]: https://docs.rs/reqwest/0.10.8/reqwest/struct.RequestBuilder.html
#[must_use = "RequestBuilder does nothing until you 'send' it"]
pub struct RequestBuilder {
inner: reqwest::RequestBuilder,

View File

@ -37,11 +37,18 @@
//! }
//! ```
//!
//! [`build`]: struct.ClientBuilder.html#method.build
//! [`ClientBuilder`]: struct.ClientBuilder.html
//! [`ClientWithMiddleware`]: struct.ClientWithMiddleware.html
//! [`reqwest::Client`]: https://docs.rs/reqwest/0.10.8/reqwest/struct.Client.html
//! [`with`]: struct.ClientBuilder.html#method.with
//! [`build`]: ClientBuilder::build
//! [`ClientBuilder`]: ClientBuilder
//! [`ClientWithMiddleware`]: ClientWithMiddleware
//! [`with`]: ClientBuilder::with
// Test README examples without overriding module docs.
// We want to keep the in-code docs separate as those allow for automatic linking to crate
// documentation.
#[doc = include_str!("../../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;
mod client;
mod error;
mod middleware;

View File

@ -30,9 +30,8 @@ use crate::error::{Error, Result};
/// }
/// ```
///
/// [`ClientWithMiddleware`]: struct.ClientWithMiddleware.html
/// [`Extensions`]: TODO
/// [`with`]: struct.ClientBuilder.html#method.with
/// [`ClientWithMiddleware`]: crate::ClientWithMiddleware
/// [`with`]: crate::ClientBuilder::with
#[async_trait::async_trait]
pub trait Middleware: 'static + Send + Sync {
/// Invoked with a request before sending it. If you want to continue processing the request,
@ -69,8 +68,8 @@ where
/// Next encapsulates the remaining middleware chain to run in [`Middleware::handle`]. You can
/// forward the request down the chain with [`run`].
///
/// [`Middleware::handle`]: trait.Middleware.html#tymethod.handle
/// [`run`]: #method.run
/// [`Middleware::handle`]: Middleware::handle
/// [`run`]: Self::run
#[derive(Clone)]
pub struct Next<'a> {
client: &'a Client,

View File

@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Re-export `RetryPolicy` from the crate root.
### Changed
- Disabled default features on `reqwest`

View File

@ -15,15 +15,7 @@ Build `RetryTransientMiddleware` from a `RetryPolicy`, then attach it to a
[`retry-policies::policies`](https://crates.io/crates/retry-policies) is reexported under
`reqwest_retry::policies` for convenience.
## How to install
Add `reqwest-retry` to your dependencies
```toml
[dependencies]
# ...
reqwest-retry = "0.1.0"
```
See [`reqwest_middleware`](https://docs.rs/reqwest_middleware) for usage with reqwest.
#### License

View File

@ -1,7 +1,34 @@
//! Middleware to retry failed HTTP requests built on [`reqwest_middleware`].
//!
//! Use [`RetryTransientMiddleware`] to retry failed HTTP requests. Retry control flow is managed
//! by a [`RetryPolicy`].
//!
//! ## Example
//!
//! ```
//! use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
//! use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
//!
//! async fn run_retries() {
//! // Retry up to 3 times with increasing intervals between attempts.
//! let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
//! let client = ClientBuilder::new(reqwest::Client::new())
//! .with(RetryTransientMiddleware::new_with_policy(retry_policy))
//! .build();
//!
//! client
//! .get("https://truelayer.com")
//! .header("foo", "bar")
//! .send()
//! .await
//! .unwrap();
//! }
//! ```
mod middleware;
mod retryable;
pub use retry_policies::policies;
pub use retry_policies::{policies, RetryPolicy};
pub use middleware::RetryTransientMiddleware;
pub use retryable::Retryable;

View File

@ -12,8 +12,23 @@ Opentracing middleware implementation for
Attach `TracingMiddleware` to your client to automatically trace HTTP requests:
```rust
use opentelemetry::exporter::trace::stdout;
```toml
# Cargo.toml
# ...
[dependencies]
opentelemetry = "0.16"
reqwest = "0.11"
reqwest-middleware = "0.1.1"
reqwest-retry = "0.1.1"
reqwest-tracing = { version = "0.1.2", features = ["opentelemetry_0_16"] }
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
tracing = "0.1"
tracing-opentelemetry = "0.15"
tracing-subscriber = "0.2"
```
```rust,skip
use opentelemetry::sdk::export::trace::stdout;
use reqwest_middleware::ClientBuilder;
use reqwest_tracing::TracingMiddleware;
use tracing_subscriber::layer::SubscriberExt;
@ -21,7 +36,7 @@ use tracing_subscriber::Registry;
#[tokio::main]
async fn main() {
let (tracer, _) = stdout::new_pipeline().install();
let tracer = stdout::new_pipeline().install_simple();
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
let subscriber = Registry::default().with(telemetry);
tracing::subscriber::set_global_default(subscriber).unwrap();
@ -29,15 +44,20 @@ async fn main() {
run().await;
}
async fun run() {
async fn run() {
let client = ClientBuilder::new(reqwest::Client::new())
.with(TracingMiddleware)
.build();`
.build();
client.get("https://truelayer.com").send().await.unwrap();
}
```
```terminal
$ cargo run
SpanData { span_context: SpanContext { trace_id: ...
```
See the [`tracing`](https://crates.io/crates/tracing) crate for more information on how to set up a
tracing subscriber to make use of the spans.

View File

@ -1,3 +1,7 @@
//! Opentracing middleware implementation for [`reqwest-middleware`].
//!
//! Attach [`TracingMiddleware`] to your client to automatically trace HTTP requests.
mod middleware;
#[cfg(any(
feature = "opentelemetry_0_13",