firebase_rs_sdk/installations/
config.rs

1use crate::app::FirebaseApp;
2use crate::installations::error::{invalid_argument, InstallationsResult};
3
4/// Extracted configuration required to contact the Firebase Installations REST API.
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub struct AppConfig {
7    pub app_name: String,
8    pub api_key: String,
9    pub project_id: String,
10    pub app_id: String,
11}
12
13/// Mirrors the JavaScript helper `extractAppConfig` from
14/// `packages/installations/src/helpers/extract-app-config.ts`.
15pub fn extract_app_config(app: &FirebaseApp) -> InstallationsResult<AppConfig> {
16    let options = app.options();
17
18    let app_name = app.name().to_owned();
19    if app_name.is_empty() {
20        return Err(missing_value_error("App Name"));
21    }
22
23    let project_id = options
24        .project_id
25        .ok_or_else(|| missing_value_error("projectId"))?;
26    let api_key = options
27        .api_key
28        .ok_or_else(|| missing_value_error("apiKey"))?;
29    let app_id = options.app_id.ok_or_else(|| missing_value_error("appId"))?;
30
31    Ok(AppConfig {
32        app_name,
33        api_key,
34        project_id,
35        app_id,
36    })
37}
38
39fn missing_value_error(value_name: &str) -> crate::installations::error::InstallationsError {
40    invalid_argument(format!(
41        "Missing App configuration value: \"{}\"",
42        value_name
43    ))
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::app::initialize_app;
50    use crate::app::{FirebaseAppSettings, FirebaseOptions};
51    use std::sync::atomic::{AtomicUsize, Ordering};
52
53    fn base_options() -> FirebaseOptions {
54        FirebaseOptions {
55            project_id: Some("project".into()),
56            api_key: Some("apikey".into()),
57            app_id: Some("app-id".into()),
58            ..Default::default()
59        }
60    }
61
62    fn unique_settings() -> FirebaseAppSettings {
63        static COUNTER: AtomicUsize = AtomicUsize::new(0);
64        FirebaseAppSettings {
65            name: Some(format!(
66                "config-test-{}",
67                COUNTER.fetch_add(1, Ordering::SeqCst)
68            )),
69            ..Default::default()
70        }
71    }
72
73    #[tokio::test(flavor = "current_thread")]
74    async fn extract_app_config_success() {
75        let options = base_options();
76        let app = initialize_app(options, Some(unique_settings()))
77            .await
78            .unwrap();
79        let config = extract_app_config(&app).unwrap();
80        assert_eq!(config.project_id, "project");
81        assert_eq!(config.api_key, "apikey");
82        assert_eq!(config.app_id, "app-id");
83        assert_eq!(config.app_name, app.name());
84    }
85
86    #[tokio::test(flavor = "current_thread")]
87    async fn missing_project_id_returns_error() {
88        let mut options = base_options();
89        options.project_id = None;
90        let app = initialize_app(options, Some(unique_settings()))
91            .await
92            .unwrap();
93        let err = extract_app_config(&app).unwrap_err();
94        assert!(err.to_string().contains("Missing App configuration value"));
95    }
96}