Skip to content

Commit

Permalink
Use builders for api clients (#77)
Browse files Browse the repository at this point in the history
* Use builders for api clients

- Use builders for api clients
- Add request timeout to reqwest client

* fix async tests

* export ureq

* fix default value for reqwest client

* fix client in async tests

* address code review comments

* address comments

* add comments

* bump version

* fix changelog entry

* Update src/api/async_telegram_api_impl.rs

Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>

* Update src/api/async_telegram_api_impl.rs

Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>

* Update src/api/telegram_api_impl.rs

Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>

* Update src/api/telegram_api_impl.rs

Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>

Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>
  • Loading branch information
ayrat555 and EdJoPaTo authored Jul 6, 2022
1 parent 8fd398f commit 8d85180
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.19.0 (2022-07-06)

* Add builders for api clients [#77](https://github.com/ayrat555/frankenstein/pull/77)

## 0.18.0 (2022-06-21)
### [Bot API 6.1](https://core.telegram.org/bots/api-changelog#june-20-2022) - [#73](https://github.com/ayrat555/frankenstein/pull/73)

Expand Down
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "frankenstein"
version = "0.18.0"
version = "0.19.0"
authors = ["Ayrat Badykov <[email protected]>"]
description = "Telegram bot API client for Rust"
edition = "2018"
Expand All @@ -26,6 +26,10 @@ required-features = ["http-client"]
name = "inline_keyboard"
required-features = ["http-client"]

[[example]]
name = "custom_client"
required-features = ["http-client"]

[[example]]
name = "async_get_me"
required-features = ["async-http-client"]
Expand All @@ -38,6 +42,10 @@ required-features = ["async-http-client"]
name = "async_file_upload"
required-features = ["async-http-client"]

[[example]]
name = "async_custom_client"
required-features = ["async-http-client"]

[[example]]
name = "api_trait_implementation"
required-features = ["telegram-trait"]
Expand Down
47 changes: 42 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Add this to your Cargo.toml

```toml
[dependencies]
frankenstein = "0.18"
frankenstein = "0.19"
```

## Features
Expand All @@ -35,13 +35,13 @@ frankenstein = "0.18"
To use the async client add the following line to your `Cargo.toml` file:

```toml
frankenstein = { version = "0.18", default-features = false, features = ["async-http-client"] }
frankenstein = { version = "0.19", default-features = false, features = ["async-http-client"] }
```

You can also disable all features:

```toml
frankenstein = { version = "0.18", default-features = false }
frankenstein = { version = "0.19", default-features = false }
```

In this case the crate will ship only with telegram types
Expand Down Expand Up @@ -156,9 +156,46 @@ It has two variants:
- `File::InputFile` is used to upload a new file using multipart upload.


### Customizing http clients

Both the async (`reqwest`) and the blocking (`ureq`) HTTP clients can be customized with their builders.

Customizing the blocking client:

```rust
use frankenstein::ureq;
use frankenstein::Api;
use std::time::Duration;

let request_agent = ureq::builder().timeout(Duration::from_secs(100)).build();
let api_url = format!("{}{}", BASE_API_URL, TOKEN);

Api::builder()
.api_url(api_url)
.request_agent(request_agent)
.build()
```

Customizing the async client:

```rust
use frankenstein::reqwest;
use frankenstein::AsyncApi;
use std::time::Duration;

let client = reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(100))
.timeout(Duration::from_secs(100))
.build()
.unwrap();
let api_url = format!("{}{}", BASE_API_URL, TOKEN);

AsyncApi::builder().api_url(api_url).client(client).build()
```

### Documentation

Frankenstein implements all telegram bot api methods. To see which parameters you should pass, check [docs.rs](https://docs.rs/frankenstein/0.18.0/frankenstein/api_traits/telegram_api/trait.TelegramApi.html#provided-methods)
Frankenstein implements all telegram bot api methods. To see which parameters you should pass, check [docs.rs](https://docs.rs/frankenstein/0.19.0/frankenstein/api_traits/telegram_api/trait.TelegramApi.html#provided-methods)

You can check out a real world bot created using this library - [El Monitorro](https://github.com/ayrat555/el_monitorro). El Monitorro is a feed reader bot.

Expand All @@ -170,7 +207,7 @@ The library uses `ureq` http client by default, but it can be easily replaced wi
1. `ureq` comes with a default feature (`impl`). So the feature should be disabled:

```toml
frankenstein = { version = "0.18", default-features = false, features = ["telegram-trait"] }
frankenstein = { version = "0.19", default-features = false, features = ["telegram-trait"] }
```

2. Implement `TelegramApi` trait which requires two functions:
Expand Down
37 changes: 37 additions & 0 deletions examples/async_custom_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use frankenstein::reqwest;
use frankenstein::AsyncApi;
use frankenstein::AsyncTelegramApi;
use std::time::Duration;

static TOKEN: &str = "API_TOKEN";
static BASE_API_URL: &str = "https://api.telegram.org/bot";

#[tokio::main]
async fn main() {
let api = custom_client();

match api.get_me().await {
Ok(response) => {
let user = response.result;
println!(
"Hello, I'm @{}, https://t.me/{}",
user.first_name,
user.username.expect("The bot must have a username.")
);
}
Err(error) => {
eprintln!("Failed to get me: {:?}", error);
}
}
}

fn custom_client() -> AsyncApi {
let client = reqwest::ClientBuilder::new()
.connect_timeout(Duration::from_secs(100))
.timeout(Duration::from_secs(100))
.build()
.unwrap();
let api_url = format!("{}{}", BASE_API_URL, TOKEN);

AsyncApi::builder().api_url(api_url).client(client).build()
}
35 changes: 35 additions & 0 deletions examples/custom_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use frankenstein::ureq;
use frankenstein::Api;
use frankenstein::TelegramApi;
use std::time::Duration;

static TOKEN: &str = "API_TOKEN";
static BASE_API_URL: &str = "https://api.telegram.org/bot";

fn main() {
let api = custom_client();

match api.get_me() {
Ok(response) => {
let user = response.result;
println!(
"Hello, I'm @{}, https://t.me/{}",
user.first_name,
user.username.expect("The bot must have a username.")
);
}
Err(error) => {
eprintln!("Failed to get me: {:?}", error);
}
}
}

fn custom_client() -> Api {
let request_agent = ureq::builder().timeout(Duration::from_secs(100)).build();
let api_url = format!("{}{}", BASE_API_URL, TOKEN);

Api::builder()
.api_url(api_url)
.request_agent(request_agent)
.build()
}
24 changes: 13 additions & 11 deletions src/api/async_telegram_api_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,30 @@ use async_trait::async_trait;
use reqwest::multipart;
use serde_json::Value;
use std::path::PathBuf;
use std::time::Duration;
use tokio::fs::File;
use typed_builder::TypedBuilder;

#[derive(Debug, Clone)]
#[derive(Debug, Clone, TypedBuilder)]
pub struct AsyncApi {
#[builder(setter(into))]
pub api_url: String,
client: reqwest::Client,
#[builder(
default_code = "reqwest::ClientBuilder::new().connect_timeout(Duration::from_secs(10)).timeout(Duration::from_secs(10)).build().unwrap()"
)]
pub client: reqwest::Client,
}

impl AsyncApi {
/// Create a new `AsyncApi`. You can use `AsyncApi::builder()` for more options.
pub fn new(api_key: &str) -> Self {
let api_url = format!("{}{}", super::BASE_API_URL, api_key);
let client = reqwest::Client::new();
Self { api_url, client }
Self::builder().api_url(api_url).build()
}

pub const fn new_with_client(client: reqwest::Client, api_url: String) -> Self {
Self { api_url, client }
}

pub fn new_url(api_url: String) -> Self {
let client = reqwest::Client::new();
Self { api_url, client }
/// Create a new `AsyncApi`. You can use `AsyncApi::builder()` for more options.
pub fn new_url<T: Into<String>>(api_url: T) -> Self {
Self::builder().api_url(api_url).build()
}

pub fn encode_params<T: serde::ser::Serialize + std::fmt::Debug>(
Expand Down
29 changes: 10 additions & 19 deletions src/api/telegram_api_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,27 @@ use multipart::client::lazy::Multipart;
use serde_json::Value;
use std::path::PathBuf;
use std::time::Duration;
use typed_builder::TypedBuilder;
use ureq::Response;

#[derive(Debug, Clone)]
#[derive(Debug, Clone, TypedBuilder)]
pub struct Api {
#[builder(setter(into))]
pub api_url: String,
request_agent: ureq::Agent,
#[builder(default_code = "ureq::builder().timeout(Duration::from_secs(10)).build()")]
pub request_agent: ureq::Agent,
}

impl Api {
/// Create a new `Api`. You can use `Api::builder()` for more options.
pub fn new(api_key: &str) -> Self {
let api_url = format!("{}{}", super::BASE_API_URL, api_key);

let request_agent = ureq::builder().timeout(Duration::from_secs(60)).build();
Self {
api_url,
request_agent,
}
}

pub fn new_url(api_url: String) -> Self {
let request_agent = ureq::builder().timeout(Duration::from_secs(60)).build();
Self {
api_url,
request_agent,
}
Self::builder().api_url(api_url).build()
}

pub fn with_timeout(&mut self, timeout: Duration) {
let request_agent = ureq::builder().timeout(timeout).build();
self.request_agent = request_agent;
/// Create a new `Api`. You can use `Api::builder()` for more options.
pub fn new_url<T: Into<String>>(api_url: T) -> Self {
Api::builder().api_url(api_url).build()
}

pub fn encode_params<T: serde::ser::Serialize + std::fmt::Debug>(
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub use api_traits::*;
#[cfg(feature = "async-http-client")]
pub use reqwest;

#[doc(hidden)]
#[cfg(feature = "http-client")]
pub use ureq;

pub mod api_params;
pub mod objects;
mod parse_mode;
Expand Down

0 comments on commit 8d85180

Please sign in to comment.