1use serde::{Deserialize, Serialize};
6
7use crate::error::Result;
8use crate::DakeraClient;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CreateKeyRequest {
17 pub name: String,
19 pub scope: String,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub namespaces: Option<Vec<String>>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub expires_in_days: Option<u64>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct CreateKeyResponse {
32 pub key_id: String,
34 pub key: String,
36 pub name: String,
38 pub scope: String,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub namespaces: Option<Vec<String>>,
43 pub created_at: u64,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub expires_at: Option<u64>,
48 pub warning: String,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct KeyInfo {
55 pub key_id: String,
56 pub name: String,
57 pub scope: String,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub namespaces: Option<Vec<String>>,
60 pub created_at: u64,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub expires_at: Option<u64>,
63 pub active: bool,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ListKeysResponse {
69 pub keys: Vec<KeyInfo>,
70 pub total: usize,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct KeySuccessResponse {
76 pub success: bool,
77 pub message: String,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct RotateKeyResponse {
83 pub new_key: String,
85 pub key_id: String,
87 pub warning: String,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct ApiKeyUsageResponse {
94 pub key_id: String,
95 pub total_requests: u64,
96 pub successful_requests: u64,
97 pub failed_requests: u64,
98 pub rate_limited_requests: u64,
99 pub bytes_transferred: u64,
100 pub avg_latency_ms: f64,
101 #[serde(default)]
102 pub by_endpoint: Vec<EndpointUsageInfo>,
103 #[serde(default)]
104 pub by_namespace: Vec<NamespaceUsageInfo>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct EndpointUsageInfo {
110 pub endpoint: String,
111 pub requests: u64,
112 pub avg_latency_ms: f64,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct NamespaceUsageInfo {
118 pub namespace: String,
119 pub requests: u64,
120 pub vectors_accessed: u64,
121}
122
123impl DakeraClient {
128 pub async fn create_key(&self, request: CreateKeyRequest) -> Result<CreateKeyResponse> {
130 let url = format!("{}/admin/keys", self.base_url);
131 let response = self.client.post(&url).json(&request).send().await?;
132 self.handle_response(response).await
133 }
134
135 pub async fn list_keys(&self) -> Result<ListKeysResponse> {
137 let url = format!("{}/admin/keys", self.base_url);
138 let response = self.client.get(&url).send().await?;
139 self.handle_response(response).await
140 }
141
142 pub async fn get_key(&self, key_id: &str) -> Result<KeyInfo> {
144 let url = format!("{}/admin/keys/{}", self.base_url, key_id);
145 let response = self.client.get(&url).send().await?;
146 self.handle_response(response).await
147 }
148
149 pub async fn delete_key(&self, key_id: &str) -> Result<KeySuccessResponse> {
151 let url = format!("{}/admin/keys/{}", self.base_url, key_id);
152 let response = self.client.delete(&url).send().await?;
153 self.handle_response(response).await
154 }
155
156 pub async fn deactivate_key(&self, key_id: &str) -> Result<KeySuccessResponse> {
158 let url = format!("{}/admin/keys/{}/deactivate", self.base_url, key_id);
159 let response = self.client.post(&url).send().await?;
160 self.handle_response(response).await
161 }
162
163 pub async fn rotate_key(&self, key_id: &str) -> Result<RotateKeyResponse> {
165 let url = format!("{}/admin/keys/{}/rotate", self.base_url, key_id);
166 let response = self.client.post(&url).send().await?;
167 self.handle_response(response).await
168 }
169
170 pub async fn key_usage(&self, key_id: &str) -> Result<ApiKeyUsageResponse> {
172 let url = format!("{}/admin/keys/{}/usage", self.base_url, key_id);
173 let response = self.client.get(&url).send().await?;
174 self.handle_response(response).await
175 }
176
177 pub async fn create_namespace_key(
185 &self,
186 namespace: &str,
187 request: CreateNamespaceKeyRequest,
188 ) -> Result<CreateNamespaceKeyResponse> {
189 let url = format!("{}/v1/namespaces/{}/keys", self.base_url, namespace);
190 let response = self.client.post(&url).json(&request).send().await?;
191 self.handle_response(response).await
192 }
193
194 pub async fn list_namespace_keys(&self, namespace: &str) -> Result<ListNamespaceKeysResponse> {
196 let url = format!("{}/v1/namespaces/{}/keys", self.base_url, namespace);
197 let response = self.client.get(&url).send().await?;
198 self.handle_response(response).await
199 }
200
201 pub async fn delete_namespace_key(
203 &self,
204 namespace: &str,
205 key_id: &str,
206 ) -> Result<KeySuccessResponse> {
207 let url = format!(
208 "{}/v1/namespaces/{}/keys/{}",
209 self.base_url, namespace, key_id
210 );
211 let response = self.client.delete(&url).send().await?;
212 self.handle_response(response).await
213 }
214
215 pub async fn namespace_key_usage(
217 &self,
218 namespace: &str,
219 key_id: &str,
220 ) -> Result<NamespaceKeyUsageResponse> {
221 let url = format!(
222 "{}/v1/namespaces/{}/keys/{}/usage",
223 self.base_url, namespace, key_id
224 );
225 let response = self.client.get(&url).send().await?;
226 self.handle_response(response).await
227 }
228
229 pub async fn get_namespace_key_usage(
231 &self,
232 namespace: &str,
233 key_id: &str,
234 ) -> Result<NamespaceKeyUsageResponse> {
235 self.namespace_key_usage(namespace, key_id).await
236 }
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct CreateNamespaceKeyRequest {
246 pub name: String,
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub expires_in_days: Option<u64>,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct CreateNamespaceKeyResponse {
258 pub key_id: String,
259 pub key: String,
261 pub name: String,
262 pub namespace: String,
263 pub created_at: u64,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub expires_at: Option<u64>,
266 pub warning: String,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct NamespaceKeyInfo {
272 pub key_id: String,
273 pub name: String,
274 pub namespace: String,
275 pub created_at: u64,
276 pub active: bool,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub expires_at: Option<u64>,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct ListNamespaceKeysResponse {
284 pub namespace: String,
285 pub keys: Vec<NamespaceKeyInfo>,
286 pub total: usize,
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct NamespaceKeyUsageResponse {
292 pub key_id: String,
293 pub namespace: String,
294 pub total_requests: u64,
295 pub successful_requests: u64,
296 pub failed_requests: u64,
297 pub bytes_transferred: u64,
298 pub avg_latency_ms: f64,
299}
300
301#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_create_namespace_key_request_serializes_without_expiry() {
311 let req = CreateNamespaceKeyRequest {
312 name: "ci-runner".to_string(),
313 expires_in_days: None,
314 };
315 let json = serde_json::to_string(&req).unwrap();
316 assert!(json.contains("\"name\":\"ci-runner\""));
317 assert!(!json.contains("expires_in_days"));
318 }
319
320 #[test]
321 fn test_create_namespace_key_request_serializes_with_expiry() {
322 let req = CreateNamespaceKeyRequest {
323 name: "ci-runner".to_string(),
324 expires_in_days: Some(30),
325 };
326 let json = serde_json::to_string(&req).unwrap();
327 assert!(json.contains("\"expires_in_days\":30"));
328 }
329
330 #[test]
331 fn test_namespace_key_info_deserializes() {
332 let json = r#"{
333 "key_id": "key-abc",
334 "name": "ci-runner",
335 "namespace": "prod-ns",
336 "created_at": 1774000000,
337 "active": true
338 }"#;
339 let info: NamespaceKeyInfo = serde_json::from_str(json).unwrap();
340 assert_eq!(info.key_id, "key-abc");
341 assert_eq!(info.namespace, "prod-ns");
342 assert!(info.active);
343 assert!(info.expires_at.is_none());
344 }
345
346 #[test]
347 fn test_namespace_key_usage_response_deserializes() {
348 let json = r#"{
349 "key_id": "key-abc",
350 "namespace": "prod-ns",
351 "total_requests": 1000,
352 "successful_requests": 980,
353 "failed_requests": 20,
354 "bytes_transferred": 512000,
355 "avg_latency_ms": 12.4
356 }"#;
357 let usage: NamespaceKeyUsageResponse = serde_json::from_str(json).unwrap();
358 assert_eq!(usage.total_requests, 1000);
359 assert!((usage.avg_latency_ms - 12.4).abs() < 0.001);
360 }
361
362 #[test]
363 fn test_list_namespace_keys_response_deserializes() {
364 let json = r#"{
365 "namespace": "prod-ns",
366 "keys": [],
367 "total": 0
368 }"#;
369 let resp: ListNamespaceKeysResponse = serde_json::from_str(json).unwrap();
370 assert_eq!(resp.namespace, "prod-ns");
371 assert_eq!(resp.total, 0);
372 assert!(resp.keys.is_empty());
373 }
374}