1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug)]
9pub enum RunbeamError {
10 JwtValidation(String),
12 Api(ApiError),
14 Storage(crate::storage::StorageError),
16 Config(String),
18 Validation(crate::validation::ValidationError),
20}
21
22impl fmt::Display for RunbeamError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 match self {
25 RunbeamError::JwtValidation(msg) => write!(f, "JWT validation failed: {}", msg),
26 RunbeamError::Api(err) => write!(f, "API error: {}", err),
27 RunbeamError::Storage(err) => write!(f, "Storage error: {}", err),
28 RunbeamError::Config(msg) => write!(f, "Configuration error: {}", msg),
29 RunbeamError::Validation(err) => write!(f, "Validation error: {}", err),
30 }
31 }
32}
33
34impl std::error::Error for RunbeamError {}
35
36impl From<ApiError> for RunbeamError {
37 fn from(err: ApiError) -> Self {
38 RunbeamError::Api(err)
39 }
40}
41
42impl From<crate::storage::StorageError> for RunbeamError {
43 fn from(err: crate::storage::StorageError) -> Self {
44 RunbeamError::Storage(err)
45 }
46}
47
48impl From<jsonwebtoken::errors::Error> for RunbeamError {
49 fn from(err: jsonwebtoken::errors::Error) -> Self {
50 RunbeamError::JwtValidation(err.to_string())
51 }
52}
53
54impl From<crate::validation::ValidationError> for RunbeamError {
55 fn from(err: crate::validation::ValidationError) -> Self {
56 RunbeamError::Validation(err)
57 }
58}
59
60#[derive(Debug)]
62pub enum ApiError {
63 Network(String),
65 Http { status: u16, message: String },
67 Parse(String),
69 Request(String),
71}
72
73impl fmt::Display for ApiError {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 match self {
76 ApiError::Network(msg) => write!(f, "Network error: {}", msg),
77 ApiError::Http { status, message } => {
78 write!(f, "HTTP {} error: {}", status, message)
79 }
80 ApiError::Parse(msg) => write!(f, "Parse error: {}", msg),
81 ApiError::Request(msg) => write!(f, "Request error: {}", msg),
82 }
83 }
84}
85
86impl std::error::Error for ApiError {}
87
88impl From<reqwest::Error> for ApiError {
89 fn from(err: reqwest::Error) -> Self {
90 if err.is_timeout() {
91 ApiError::Network("Request timeout".to_string())
92 } else if err.is_connect() {
93 ApiError::Network(format!("Connection failed: {}", err))
94 } else if let Some(status) = err.status() {
95 ApiError::Http {
96 status: status.as_u16(),
97 message: err.to_string(),
98 }
99 } else {
100 ApiError::Network(err.to_string())
101 }
102 }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct UserInfo {
108 pub id: String,
109 pub email: String,
110 pub name: String,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct UserToken {
120 pub token: String,
122 #[serde(default)]
124 pub expires_at: Option<i64>,
125 #[serde(default)]
127 pub user: Option<UserInfo>,
128}
129
130impl UserToken {
131 pub fn new(token: String, expires_at: Option<i64>, user: Option<UserInfo>) -> Self {
133 Self {
134 token,
135 expires_at,
136 user,
137 }
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct TeamInfo {
144 pub id: String,
145 pub name: String,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct GatewayInfo {
151 pub id: String,
152 pub code: String,
153 pub name: String,
154 #[serde(default)]
155 pub authorized_by: Option<AuthorizedBy>,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct AuthorizedBy {
161 pub id: String,
162 pub name: String,
163 pub email: String,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct AuthorizeResponse {
169 pub machine_token: String,
170 pub expires_in: f64,
171 pub expires_at: String,
172 pub gateway: GatewayInfo,
173 #[serde(default)]
174 pub abilities: Vec<String>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct StoreConfigRequest {
183 #[serde(rename = "type")]
185 pub config_type: String,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub id: Option<String>,
189 pub config: String,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct StoreConfigResponse {
198 pub success: bool,
200 pub message: String,
202 pub data: StoreConfigModel,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct StoreConfigModel {
209 pub id: String,
211 #[serde(rename = "type")]
213 pub model_type: String,
214 pub action: String,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct MeshInfo {
221 pub id: String,
223 pub name: String,
225 #[serde(rename = "type")]
227 pub mesh_type: String,
228 pub provider: String,
230 #[serde(default = "default_auth_type")]
232 pub auth_type: String,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub jwt_secret: Option<String>,
236 #[serde(default, skip_serializing_if = "Option::is_none")]
238 pub jwt_private_key_path: Option<String>,
239 #[serde(default, skip_serializing_if = "Option::is_none")]
241 pub jwt_public_key_path: Option<String>,
242 #[serde(default)]
244 pub ingress: Vec<String>,
245 #[serde(default)]
247 pub egress: Vec<String>,
248 #[serde(default = "default_true")]
250 pub enabled: bool,
251 #[serde(default)]
253 pub description: Option<String>,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct MeshIngressInfo {
259 pub id: String,
261 pub name: String,
263 #[serde(rename = "type")]
265 pub ingress_type: String,
266 pub pipeline: String,
268 #[serde(default = "default_mode")]
270 pub mode: String,
271 #[serde(default, skip_serializing_if = "Option::is_none")]
273 pub endpoint: Option<String>,
274 #[serde(default)]
276 pub urls: Vec<String>,
277 #[serde(default = "default_true")]
279 pub enabled: bool,
280 #[serde(default)]
282 pub description: Option<String>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct MeshEgressInfo {
288 pub id: String,
290 pub name: String,
292 #[serde(rename = "type")]
294 pub egress_type: String,
295 pub pipeline: String,
297 #[serde(default = "default_mode")]
299 pub mode: String,
300 #[serde(default, skip_serializing_if = "Option::is_none")]
302 pub backend: Option<String>,
303 #[serde(default = "default_true")]
305 pub enabled: bool,
306 #[serde(default)]
308 pub description: Option<String>,
309}
310
311fn default_true() -> bool {
312 true
313}
314
315fn default_mode() -> String {
316 "default".to_string()
317}
318
319fn default_auth_type() -> String {
320 "jwt".to_string()
321}
322
323#[cfg(test)]
324mod tests {
325 use super::*;
326
327 #[test]
328 fn test_store_config_request_with_id() {
329 let request = StoreConfigRequest {
330 config_type: "gateway".to_string(),
331 id: Some("01k8ek6h9aahhnrv3benret1nn".to_string()),
332 config: "[proxy]\nid = \"test\"\n".to_string(),
333 };
334
335 let json = serde_json::to_string(&request).unwrap();
336 assert!(json.contains("\"type\":\"gateway\""));
337 assert!(json.contains("\"id\":\"01k8ek6h9aahhnrv3benret1nn\""));
338 assert!(json.contains("\"config\":"));
339 assert!(json.contains("[proxy]"));
340
341 let deserialized: StoreConfigRequest = serde_json::from_str(&json).unwrap();
343 assert_eq!(deserialized.config_type, "gateway");
344 assert_eq!(
345 deserialized.id,
346 Some("01k8ek6h9aahhnrv3benret1nn".to_string())
347 );
348 }
349
350 #[test]
351 fn test_store_config_request_without_id() {
352 let request = StoreConfigRequest {
353 config_type: "pipeline".to_string(),
354 id: None,
355 config: "[pipeline]\nname = \"test\"\n".to_string(),
356 };
357
358 let json = serde_json::to_string(&request).unwrap();
359 assert!(json.contains("\"type\":\"pipeline\""));
360 assert!(json.contains("\"config\":"));
361 assert!(!json.contains("\"id\""));
363
364 let deserialized: StoreConfigRequest = serde_json::from_str(&json).unwrap();
366 assert_eq!(deserialized.config_type, "pipeline");
367 assert_eq!(deserialized.id, None);
368 }
369
370 #[test]
371 fn test_store_config_request_type_field_rename() {
372 let json = r#"{"type":"transform","config":"[transform]\nname = \"test\"\n"}"#;
374 let request: StoreConfigRequest = serde_json::from_str(json).unwrap();
375 assert_eq!(request.config_type, "transform");
376 assert_eq!(request.id, None);
377 }
378
379 #[test]
380 fn test_store_config_response() {
381 let json = r#"{
382 "success": true,
383 "message": "Gateway configuration updated successfully",
384 "data": {
385 "id": "01k9npa4tatmwddk66xxpcr2r0",
386 "type": "gateway",
387 "action": "updated"
388 }
389 }"#;
390
391 let response: StoreConfigResponse = serde_json::from_str(json).unwrap();
392 assert_eq!(response.success, true);
393 assert!(response.message.contains("updated successfully"));
394 assert_eq!(response.data.id, "01k9npa4tatmwddk66xxpcr2r0");
395 assert_eq!(response.data.model_type, "gateway");
396 assert_eq!(response.data.action, "updated");
397 }
398}