autoendpoint/routers/stub/
router.rs

1use super::client::StubClient;
2use super::error::StubError;
3use super::settings::{StubServerSettings, StubSettings};
4use crate::error::ApiResult;
5use crate::extractors::notification::Notification;
6use crate::extractors::router_data_input::RouterDataInput;
7use crate::routers::{Router, RouterError, RouterResponse};
8use async_trait::async_trait;
9use serde_json::Value;
10use std::collections::HashMap;
11
12/// ##Stub router
13///
14/// This will create a testing router who's behavior is determined
15/// by the messages that it handles. This router can be used for
16/// local development and testing. The Stub router can be controlled
17/// by specifying the action in the `app_id` of the subscription URL.
18/// The `success` `app_id` is always defined.
19///
20/// for example, using a subscription registration URL of
21/// `/v1/stub/success/registration` with the request body of
22/// `"{\"token\":\"success\", \"key\":"..."}"` will return a successful
23/// registration endpoint that uses the provided VAPID key. Calling that endpoint
24/// with a VAPID signed request will succeed.
25///
26/// Likewise, using a subscription registration URL of
27/// `/v1/stub/error/registration` with the request body of
28/// `"{\"token\":\"General Error\", \"key\":"..."}"` will return that error
29/// when the subscription endpoint is called.
30pub struct StubRouter {
31    settings: StubSettings,
32    server_settings: StubServerSettings,
33    /// A map from application ID to an authenticated FCM client
34    clients: HashMap<String, StubClient>,
35}
36
37impl StubRouter {
38    /// Create a new `StubRouter`
39    pub fn new(settings: StubSettings) -> Result<Self, StubError> {
40        let server_settings =
41            serde_json::from_str::<StubServerSettings>(&settings.server_credentials)
42                .unwrap_or_default();
43        let clients = Self::create_clients(&server_settings);
44        Ok(Self {
45            settings,
46            server_settings,
47            clients,
48        })
49    }
50
51    /// Create Test clients for each application. Tests can specify which client
52    /// to use by designating the value in the `app_id` of the subscription URL.
53    /// While the `success` client is always defined, the `error` client can take on
54    /// different error results based on the server configuration. See [StubServerSettings]
55    /// for details.
56    fn create_clients(server_settings: &StubServerSettings) -> HashMap<String, StubClient> {
57        let mut clients = HashMap::new();
58        // TODO: Expand this to provide for additional error states based on app_id drawn
59        // from the StubServerSettings.
60        clients.insert("success".to_owned(), StubClient::success());
61        clients.insert(
62            "error".to_owned(),
63            StubClient::error(StubError::General(server_settings.error.clone())),
64        );
65        clients
66    }
67}
68
69#[async_trait(?Send)]
70impl Router for StubRouter {
71    fn register(
72        &self,
73        router_data_input: &RouterDataInput,
74        app_id: &str,
75    ) -> Result<HashMap<String, Value>, RouterError> {
76        if !self.clients.contains_key(app_id) {
77            return Err(
78                StubError::General(format!("Unknown App ID: {}", app_id.to_owned())).into(),
79            );
80        }
81
82        let mut router_data = HashMap::new();
83        router_data.insert(
84            "token".to_owned(),
85            serde_json::to_value(&router_data_input.token).unwrap(),
86        );
87        router_data.insert("app_id".to_owned(), serde_json::to_value(app_id).unwrap());
88
89        Ok(router_data)
90    }
91
92    async fn route_notification(&self, notification: Notification) -> ApiResult<RouterResponse> {
93        debug!(
94            "Sending Test notification to UAID {}",
95            notification.subscription.user.uaid
96        );
97        trace!("Notification = {:?}", notification);
98
99        let router_data = notification
100            .subscription
101            .user
102            .router_data
103            .as_ref()
104            .ok_or(StubError::General("No Registration".to_owned()))?;
105
106        let default = Value::String("success".to_owned());
107        let client_type = router_data.get("type").unwrap_or(&default);
108        let client = self
109            .clients
110            .get(&client_type.to_string().to_lowercase())
111            .unwrap_or_else(|| self.clients.get("error").unwrap());
112        client.call(&self.server_settings).await?;
113        Ok(RouterResponse::success(
114            format!("{}/m/123", self.settings.url),
115            notification.headers.ttl as usize,
116        ))
117    }
118}