-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(all): expand #1
Merged
Merged
Changes from 12 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
0033b92
.gitignore editors
martsokha 3595a25
.gitignore openapi gen
martsokha bce302d
services, meta, openapi gen types
martsokha 9342bc4
api keys, tests
martsokha ed39137
api keys types
martsokha 7934c1b
blocking
martsokha edd6884
base_url support
martsokha e84ac5f
restore config.toml, must_use, inline
martsokha 6807f0e
the rest
martsokha 84ac97f
update docs, builder
martsokha 5a9e7ac
support user-agent
martsokha 6e7b231
dependabot.yaml
martsokha d5d0229
example, readme, typo
martsokha c61aa2b
error type
martsokha b7d1f61
before maybe_async
martsokha c1ef2b7
folder mods
martsokha 231bfb9
error handling for the rest
martsokha d9ea634
example, docs, rem tokio
martsokha f6285c6
revert rem tokio
martsokha f674cfd
docs, types
martsokha 51874b6
types, contacts test
martsokha f390301
Fix doctest
AntoniosBarotsis f4706c8
Temporarily fix test rate limits
AntoniosBarotsis c3e6a23
Add resend api key
AntoniosBarotsis ccf9bd4
Fix key
AntoniosBarotsis fc8ed42
types
martsokha e83e10e
Merge remote-tracking branch 'origin/main2' into main2
martsokha a4f014f
id types
martsokha fdb10be
contactid new, typo
martsokha cf3cb49
feature time, renames, docs
martsokha 79df52e
revert region, rem time
martsokha 0406826
rem feat time, rem domainreply, fix email
martsokha 5ab313b
rem time
martsokha 3212abe
cargo fmt
martsokha 53fac63
rate limit, rename svc
martsokha 814dddb
pedantic
martsokha b5b9422
revert config::send
martsokha 49973fc
env var RESEND_RATE_LIMIT
martsokha 028bc99
fmt, clippy
martsokha d07ff94
typo, String to &str
martsokha e269b99
clippy pedantic
martsokha 139e3f0
rate limit test
martsokha 389f741
rem print, import
martsokha 6209ba8
rem limit
martsokha 8edca52
consts
martsokha 8e90941
docs, fix contacts::delete_by_*
martsokha d6504c2
oops
martsokha cb70dcc
Update email (oops)
AntoniosBarotsis 220a801
Run clippy on all features again
AntoniosBarotsis f1ff555
Ignore some warnings
AntoniosBarotsis 6c23134
Add v0.4.0
AntoniosBarotsis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
version: 2 | ||
updates: | ||
|
||
- package-ecosystem: "cargo" | ||
directory: "/" | ||
schedule: | ||
interval: "weekly" | ||
timezone: "Europe/Amsterdam" | ||
day: "friday" | ||
time: "18:00" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,23 @@ | ||
# OS | ||
Thumbs.db | ||
.DS_Store | ||
*.pdb | ||
|
||
# Editors | ||
.vs/ | ||
.vscode/ | ||
.idea/ | ||
.fleet/ | ||
|
||
# Generated by Cargo | ||
# will have compiled files and executables | ||
debug/ | ||
target/ | ||
|
||
gen/ | ||
|
||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries | ||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html | ||
Cargo.lock | ||
|
||
|
||
# These are backup files generated by rustfmt | ||
**/*.rs.bk | ||
|
||
# MSVC Windows builds of rustc generate these, which store debugging information | ||
*.pdb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,37 @@ | ||
# https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[package] | ||
name = "resend-rs" | ||
description = "A very minimal Resend client for sending emails." | ||
version = "0.3.0" | ||
version = "0.4.0" | ||
edition = "2021" | ||
repository = "https://github.com/AntoniosBarotsis/resend-rs" | ||
license = "GPL-3.0" | ||
readme = "./README.md" | ||
|
||
authors = ["Antonios Barotsis <[email protected]>"] | ||
repository = "https://github.com/AntoniosBarotsis/resend-rs" | ||
homepage = "https://github.com/AntoniosBarotsis/resend-rs" | ||
documentation = "https://docs.rs/resend-rs" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
categories = ["email", "web-programming"] | ||
keywords = ["email", "resend"] | ||
description = "A minimal Resend client." | ||
|
||
[package.metadata.docs.rs] | ||
all-features = false | ||
rustdoc-args = ["--cfg", "docsrs"] | ||
martsokha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
[features] | ||
async = [] | ||
default = ["native-tls"] | ||
|
||
blocking = ["reqwest/blocking"] | ||
native-tls = ["reqwest/native-tls"] | ||
rustls-tls = ["reqwest/rustls-tls"] | ||
|
||
[dependencies] | ||
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] } | ||
thiserror = "1.0.43" | ||
reqwest = { version = "0.12.3", default-features = false, features = ["json"] } | ||
serde = { version = "1.0.197", features = ["derive"] } | ||
serde_json = { version = "1.0.115", features = [] } | ||
thiserror = { version = "1.0.58", features = [] } | ||
|
||
[dev-dependencies] | ||
tokio = { version = "1.37.0", features = ["macros", "test-util", "rt-multi-thread"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,37 @@ | ||
# resend-rs | ||
## resend-rs | ||
|
||
[![Crates.io](https://img.shields.io/crates/v/resend-rs)](https://crates.io/crates/resend-rs) | ||
[![docs.rs](https://img.shields.io/docsrs/resend-rs)](https://docs.rs/resend-rs) | ||
[![Build Status][action-badge]][action-url] | ||
[![Crate Docs][docs-badge]][docs-url] | ||
[![Crate Version][crates-badge]][crates-url] | ||
|
||
A *very* minimal [Resend](https://resend.com) client for sending emails. | ||
A minimal [Resend](https://resend.com) client. | ||
|
||
Emails are sent via the [`resend_client::ResendClient`] which provides both a synchronous and | ||
asynchronous send method. The two are mutually exclusive and accessible via the `async` feature. The | ||
crate uses [`reqwest`](https://github.com/seanmonstar/reqwest) internally. | ||
[action-badge]: https://img.shields.io/github/actions/workflow/status/AntoniosBarotsis/resend-rs/ci.yml | ||
[action-url]: https://github.com/spire-rs/AntoniosBarotsis/resend-rs/workflows/build.yaml | ||
[crates-badge]: https://img.shields.io/crates/v/resend-rs | ||
[crates-url]: https://crates.io/crates/resend-rs | ||
[docs-badge]: https://img.shields.io/docsrs/resend-rs | ||
[docs-url]: https://docs.rs/resend-rs | ||
|
||
Currently, this only supports the `html` Resend parameter as I built this for my own use and | ||
that's all I need. If anyone else is looking into this, however, I would not mind expanding it. | ||
Emails are sent via the `Client` which provides both a synchronous and | ||
asynchronous send method. The two are mutually exclusive and accessible via the | ||
`blocking` feature. The crate uses [reqwest][reqwest] and [serde][serde] | ||
internally. | ||
|
||
[reqwest]: https://github.com/seanmonstar/reqwest | ||
[serde]: https://github.com/serde-rs/serde | ||
|
||
If anyone else is looking into this, however, I would not mind expanding it. | ||
|
||
#### Features | ||
|
||
- `blocking` to enable the blocking client. | ||
- `native-tls` to use system-native TLS. **Enabled by default**. | ||
- `rustls-tls` to use TLS backed by rustls . | ||
|
||
#### Variables | ||
|
||
- `RESEND_API_KEY` to enable `impl Default` for a `Client`. | ||
- `RESEND_BASE_URL` to override the default base address: | ||
`https://api.resend.com`. | ||
- `RESEND_USER_AGENT` to override the default user-agent: `resend-rs/0.1.0`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
tab_spaces = 2 | ||
# tab_spaces = 2 | ||
edition ="2021" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
use std::fmt; | ||
use std::sync::Arc; | ||
|
||
use reqwest::Method; | ||
|
||
use crate::types::{CreateApiKeyRequest, CreateApiKeyResponse, ListApiKeysResponse}; | ||
use crate::{Config, Result}; | ||
|
||
/// `Resend` APIs for `METHOD /api-keys` endpoints. | ||
#[derive(Clone)] | ||
pub struct ApiKeys(pub(crate) Arc<Config>); | ||
|
||
impl ApiKeys { | ||
/// Add a new API key to authenticate communications with Resend. | ||
/// | ||
/// <https://resend.com/docs/api-reference/api-keys/create-api-key> | ||
#[cfg(not(feature = "blocking"))] | ||
#[cfg_attr(docsrs, doc(cfg(not(feature = "blocking"))))] | ||
pub async fn create(&self, api_key: CreateApiKeyRequest) -> Result<CreateApiKeyResponse> { | ||
let request = self.0.build(Method::POST, "/api-keys"); | ||
let response = request.json(&api_key).send().await?; | ||
let content = response.json::<CreateApiKeyResponse>().await?; | ||
martsokha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Ok(content) | ||
} | ||
|
||
/// Retrieve a list of API keys for the authenticated user. | ||
/// | ||
/// <https://resend.com/docs/api-reference/api-keys/list-api-keys> | ||
#[cfg(not(feature = "blocking"))] | ||
#[cfg_attr(docsrs, doc(cfg(not(feature = "blocking"))))] | ||
pub async fn list(&self) -> Result<ListApiKeysResponse> { | ||
let request = self.0.build(Method::GET, "/api-keys"); | ||
let response = request.send().await?; | ||
let content = response.json::<ListApiKeysResponse>().await?; | ||
|
||
Ok(content) | ||
} | ||
|
||
/// Remove an existing API key. | ||
/// | ||
/// <https://resend.com/docs/api-reference/api-keys/delete-api-key> | ||
#[cfg(not(feature = "blocking"))] | ||
#[cfg_attr(docsrs, doc(cfg(not(feature = "blocking"))))] | ||
pub async fn delete(&self, api_key_id: &str) -> Result<()> { | ||
let path = format!("/api-keys/{api_key_id}"); | ||
|
||
let request = self.0.build(Method::DELETE, &path); | ||
let _response = request.send().await?; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Add a new API key to authenticate communications with Resend. | ||
/// | ||
/// <https://resend.com/docs/api-reference/api-keys/create-api-key> | ||
#[cfg(feature = "blocking")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))] | ||
pub fn create(&self, api_key: CreateApiKeyRequest) -> Result<CreateApiKeyResponse> { | ||
let request = self.0.build(Method::POST, "/api-keys"); | ||
let response = request.json(&api_key).send()?; | ||
let content = response.json::<CreateApiKeyResponse>()?; | ||
|
||
Ok(content) | ||
} | ||
|
||
/// Retrieve a list of API keys for the authenticated user. | ||
/// | ||
/// <https://resend.com/docs/api-reference/api-keys/list-api-keys> | ||
#[cfg(feature = "blocking")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))] | ||
pub fn list(&self) -> Result<ListApiKeysResponse> { | ||
let request = self.0.build(Method::GET, "/api-keys"); | ||
let response = request.send()?; | ||
let content = response.json::<ListApiKeysResponse>()?; | ||
|
||
Ok(content) | ||
} | ||
|
||
/// Remove an existing API key. | ||
/// | ||
/// <https://resend.com/docs/api-reference/api-keys/delete-api-key> | ||
#[cfg(feature = "blocking")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))] | ||
pub fn delete(&self, api_key_id: &str) -> Result<()> { | ||
let path = format!("/api-keys/{api_key_id}"); | ||
|
||
let request = self.0.build(Method::DELETE, &path); | ||
let _response = request.send()?; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl fmt::Debug for ApiKeys { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt::Debug::fmt(&self.0, f) | ||
} | ||
} | ||
|
||
pub mod types { | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[must_use] | ||
#[derive(Debug, Clone, Serialize)] | ||
pub struct CreateApiKeyRequest { | ||
/// The API key name. | ||
pub name: String, | ||
|
||
/// The API key can have full access to Resend’s API or be only restricted to send emails. | ||
/// * `full_access` - Can create, delete, get, and update any resource. | ||
/// * `sending_access` - Can only send emails. | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub permission: Option<Permission>, | ||
/// Restrict an API key to send emails only from a specific domain. | ||
/// Only used when the permission is `sending_access`. | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub domain_id: Option<String>, | ||
} | ||
|
||
impl CreateApiKeyRequest { | ||
/// Creates a new [`CreateApiKeyRequest`]. | ||
#[inline] | ||
pub fn new(name: &str) -> Self { | ||
Self { | ||
name: name.to_owned(), | ||
permission: None, | ||
domain_id: None, | ||
} | ||
} | ||
|
||
/// Allows an API key to create, delete, get, and update any resource. | ||
#[inline] | ||
pub fn with_full_access(mut self) -> Self { | ||
self.permission = Some(Permission::FullAccess); | ||
self | ||
} | ||
|
||
/// Restricts an API key to only sending emails | ||
#[inline] | ||
pub fn with_sending_access(mut self) -> Self { | ||
self.permission = Some(Permission::SendingAccess); | ||
self | ||
} | ||
|
||
/// Restricts an API key to send emails only from a specific domain. | ||
#[inline] | ||
pub fn with_domain_access(mut self, domain_id: &str) -> Self { | ||
self.permission = Some(Permission::SendingAccess); | ||
self.domain_id = Some(domain_id.to_owned()); | ||
self | ||
} | ||
} | ||
|
||
/// Full access to Resend’s API or restricted to only send emails. | ||
/// * `full_access` - Can create, delete, get, and update any resource. | ||
/// * `sending_access` - Can only send emails. | ||
#[must_use] | ||
#[derive(Debug, Copy, Clone, Serialize)] | ||
pub enum Permission { | ||
#[serde(rename = "full_access")] | ||
FullAccess, | ||
#[serde(rename = "sending_access")] | ||
SendingAccess, | ||
} | ||
|
||
#[must_use] | ||
#[derive(Debug, Clone, Deserialize)] | ||
pub struct CreateApiKeyResponse { | ||
/// The ID of the API key. | ||
pub id: String, | ||
/// The token of the API key. | ||
pub token: String, | ||
} | ||
|
||
#[must_use] | ||
#[derive(Debug, Clone, Deserialize)] | ||
pub struct ListApiKeysResponse { | ||
pub data: Vec<ApiKey>, | ||
} | ||
|
||
#[must_use] | ||
#[derive(Debug, Clone, Deserialize)] | ||
pub struct ApiKey { | ||
/// The ID of the API key. | ||
pub id: String, | ||
/// The name of the API key. | ||
pub name: String, | ||
/// The date and time the API key was created. | ||
pub created_at: String, | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to have a before-after conversion similar to what I had in v0.3. Since there was just 1 method before this PR this should be easy. I have a code example here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The weird formatting is a result of me using deno fmt for markdown files.
I'm a bit confused, do you want an example in the client documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Github likes showing 6 lines minimum because I specifically selected the "unreleased" line. Meant to add an example similar to this:
for the "hello world" example I had in the repo before, in case anyone that was using the crate already wants to upgrade. I can add this myself as well.