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#[derive(Clone)]
14pub struct ApiKeysSvc(pub(crate) Arc<Config>);
15
16impl ApiKeysSvc {
17 #[maybe_async::maybe_async]
21 #[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 #[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 #[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 #[must_use]
77 #[derive(Debug, Clone, Serialize)]
78 pub struct CreateApiKeyOptions {
79 name: String,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
86 permission: Option<Permission>,
87 #[serde(skip_serializing_if = "Option::is_none")]
90 domain_id: Option<DomainId>,
91 }
92
93 impl CreateApiKeyOptions {
94 #[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 #[inline]
108 pub const fn with_full_access(mut self) -> Self {
109 self.permission = Some(Permission::FullAccess);
110 self
111 }
112
113 #[inline]
115 pub const fn with_sending_access(mut self) -> Self {
116 self.permission = Some(Permission::SendingAccess);
117 self
118 }
119
120 #[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 #[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 #[must_use]
144 #[derive(Debug, Clone, Deserialize)]
145 pub struct ApiKeyToken {
146 pub id: ApiKeyId,
148 pub token: String,
150 }
151
152 #[must_use]
154 #[derive(Debug, Clone, Deserialize)]
155 pub struct ApiKey {
156 pub id: ApiKeyId,
158 pub name: String,
160 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 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 let api_keys = resend.api_keys.list(ListOptions::default()).await?;
186 let api_keys_amt = api_keys.len();
187
188 resend.api_keys.delete(&id).await?;
190
191 let api_keys = resend.api_keys.list(ListOptions::default()).await?;
193 assert!(api_keys_amt == api_keys.len() + 1);
194
195 Ok(())
196 }
197}