use std::fmt;
use std::sync::Arc;
use reqwest::Method;
use crate::types::{ApiKey, ApiKeyToken, CreateApiKeyOptions};
use crate::{Config, Result};
#[derive(Clone)]
pub struct ApiKeysSvc(pub(crate) Arc<Config>);
impl ApiKeysSvc {
#[maybe_async::maybe_async]
#[allow(clippy::needless_pass_by_value)]
pub async fn create(&self, api_key: CreateApiKeyOptions) -> Result<ApiKeyToken> {
let request = self.0.build(Method::POST, "/api-keys");
let response = self.0.send(request.json(&api_key)).await?;
let content = response.json::<ApiKeyToken>().await?;
Ok(content)
}
#[maybe_async::maybe_async]
pub async fn list(&self) -> Result<Vec<ApiKey>> {
let request = self.0.build(Method::GET, "/api-keys");
let response = self.0.send(request).await?;
let content = response.json::<types::ListApiKeyResponse>().await?;
Ok(content.data)
}
#[maybe_async::maybe_async]
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 = self.0.send(request).await?;
Ok(())
}
}
impl fmt::Debug for ApiKeysSvc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
pub mod types {
use std::{fmt, ops::Deref};
use ecow::EcoString;
use serde::{Deserialize, Serialize};
use crate::types::DomainId;
#[derive(Debug, Clone, Deserialize)]
pub struct ApiKeyId(EcoString);
impl ApiKeyId {
#[inline]
#[must_use]
pub fn new(id: &str) -> Self {
Self(EcoString::from(id))
}
}
impl Deref for ApiKeyId {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl AsRef<str> for ApiKeyId {
#[inline]
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl fmt::Display for ApiKeyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self, f)
}
}
#[must_use]
#[derive(Debug, Clone, Serialize)]
pub struct CreateApiKeyOptions {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission: Option<Permission>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain_id: Option<DomainId>,
}
impl CreateApiKeyOptions {
#[inline]
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
permission: None,
domain_id: None,
}
}
#[inline]
pub const fn with_full_access(mut self) -> Self {
self.permission = Some(Permission::FullAccess);
self
}
#[inline]
pub const fn with_sending_access(mut self) -> Self {
self.permission = Some(Permission::SendingAccess);
self
}
#[inline]
pub fn with_domain_access(mut self, domain_id: &DomainId) -> Self {
self.permission = Some(Permission::SendingAccess);
self.domain_id = Some(domain_id.clone());
self
}
}
#[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 ApiKeyToken {
pub id: ApiKeyId,
pub token: String,
}
#[must_use]
#[derive(Debug, Clone, Deserialize)]
pub struct ListApiKeyResponse {
pub data: Vec<ApiKey>,
}
#[must_use]
#[derive(Debug, Clone, Deserialize)]
pub struct ApiKey {
pub id: ApiKeyId,
pub name: String,
pub created_at: String,
}
}
#[cfg(test)]
mod test {
use crate::tests::CLIENT;
use crate::types::CreateApiKeyOptions;
use crate::{Resend, Result};
#[tokio::test]
#[cfg(not(feature = "blocking"))]
async fn all() -> Result<()> {
let resend = CLIENT.get_or_init(Resend::default);
let api_key = "test_";
let request = CreateApiKeyOptions::new(api_key).with_full_access();
let response = resend.api_keys.create(request).await?;
let id = response.id;
let api_keys = resend.api_keys.list().await?;
let api_keys_amt = api_keys.len();
resend.api_keys.delete(&id).await?;
let api_keys = resend.api_keys.list().await?;
assert!(api_keys_amt == api_keys.len() + 1);
Ok(())
}
}