Skip to main content

edgebase_admin/
push.rs

1//! PushClient — Push notification management for Admin SDK.
2
3use std::sync::Arc;
4
5use serde_json::Value;
6
7use crate::generated::admin_api_core::GeneratedAdminApi;
8use edgebase_core::error::Error;
9use edgebase_core::http_client::HttpClient;
10
11/// Result of a push send operation.
12#[derive(Debug, Default)]
13pub struct PushResult {
14    pub sent: i64,
15    pub failed: i64,
16    pub removed: i64,
17}
18
19/// Client for push notification operations.
20pub struct PushClient {
21    http: Arc<HttpClient>,
22}
23
24impl PushClient {
25    pub(crate) fn new(http: Arc<HttpClient>) -> Self {
26        Self { http }
27    }
28
29    fn core(&self) -> GeneratedAdminApi<'_> {
30        GeneratedAdminApi::new(&self.http)
31    }
32
33    /// Send a push notification to a single user's devices.
34    pub async fn send(&self, user_id: &str, payload: &Value) -> Result<PushResult, Error> {
35        let body = serde_json::json!({
36            "userId": user_id,
37            "payload": payload,
38        });
39        let res: Value = self.core().push_send(&body).await?;
40        Ok(parse_push_result(&res))
41    }
42
43    /// Send a push notification to multiple users (no limit — server chunks internally).
44    pub async fn send_many(&self, user_ids: &[&str], payload: &Value) -> Result<PushResult, Error> {
45        let body = serde_json::json!({
46            "userIds": user_ids,
47            "payload": payload,
48        });
49        let res: Value = self.core().push_send_many(&body).await?;
50        Ok(parse_push_result(&res))
51    }
52
53    /// Send a push notification to a specific device token.
54    pub async fn send_to_token(&self, token: &str, payload: &Value, platform: Option<&str>) -> Result<PushResult, Error> {
55        let body = serde_json::json!({
56            "token": token,
57            "payload": payload,
58            "platform": platform.unwrap_or("web"),
59        });
60        let res: Value = self.core().push_send_to_token(&body).await?;
61        Ok(parse_push_result(&res))
62    }
63
64    /// Get registered device tokens for a user — token values NOT exposed.
65    pub async fn get_tokens(&self, user_id: &str) -> Result<Vec<Value>, Error> {
66        let mut query = std::collections::HashMap::new();
67        query.insert("userId".to_string(), user_id.to_string());
68        let res: Value = self.core().get_push_tokens(&query).await?;
69        if let Some(items) = res.get("items").and_then(|v| v.as_array()) {
70            Ok(items.clone())
71        } else {
72            Ok(Vec::new())
73        }
74    }
75
76    /// Get push send logs for a user (last 24 hours).
77    pub async fn get_logs(&self, user_id: &str, limit: Option<u32>) -> Result<Vec<Value>, Error> {
78        let mut query = std::collections::HashMap::new();
79        query.insert("userId".to_string(), user_id.to_string());
80        if let Some(l) = limit {
81            query.insert("limit".to_string(), l.to_string());
82        }
83        let res: Value = self.core().get_push_logs(&query).await?;
84        if let Some(items) = res.get("items").and_then(|v| v.as_array()) {
85            Ok(items.clone())
86        } else {
87            Ok(Vec::new())
88        }
89    }
90
91    /// Send a push notification to an FCM topic.
92    pub async fn send_to_topic(&self, topic: &str, payload: &Value) -> Result<Value, Error> {
93        let body = serde_json::json!({
94            "topic": topic,
95            "payload": payload,
96        });
97        self.core().push_send_to_topic(&body).await
98    }
99
100    /// Broadcast a push notification to all devices via /topics/all.
101    pub async fn broadcast(&self, payload: &Value) -> Result<Value, Error> {
102        let body = serde_json::json!({
103            "payload": payload,
104        });
105        self.core().push_broadcast(&body).await
106    }
107}
108
109fn parse_push_result(v: &Value) -> PushResult {
110    PushResult {
111        sent: v.get("sent").and_then(|v| v.as_i64()).unwrap_or(0),
112        failed: v.get("failed").and_then(|v| v.as_i64()).unwrap_or(0),
113        removed: v.get("removed").and_then(|v| v.as_i64()).unwrap_or(0),
114    }
115}