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#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_store_config_request_with_id() {
224 let request = StoreConfigRequest {
225 config_type: "gateway".to_string(),
226 id: Some("01k8ek6h9aahhnrv3benret1nn".to_string()),
227 config: "[proxy]\nid = \"test\"\n".to_string(),
228 };
229
230 let json = serde_json::to_string(&request).unwrap();
231 assert!(json.contains("\"type\":\"gateway\""));
232 assert!(json.contains("\"id\":\"01k8ek6h9aahhnrv3benret1nn\""));
233 assert!(json.contains("\"config\":"));
234 assert!(json.contains("[proxy]"));
235
236 let deserialized: StoreConfigRequest = serde_json::from_str(&json).unwrap();
238 assert_eq!(deserialized.config_type, "gateway");
239 assert_eq!(
240 deserialized.id,
241 Some("01k8ek6h9aahhnrv3benret1nn".to_string())
242 );
243 }
244
245 #[test]
246 fn test_store_config_request_without_id() {
247 let request = StoreConfigRequest {
248 config_type: "pipeline".to_string(),
249 id: None,
250 config: "[pipeline]\nname = \"test\"\n".to_string(),
251 };
252
253 let json = serde_json::to_string(&request).unwrap();
254 assert!(json.contains("\"type\":\"pipeline\""));
255 assert!(json.contains("\"config\":"));
256 assert!(!json.contains("\"id\""));
258
259 let deserialized: StoreConfigRequest = serde_json::from_str(&json).unwrap();
261 assert_eq!(deserialized.config_type, "pipeline");
262 assert_eq!(deserialized.id, None);
263 }
264
265 #[test]
266 fn test_store_config_request_type_field_rename() {
267 let json = r#"{"type":"transform","config":"[transform]\nname = \"test\"\n"}"#;
269 let request: StoreConfigRequest = serde_json::from_str(json).unwrap();
270 assert_eq!(request.config_type, "transform");
271 assert_eq!(request.id, None);
272 }
273
274 #[test]
275 fn test_store_config_response() {
276 let json = r#"{
277 "success": true,
278 "message": "Gateway configuration updated successfully",
279 "data": {
280 "id": "01k9npa4tatmwddk66xxpcr2r0",
281 "type": "gateway",
282 "action": "updated"
283 }
284 }"#;
285
286 let response: StoreConfigResponse = serde_json::from_str(json).unwrap();
287 assert_eq!(response.success, true);
288 assert!(response.message.contains("updated successfully"));
289 assert_eq!(response.data.id, "01k9npa4tatmwddk66xxpcr2r0");
290 assert_eq!(response.data.model_type, "gateway");
291 assert_eq!(response.data.action, "updated");
292 }
293}