Skip to main content

firebase_admin_sdk/crashlytics/
mod.rs

1//! Firebase Crashlytics module.
2//!
3//! This module provides functionality for managing Crashlytics data.
4//! Currently, it supports deleting crash reports for a specific user, which is useful for
5//! privacy compliance (e.g., "Right to be Forgotten").
6//!
7//! # Examples
8//!
9//! ```rust,no_run
10//! # use firebase_admin_sdk::FirebaseApp;
11//! # async fn run(app: FirebaseApp) {
12//! let crashlytics = app.crashlytics();
13//!
14//! // Delete crash reports for a user
15//! let _ = crashlytics.delete_crash_reports("your-app-id", "user-uid").await;
16//! # }
17//! ```
18
19use crate::core::middleware::AuthMiddleware;
20use reqwest::Client;
21use reqwest::StatusCode;
22use reqwest_middleware::ClientBuilder;
23use reqwest_middleware::ClientWithMiddleware;
24use reqwest_retry::policies::ExponentialBackoff;
25use reqwest_retry::RetryTransientMiddleware;
26use thiserror::Error;
27
28/// Error type for Firebase Crashlytics operations.
29#[derive(Debug, Error)]
30pub enum Error {
31    /// An error occurred while sending the request or receiving the response.
32    #[error("Request error: {0}")]
33    Reqwest(#[from] reqwest::Error),
34    /// The middleware encountered an error (e.g., authentication failed).
35    #[error("Middleware error: {0}")]
36    Middleware(#[from] reqwest_middleware::Error),
37    /// The API returned an error status code.
38    #[error("API error: {0}")]
39    Api(StatusCode),
40}
41
42const CRASHLYTICS_V1_API: &str =
43    "https://firebasecrashlytics.googleapis.com/v1alpha/projects/{project_id}";
44
45/// Client for interacting with the Firebase Crashlytics API.
46pub struct FirebaseCrashlytics {
47    client: ClientWithMiddleware,
48    base_url: String,
49}
50
51impl FirebaseCrashlytics {
52    /// Creates a new `FirebaseCrashlytics` client.
53    pub fn new(middleware: AuthMiddleware) -> Self {
54        let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
55
56        let client = ClientBuilder::new(Client::new())
57            .with(RetryTransientMiddleware::new_with_policy(retry_policy))
58            .with(middleware.clone())
59            .build();
60
61        let project_id = middleware
62            .key
63            .project_id
64            .clone()
65            .unwrap_or_default();
66
67        let base_url = CRASHLYTICS_V1_API.replace("{project_id}", &project_id);
68
69        Self { client, base_url }
70    }
71
72    /// Creates a new `FirebaseCrashlytics` client with a custom client and base URL.
73    /// Internal use only, primarily for testing.
74    #[allow(dead_code)]
75    pub(crate) fn new_with_client(client: ClientWithMiddleware, base_url: String) -> Self {
76        Self { client, base_url }
77    }
78
79    /// Enqueues a request to permanently remove crash reports associated with the specified user.
80    ///
81    /// # Arguments
82    ///
83    /// * `app_id` - The App ID (e.g., the Google App ID, like `1:1234567890:android:321abc456def7890`).
84    /// * `user_id` - The unique identifier of the user whose crash reports should be deleted.
85    ///
86    /// # Errors
87    ///
88    /// Returns an error if the request fails or if the API returns a non-success status code.
89    pub async fn delete_crash_reports(&self, app_id: &str, user_id: &str) -> Result<(), Error> {
90        // The resource name format is: projects/{project}/apps/{app}/users/{user}/crashReports
91        let url = format!(
92            "{}/apps/{}/users/{}/crashReports",
93            self.base_url, app_id, user_id
94        );
95
96        let response = self.client.delete(&url).send().await?;
97
98        if response.status().is_success() {
99            Ok(())
100        } else {
101            Err(Error::Api(response.status()))
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests;