webex_message_handler/
device_manager.rs1use crate::errors::{Result, WebexError};
4use crate::types::{DeviceRegistration, FetchFn, FetchRequest};
5use crate::url_validation::validate_webex_url;
6use serde_json::json;
7use std::collections::HashMap;
8use tracing::{debug, error, info};
9
10const WDM_API_BASE: &str = "https://wdm-a.wbx2.com/wdm/api/v1/devices";
11
12fn device_body() -> serde_json::Value {
13 json!({
14 "deviceName": "webex-message-handler",
15 "deviceType": "DESKTOP",
16 "localizedModel": "rust",
17 "model": "rust",
18 "name": "webex-message-handler",
19 "systemName": "webex-message-handler",
20 "systemVersion": "1.0.0"
21 })
22}
23
24pub struct DeviceManager {
26 device_url: Option<String>,
27 http_do: FetchFn,
28}
29
30impl DeviceManager {
31 pub fn new(http_do: FetchFn) -> Self {
32 Self {
33 device_url: None,
34 http_do,
35 }
36 }
37
38 pub async fn register(&mut self, token: &str) -> Result<DeviceRegistration> {
40 debug!("Registering device with WDM");
41
42 let mut headers = HashMap::new();
43 headers.insert("Authorization".to_string(), format!("Bearer {}", token));
44 headers.insert("Content-Type".to_string(), "application/json".to_string());
45
46 let body = serde_json::to_string(&device_body())
47 .map_err(|e| WebexError::device_registration(format!("Failed to serialize body: {e}"), None))?;
48
49 let response = (self.http_do)(FetchRequest {
50 url: WDM_API_BASE.to_string(),
51 method: "POST".to_string(),
52 headers,
53 body: Some(body),
54 })
55 .await
56 .map_err(|e| WebexError::device_registration(format!("Failed to register device: {e}"), None))?;
57
58 let status = response.status;
59
60 if status == 401 {
61 error!("Device registration failed: Unauthorized");
62 return Err(WebexError::auth("Unauthorized to register device"));
63 }
64
65 if !response.ok {
66 error!("Device registration failed with status {status}");
67 return Err(WebexError::device_registration("Failed to register device", Some(status)));
68 }
69
70 let mut reg: DeviceRegistration = serde_json::from_slice(&response.body)
71 .map_err(|e| WebexError::device_registration(format!("Failed to parse response: {e}"), None))?;
72
73 reg.encryption_service_url = reg.services.get("encryptionServiceUrl").cloned().unwrap_or_default();
74
75 if !reg.web_socket_url.is_empty() {
77 validate_webex_url(®.web_socket_url, "wss")
78 .map_err(|e| WebexError::device_registration(format!("Invalid web_socket_url: {e}"), None))?;
79 }
80
81 if !reg.encryption_service_url.is_empty() {
82 validate_webex_url(®.encryption_service_url, "https")
83 .map_err(|e| WebexError::device_registration(format!("Invalid encryption_service_url: {e}"), None))?;
84 }
85
86 self.device_url = Some(reg.device_url.clone());
87
88 info!("Device registered successfully");
89 Ok(reg)
90 }
91
92 pub async fn refresh(&self, token: &str) -> Result<DeviceRegistration> {
94 let device_url = self.device_url.as_deref().ok_or_else(|| {
95 WebexError::device_registration("Device not registered. Call register() first.", None)
96 })?;
97
98 debug!("Refreshing device registration");
99
100 let mut headers = HashMap::new();
101 headers.insert("Authorization".to_string(), format!("Bearer {}", token));
102 headers.insert("Content-Type".to_string(), "application/json".to_string());
103
104 let body = serde_json::to_string(&device_body())
105 .map_err(|e| WebexError::device_registration(format!("Failed to serialize body: {e}"), None))?;
106
107 let response = (self.http_do)(FetchRequest {
108 url: device_url.to_string(),
109 method: "PUT".to_string(),
110 headers,
111 body: Some(body),
112 })
113 .await
114 .map_err(|e| WebexError::device_registration(format!("Failed to refresh device: {e}"), None))?;
115
116 let status = response.status;
117
118 if status == 401 {
119 error!("Device refresh failed: Unauthorized");
120 return Err(WebexError::auth("Unauthorized to refresh device"));
121 }
122
123 if !response.ok {
124 error!("Device refresh failed with status {status}");
125 return Err(WebexError::device_registration("Failed to refresh device", Some(status)));
126 }
127
128 let mut reg: DeviceRegistration = serde_json::from_slice(&response.body)
129 .map_err(|e| WebexError::device_registration(format!("Failed to parse response: {e}"), None))?;
130
131 reg.encryption_service_url = reg.services.get("encryptionServiceUrl").cloned().unwrap_or_default();
132
133 if !reg.web_socket_url.is_empty() {
135 validate_webex_url(®.web_socket_url, "wss")
136 .map_err(|e| WebexError::device_registration(format!("Invalid web_socket_url: {e}"), None))?;
137 }
138
139 if !reg.encryption_service_url.is_empty() {
140 validate_webex_url(®.encryption_service_url, "https")
141 .map_err(|e| WebexError::device_registration(format!("Invalid encryption_service_url: {e}"), None))?;
142 }
143
144 info!("Device refreshed successfully");
145 Ok(reg)
146 }
147
148 pub async fn unregister(&mut self, token: &str) -> Result<()> {
150 let device_url = self.device_url.as_deref().ok_or_else(|| {
151 WebexError::device_registration("Device not registered. Call register() first.", None)
152 })?;
153
154 debug!("Unregistering device");
155
156 let mut headers = HashMap::new();
157 headers.insert("Authorization".to_string(), format!("Bearer {}", token));
158 headers.insert("Content-Type".to_string(), "application/json".to_string());
159
160 let response = (self.http_do)(FetchRequest {
161 url: device_url.to_string(),
162 method: "DELETE".to_string(),
163 headers,
164 body: None,
165 })
166 .await
167 .map_err(|e| WebexError::device_registration(format!("Failed to unregister device: {e}"), None))?;
168
169 let status = response.status;
170
171 if status == 401 {
172 error!("Device unregistration failed: Unauthorized");
173 return Err(WebexError::auth("Unauthorized to unregister device"));
174 }
175
176 if !response.ok && status != 404 {
177 error!("Device unregistration failed with status {status}");
178 return Err(WebexError::device_registration("Failed to unregister device", Some(status)));
179 }
180
181 self.device_url = None;
182 info!("Device unregistered successfully");
183 Ok(())
184 }
185}