resend_rs/
api_keys.rs

1use std::fmt;
2use std::sync::Arc;
3
4use reqwest::Method;
5
6use crate::{Config, Result};
7use crate::{
8    list_opts::{ListOptions, ListResponse},
9    types::{ApiKey, ApiKeyToken, CreateApiKeyOptions},
10};
11
12/// `Resend` APIs for `/api-keys` endpoints.
13#[derive(Clone)]
14pub struct ApiKeysSvc(pub(crate) Arc<Config>);
15
16impl ApiKeysSvc {
17    /// Add a new API key to authenticate communications with Resend.
18    ///
19    /// <https://resend.com/docs/api-reference/api-keys/create-api-key>
20    #[maybe_async::maybe_async]
21    // Reasoning for allow: https://github.com/resend/resend-rust/pull/1#issuecomment-2081646115
22    #[allow(clippy::needless_pass_by_value)]
23    pub async fn create(&self, api_key: CreateApiKeyOptions) -> Result<ApiKeyToken> {
24        let request = self.0.build(Method::POST, "/api-keys");
25        let response = self.0.send(request.json(&api_key)).await?;
26        let content = response.json::<ApiKeyToken>().await?;
27
28        Ok(content)
29    }
30
31    /// Retrieve a list of API keys for the authenticated user.
32    ///
33    /// - Default limit: no limit (return everything)
34    ///
35    /// <https://resend.com/docs/api-reference/api-keys/list-api-keys>
36    #[maybe_async::maybe_async]
37    #[allow(clippy::needless_pass_by_value)]
38    pub async fn list<T>(&self, list_opts: ListOptions<T>) -> Result<ListResponse<ApiKey>> {
39        let request = self.0.build(Method::GET, "/api-keys").query(&list_opts);
40        let response = self.0.send(request).await?;
41        let content = response.json::<ListResponse<ApiKey>>().await?;
42
43        Ok(content)
44    }
45
46    /// Remove an existing API key.
47    ///
48    /// <https://resend.com/docs/api-reference/api-keys/delete-api-key>
49    #[maybe_async::maybe_async]
50    #[allow(clippy::needless_pass_by_value)]
51    pub async fn delete(&self, api_key_id: &str) -> Result<()> {
52        let path = format!("/api-keys/{api_key_id}");
53
54        let request = self.0.build(Method::DELETE, &path);
55        let _response = self.0.send(request).await?;
56
57        Ok(())
58    }
59}
60
61impl fmt::Debug for ApiKeysSvc {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        fmt::Debug::fmt(&self.0, f)
64    }
65}
66
67#[allow(unreachable_pub)]
68pub mod types {
69    use serde::{Deserialize, Serialize};
70
71    use crate::types::DomainId;
72
73    crate::define_id_type!(ApiKeyId);
74
75    /// Name and permissions of the new [`ApiKey`].
76    #[must_use]
77    #[derive(Debug, Clone, Serialize)]
78    pub struct CreateApiKeyOptions {
79        /// The API key name.
80        name: String,
81
82        /// The API key can have full access to Resend’s API or be only restricted to send emails.
83        /// * `full_access` - Can create, delete, get, and update any resource.
84        /// * `sending_access` - Can only send emails.
85        #[serde(skip_serializing_if = "Option::is_none")]
86        permission: Option<Permission>,
87        /// Restrict an API key to send emails only from a specific domain.
88        /// Only used when the permission is `sending_access`.
89        #[serde(skip_serializing_if = "Option::is_none")]
90        domain_id: Option<DomainId>,
91    }
92
93    impl CreateApiKeyOptions {
94        /// Creates a new [`CreateApiKeyOptions`].
95        #[inline]
96        pub fn new(name: &str) -> Self {
97            Self {
98                name: name.to_owned(),
99                permission: None,
100                domain_id: None,
101            }
102        }
103
104        /// The API key can have full access to Resend’s API or be only restricted to send emails.
105        /// * `full_access` - Can create, delete, get, and update any resource.
106        /// * `sending_access` - Can only send emails.
107        #[inline]
108        pub const fn with_full_access(mut self) -> Self {
109            self.permission = Some(Permission::FullAccess);
110            self
111        }
112
113        /// Restricts an API key to only sending emails
114        #[inline]
115        pub const fn with_sending_access(mut self) -> Self {
116            self.permission = Some(Permission::SendingAccess);
117            self
118        }
119
120        /// Restricts an API key to send emails only from a specific domain.
121        #[inline]
122        pub fn with_domain_access(mut self, domain_id: &DomainId) -> Self {
123            self.permission = Some(Permission::SendingAccess);
124            self.domain_id = Some(domain_id.clone());
125            self
126        }
127    }
128
129    /// Full or restricted access of the [`ApiKey`].
130    ///
131    /// * `full_access` - Can create, delete, get, and update any resource.
132    /// * `sending_access` - Can only send emails.
133    #[must_use]
134    #[derive(Debug, Copy, Clone, Serialize)]
135    pub enum Permission {
136        #[serde(rename = "full_access")]
137        FullAccess,
138        #[serde(rename = "sending_access")]
139        SendingAccess,
140    }
141
142    /// Token and ID of the newly created [`ApiKey`].
143    #[must_use]
144    #[derive(Debug, Clone, Deserialize)]
145    pub struct ApiKeyToken {
146        /// The ID of the API key.
147        pub id: ApiKeyId,
148        /// The token of the API key.
149        pub token: String,
150    }
151
152    /// Name and ID of an existing API key.
153    #[must_use]
154    #[derive(Debug, Clone, Deserialize)]
155    pub struct ApiKey {
156        /// The ID of the API key.
157        pub id: ApiKeyId,
158        /// The name of the API key.
159        pub name: String,
160        /// The date and time the API key was created in ISO8601 format.
161        pub created_at: String,
162    }
163}
164
165#[cfg(test)]
166#[allow(clippy::needless_return)]
167mod test {
168    use crate::list_opts::ListOptions;
169    use crate::test::{CLIENT, DebugResult};
170    use crate::types::CreateApiKeyOptions;
171
172    #[tokio_shared_rt::test(shared = true)]
173    #[cfg(not(feature = "blocking"))]
174    async fn all() -> DebugResult<()> {
175        let resend = &*CLIENT;
176
177        let api_key = "test_";
178
179        // Create.
180        let request = CreateApiKeyOptions::new(api_key).with_full_access();
181        let response = resend.api_keys.create(request).await?;
182        let id = response.id;
183
184        // List.
185        let api_keys = resend.api_keys.list(ListOptions::default()).await?;
186        let api_keys_amt = api_keys.len();
187
188        // Delete.
189        resend.api_keys.delete(&id).await?;
190
191        // List.
192        let api_keys = resend.api_keys.list(ListOptions::default()).await?;
193        assert!(api_keys_amt == api_keys.len() + 1);
194
195        Ok(())
196    }
197}