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: StoreConfigResponseData,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
208pub struct StoreConfigResponseData {
209 pub model: StoreConfigModel,
211 }
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct StoreConfigModel {
217 pub id: String,
219 #[serde(rename = "type")]
221 pub model_type: String,
222 pub action: String,
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn test_store_config_request_with_id() {
232 let request = StoreConfigRequest {
233 config_type: "gateway".to_string(),
234 id: Some("01k8ek6h9aahhnrv3benret1nn".to_string()),
235 config: "[proxy]\nid = \"test\"\n".to_string(),
236 };
237
238 let json = serde_json::to_string(&request).unwrap();
239 assert!(json.contains("\"type\":\"gateway\""));
240 assert!(json.contains("\"id\":\"01k8ek6h9aahhnrv3benret1nn\""));
241 assert!(json.contains("\"config\":"));
242 assert!(json.contains("[proxy]"));
243
244 let deserialized: StoreConfigRequest = serde_json::from_str(&json).unwrap();
246 assert_eq!(deserialized.config_type, "gateway");
247 assert_eq!(
248 deserialized.id,
249 Some("01k8ek6h9aahhnrv3benret1nn".to_string())
250 );
251 }
252
253 #[test]
254 fn test_store_config_request_without_id() {
255 let request = StoreConfigRequest {
256 config_type: "pipeline".to_string(),
257 id: None,
258 config: "[pipeline]\nname = \"test\"\n".to_string(),
259 };
260
261 let json = serde_json::to_string(&request).unwrap();
262 assert!(json.contains("\"type\":\"pipeline\""));
263 assert!(json.contains("\"config\":"));
264 assert!(!json.contains("\"id\""));
266
267 let deserialized: StoreConfigRequest = serde_json::from_str(&json).unwrap();
269 assert_eq!(deserialized.config_type, "pipeline");
270 assert_eq!(deserialized.id, None);
271 }
272
273 #[test]
274 fn test_store_config_request_type_field_rename() {
275 let json = r#"{"type":"transform","config":"[transform]\nname = \"test\"\n"}"#;
277 let request: StoreConfigRequest = serde_json::from_str(json).unwrap();
278 assert_eq!(request.config_type, "transform");
279 assert_eq!(request.id, None);
280 }
281
282 #[test]
283 fn test_store_config_response() {
284 let json = r#"{
285 "success": true,
286 "message": "Gateway configuration updated successfully",
287 "data": {
288 "model": {
289 "id": "01k9npa4tatmwddk66xxpcr2r0",
290 "type": "gateway",
291 "action": "updated"
292 },
293 "change": {}
294 }
295 }"#;
296
297 let response: StoreConfigResponse = serde_json::from_str(json).unwrap();
298 assert_eq!(response.success, true);
299 assert!(response.message.contains("updated successfully"));
300 assert_eq!(response.data.model.id, "01k9npa4tatmwddk66xxpcr2r0");
301 assert_eq!(response.data.model.model_type, "gateway");
302 assert_eq!(response.data.model.action, "updated");
303 }
304}