Skip to main content

webex_message_handler/
device_manager.rs

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