From 0d351ca4b9a1d6a2e1087ff8d3d2927e032f2d24 Mon Sep 17 00:00:00 2001 From: Matthew Mauer Date: Fri, 13 Feb 2026 12:29:25 -0500 Subject: [PATCH] support fetching user insights reports --- src/apis/mod.rs | 5 +- src/apis/report_service_api.rs | 132 +++++++++++++++++ src/models/mod.rs | 1 + src/models/reports.rs | 168 +++++++++++++++++++++ src/propelauth/auth.rs | 12 +- src/propelauth/errors.rs | 17 ++- src/propelauth/mfa.rs | 62 +++----- src/propelauth/mod.rs | 7 +- src/propelauth/reports.rs | 262 +++++++++++++++++++++++++++++++++ 9 files changed, 618 insertions(+), 48 deletions(-) create mode 100644 src/apis/report_service_api.rs create mode 100644 src/models/reports.rs create mode 100644 src/propelauth/reports.rs diff --git a/src/apis/mod.rs b/src/apis/mod.rs index a159d61..e719125 100644 --- a/src/apis/mod.rs +++ b/src/apis/mod.rs @@ -72,9 +72,10 @@ pub fn urlencode>(s: T) -> String { pub mod access_token_service_api; pub mod api_key_service_api; pub mod auth_service_api; -pub mod org_service_api; -pub mod user_service_api; pub mod employee_service_api; pub mod mfa_service_api; +pub mod org_service_api; +pub(crate) mod report_service_api; +pub mod user_service_api; pub mod configuration; diff --git a/src/apis/report_service_api.rs b/src/apis/report_service_api.rs new file mode 100644 index 0000000..251f5ba --- /dev/null +++ b/src/apis/report_service_api.rs @@ -0,0 +1,132 @@ +/* + * propelauth + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * Generated by: https://openapi-generator.tech + */ + +use reqwest; + +use super::{configuration, Error}; +use crate::apis::ResponseContent; +use crate::models::reports::{ + FetchReportQuery, OrgReport, OrgReportType, UserReportPage, UserReportType, +}; +use crate::propelauth::auth::AUTH_HOSTNAME_HEADER; + +/// struct for typed errors of methods [`fetch_user_report`] or [`fetch_org_report`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum FetchReportRequestError { + Status401(serde_json::Value), + Status400(serde_json::Value), + Status429(serde_json::Value), + UnknownValue(serde_json::Value), +} + +pub(crate) async fn fetch_user_report( + configuration: &configuration::Configuration, + report_key: UserReportType, + params: FetchReportQuery, +) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/api/backend/v1/user_report/{report_key}", + local_var_configuration.base_path, + report_key = report_key.as_str(), + ); + let mut local_var_req_builder = + local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + local_var_req_builder = local_var_req_builder.query(¶ms); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = + local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.header( + AUTH_HOSTNAME_HEADER, + local_var_configuration.auth_hostname.to_owned(), + ); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = + serde_json::from_str(&local_var_content).ok(); + let local_var_error: crate::apis::ResponseContent = + ResponseContent { + status: local_var_status, + content: local_var_content, + entity: local_var_entity, + }; + Err(Error::ResponseError(local_var_error)) + } +} + +pub(crate) async fn fetch_org_report( + configuration: &configuration::Configuration, + report_key: OrgReportType, + params: FetchReportQuery, +) -> Result> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/api/backend/v1/org_report/{report_key}", + local_var_configuration.base_path, + report_key = report_key.as_str(), + ); + let mut local_var_req_builder = + local_var_client.request(reqwest::Method::GET, local_var_uri_str.as_str()); + + local_var_req_builder = local_var_req_builder.query(¶ms); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = + local_var_req_builder.header(reqwest::header::USER_AGENT, local_var_user_agent.clone()); + } + if let Some(ref local_var_token) = local_var_configuration.bearer_access_token { + local_var_req_builder = local_var_req_builder.bearer_auth(local_var_token.to_owned()); + }; + local_var_req_builder = local_var_req_builder.header( + AUTH_HOSTNAME_HEADER, + local_var_configuration.auth_hostname.to_owned(), + ); + + let local_var_req = local_var_req_builder.build()?; + let local_var_resp = local_var_client.execute(local_var_req).await?; + + let local_var_status = local_var_resp.status(); + let local_var_content = local_var_resp.text().await?; + + if !local_var_status.is_client_error() && !local_var_status.is_server_error() { + serde_json::from_str(&local_var_content).map_err(Error::from) + } else { + let local_var_entity: Option = + serde_json::from_str(&local_var_content).ok(); + let local_var_error: crate::apis::ResponseContent = + ResponseContent { + status: local_var_status, + content: local_var_content, + entity: local_var_entity, + }; + Err(Error::ResponseError(local_var_error)) + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index ac152ac..3438d5f 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -89,6 +89,7 @@ pub mod user_ids_query; pub use self::user_ids_query::UserIdsQuery; pub mod user_in_org; pub use self::user_in_org::UserInOrg; +pub mod reports; pub mod user_metadata; pub use self::user_metadata::UserMetadata; pub mod user_paged_response; diff --git a/src/models/reports.rs b/src/models/reports.rs new file mode 100644 index 0000000..56c2376 --- /dev/null +++ b/src/models/reports.rs @@ -0,0 +1,168 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Default)] +pub struct ReportPagination { + pub page_size: Option, + pub page_number: Option, +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub(crate) struct FetchReportQuery { + pub(crate) report_interval: ReportInterval, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) page_size: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) page_number: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum OrgReportType { + Attrition, + Growth, + Reengagement, + Churn, +} + +impl OrgReportType { + pub fn as_str(&self) -> &'static str { + match self { + OrgReportType::Attrition => "attrition", + OrgReportType::Growth => "growth", + OrgReportType::Reengagement => "reengagement", + OrgReportType::Churn => "churn", + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum UserReportType { + TopInviter, + Champion, + Reengagement, + Churn, +} + +impl UserReportType { + pub fn as_str(&self) -> &'static str { + match self { + UserReportType::TopInviter => "top_inviter", + UserReportType::Champion => "champion", + UserReportType::Reengagement => "reengagement", + UserReportType::Churn => "churn", + } + } +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +#[serde(untagged)] +pub(crate) enum ReportInterval { + TopInviter(TopInviterReportInterval), + Champion(ChampionReportInterval), + Reengagement(ReengagementReportInterval), + Churn(ChurnReportInterval), + Attrition(AttritionReportInterval), + Growth(GrowthReportInterval), +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub enum TopInviterReportInterval { + #[serde(rename = "30")] + ThirtyDays, + #[serde(rename = "60")] + SixtyDays, + #[serde(rename = "90")] + NinetyDays, +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub enum ChampionReportInterval { + #[serde(rename = "30")] + ThirtyDays, + #[serde(rename = "60")] + SixtyDays, + #[serde(rename = "90")] + NinetyDays, +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub enum ReengagementReportInterval { + #[serde(rename = "Weekly")] + Weekly, + #[serde(rename = "Monthly")] + Monthly, +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub enum ChurnReportInterval { + #[serde(rename = "7")] + SevenDays, + #[serde(rename = "14")] + FourteenDays, + #[serde(rename = "30")] + ThirtyDays, +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub enum AttritionReportInterval { + #[serde(rename = "30")] + ThirtyDays, + #[serde(rename = "60")] + SixtyDays, + #[serde(rename = "90")] + NinetyDays, +} + +#[derive(Clone, Debug, PartialEq, Serialize)] +pub enum GrowthReportInterval { + #[serde(rename = "30")] + ThirtyDays, + #[serde(rename = "60")] + SixtyDays, + #[serde(rename = "90")] + NinetyDays, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct OrgReportRecord { + pub id: String, + pub report_id: String, + pub org_id: String, + pub name: String, + pub num_users: i32, + pub org_created_at: i64, + pub extra_properties: Option, +} + +#[derive(Deserialize)] +pub struct OrgReport { + pub org_reports: Vec, + pub current_page: i64, + pub total_count: i64, + pub page_size: i64, + pub has_more_results: bool, + pub report_time: i64, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct UserReportRecord { + pub id: String, + pub report_id: String, + pub user_id: String, + pub user_created_at: i64, + pub username: Option, + pub first_name: Option, + pub last_name: Option, + pub email: String, + pub last_active_at: i64, + pub org_data: serde_json::Value, + pub extra_properties: Option, +} +#[derive(Deserialize)] +pub struct UserReportPage { + pub user_reports: Vec, + pub current_page: i64, + pub total_count: i64, + pub page_size: i64, + pub has_more_results: bool, + pub report_time: i64, +} diff --git a/src/propelauth/auth.rs b/src/propelauth/auth.rs index 71b8b87..f604a8b 100644 --- a/src/propelauth/auth.rs +++ b/src/propelauth/auth.rs @@ -5,14 +5,15 @@ use crate::apis::configuration::Configuration; use crate::models::AuthTokenVerificationMetadata; use crate::propelauth::access_token::AccessTokenService; use crate::propelauth::api_key::ApiKeyService; +use crate::propelauth::employee::EmployeeService; use crate::propelauth::errors::InitializationError; use crate::propelauth::helpers::map_autogenerated_error; use crate::propelauth::mfa::MfaService; use crate::propelauth::options::{AuthOptions, AuthOptionsWithTokenVerification}; use crate::propelauth::org::OrgService; +use crate::propelauth::reports::ReportService; use crate::propelauth::token::TokenService; use crate::propelauth::user::UserService; -use crate::propelauth::employee::EmployeeService; static BACKEND_API_BASE_URL: &str = "https://propelauth-api.com"; pub(crate) static AUTH_HOSTNAME_HEADER: &str = "X-Propelauth-url"; @@ -124,12 +125,19 @@ impl PropelAuth { } } - /// API requests related to employees. + /// API requests related to mfa. pub fn mfa(&self) -> MfaService { MfaService { config: &self.config, } } + + /// API requests related to reports. + pub fn reports(&self) -> ReportService { + ReportService { + config: &self.config, + } + } } fn validate_auth_url_extract_hostname(auth_url: &str) -> Result { diff --git a/src/propelauth/errors.rs b/src/propelauth/errors.rs index 7f79515..ce8bdf4 100644 --- a/src/propelauth/errors.rs +++ b/src/propelauth/errors.rs @@ -1,8 +1,8 @@ use crate::models::{ BadCreateAccessTokenError, BadCreateMagicLinkRequest, BadCreateOrgRequest, BadCreateUserRequest, BadFetchOrgQuery, BadFetchUsersByQuery, BadFetchUsersInOrgQuery, - BadMigrateUserRequest, BadMigrateUserPasswordRequest, BadUpdateOrgRequest, BadUpdatePasswordRequest, - BadUpdateUserEmailRequest, BadUpdateUserMetadataRequest, + BadMigrateUserPasswordRequest, BadMigrateUserRequest, BadUpdateOrgRequest, + BadUpdatePasswordRequest, BadUpdateUserEmailRequest, BadUpdateUserMetadataRequest, }; use thiserror::Error; @@ -462,7 +462,7 @@ pub enum VerifyStepUpGrantError { #[derive(Error, Debug, PartialEq, Clone)] pub enum SendSmsCodeError { - #[error("Invalid API Key")] + #[error("Invalid API Key")] InvalidApiKey, #[error("Rate limited by PropelAuth")] @@ -511,3 +511,14 @@ pub enum VerifySmsChallengeError { UnexpectedException, } +#[derive(Error, Debug, PartialEq, Clone)] +pub enum FetchReportError { + #[error("Invalid API Key")] + InvalidApiKey, + + #[error("Rate limited by PropelAuth")] + PropelAuthRateLimit, + + #[error("Unexpected exception, please try again")] + UnexpectedException, +} diff --git a/src/propelauth/mfa.rs b/src/propelauth/mfa.rs index c83c014..c3e50fc 100644 --- a/src/propelauth/mfa.rs +++ b/src/propelauth/mfa.rs @@ -1,11 +1,13 @@ use crate::apis::configuration::Configuration; -use crate::apis::mfa_service_api::{StepUpMfaError, VerifySmsChallengeParams, VerifyStepUpGrantParams}; +use crate::apis::mfa_service_api::{SendSmsMfaCodeParams, VerifyTotpChallengeParams}; +use crate::apis::mfa_service_api::{VerifySmsChallengeParams, VerifyStepUpGrantParams}; use crate::apis::Error; -use crate::models::{SendSmsCodeResponse, VerifySmsChallengeResponse, VerifyStepUpGrantResponse}; -use crate::propelauth::errors::{VerifyStepUpGrantError, VerifyStepUpTotpChallengeError, SendSmsCodeError, VerifySmsChallengeError}; -use crate::propelauth::helpers::map_autogenerated_error; use crate::models::VerifyTotpChallengeResponse; -use crate::apis::mfa_service_api::{SendSmsMfaCodeParams, VerifyTotpChallengeParams}; +use crate::models::{SendSmsCodeResponse, VerifySmsChallengeResponse, VerifyStepUpGrantResponse}; +use crate::propelauth::errors::{ + SendSmsCodeError, VerifySmsChallengeError, VerifyStepUpGrantError, + VerifyStepUpTotpChallengeError, +}; pub struct MfaService<'a> { pub(crate) config: &'a Configuration, @@ -66,8 +68,7 @@ impl MfaService<'_> { &self, params: VerifyStepUpGrantParams, ) -> Result { - let result = - crate::apis::mfa_service_api::verify_step_up_grant(&self.config, params).await; + let result = crate::apis::mfa_service_api::verify_step_up_grant(&self.config, params).await; match result { Ok(_) => Ok(VerifyStepUpGrantResponse { success: true }), @@ -85,23 +86,24 @@ impl MfaService<'_> { { match error_code { "invalid_request_fields" => { - if let Some(field_to_errors) = - error_json.get("field_to_errors").and_then(|v| v.as_object()) + if let Some(field_to_errors) = error_json + .get("field_to_errors") + .and_then(|v| v.as_object()) { if let Some(grant_error) = field_to_errors.get("grant").and_then(|v| v.as_str()) { if grant_error == "grant_not_found" { - return Ok(VerifyStepUpGrantResponse { success: false }); + return Ok(VerifyStepUpGrantResponse { + success: false, + }); } } } return Err(VerifyStepUpGrantError::BadRequest(response.content)); } - "feature_gated" => { - return Err(VerifyStepUpGrantError::FeatureGated) - } + "feature_gated" => return Err(VerifyStepUpGrantError::FeatureGated), _ => {} } } @@ -117,8 +119,7 @@ impl MfaService<'_> { &self, params: SendSmsMfaCodeParams, ) -> Result { - let result = - crate::apis::mfa_service_api::send_sms_mfa_code(&self.config, params).await; + let result = crate::apis::mfa_service_api::send_sms_mfa_code(&self.config, params).await; match result { Ok(response) => Ok(response), @@ -135,20 +136,12 @@ impl MfaService<'_> { if let Some(error_code) = error_json.get("error_code").and_then(|v| v.as_str()) { match error_code { - "user_not_found" => { - return Err(SendSmsCodeError::UserNotFound) - } - "mfa_not_enabled" => { - return Err(SendSmsCodeError::MfaNotEnabled) - } + "user_not_found" => return Err(SendSmsCodeError::UserNotFound), + "mfa_not_enabled" => return Err(SendSmsCodeError::MfaNotEnabled), "invalid_request_fields" => { - return Err(SendSmsCodeError::BadRequest( - response.content, - )) - } - "feature_gated" => { - return Err(SendSmsCodeError::FeatureGated) + return Err(SendSmsCodeError::BadRequest(response.content)) } + "feature_gated" => return Err(SendSmsCodeError::FeatureGated), _ => {} } } @@ -163,8 +156,7 @@ impl MfaService<'_> { &self, params: VerifySmsChallengeParams, ) -> Result { - let result = - crate::apis::mfa_service_api::verify_sms_challenge(&self.config, params).await; + let result = crate::apis::mfa_service_api::verify_sms_challenge(&self.config, params).await; match result { Ok(response) => Ok(response), @@ -181,20 +173,14 @@ impl MfaService<'_> { if let Some(error_code) = error_json.get("error_code").and_then(|v| v.as_str()) { match error_code { - "user_not_found" => { - return Err(VerifySmsChallengeError::UserNotFound) - } + "user_not_found" => return Err(VerifySmsChallengeError::UserNotFound), "mfa_not_enabled" => { return Err(VerifySmsChallengeError::MfaNotEnabled) } "invalid_request_fields" => { - return Err(VerifySmsChallengeError::BadRequest( - response.content, - )) - } - "feature_gated" => { - return Err(VerifySmsChallengeError::FeatureGated) + return Err(VerifySmsChallengeError::BadRequest(response.content)) } + "feature_gated" => return Err(VerifySmsChallengeError::FeatureGated), _ => {} } } diff --git a/src/propelauth/mod.rs b/src/propelauth/mod.rs index 2d69864..01c9b34 100644 --- a/src/propelauth/mod.rs +++ b/src/propelauth/mod.rs @@ -1,12 +1,13 @@ +pub mod access_token; pub mod api_key; pub mod auth; +pub mod employee; pub mod errors; pub(crate) mod helpers; +pub mod mfa; pub mod options; pub mod org; +pub mod reports; pub mod token; pub mod token_models; pub mod user; -pub mod access_token; -pub mod employee; -pub mod mfa; \ No newline at end of file diff --git a/src/propelauth/reports.rs b/src/propelauth/reports.rs new file mode 100644 index 0000000..3e00887 --- /dev/null +++ b/src/propelauth/reports.rs @@ -0,0 +1,262 @@ +use crate::apis::configuration::Configuration; +use crate::apis::Error; +use crate::models::reports::{ + AttritionReportInterval, ChampionReportInterval, ChurnReportInterval, FetchReportQuery, + GrowthReportInterval, OrgReportType, ReengagementReportInterval, ReportInterval, + ReportPagination, TopInviterReportInterval, UserReportPage, UserReportType, +}; +use crate::propelauth::errors::FetchReportError; + +pub struct ReportService<'a> { + pub(crate) config: &'a Configuration, +} + +impl ReportService<'_> { + pub async fn fetch_user_top_inviter_report( + &self, + report_interval: TopInviterReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_user_report( + &self.config, + UserReportType::TopInviter, + FetchReportQuery { + report_interval: ReportInterval::TopInviter(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_user_champion_report( + &self, + report_interval: ChampionReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_user_report( + &self.config, + UserReportType::Champion, + FetchReportQuery { + report_interval: ReportInterval::Champion(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_user_reengagement_report( + &self, + report_interval: ReengagementReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_user_report( + &self.config, + UserReportType::Reengagement, + FetchReportQuery { + report_interval: ReportInterval::Reengagement(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_user_churn_report( + &self, + report_interval: ChurnReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_user_report( + &self.config, + UserReportType::Churn, + FetchReportQuery { + report_interval: ReportInterval::Churn(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_org_attrition_report( + &self, + report_interval: AttritionReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_org_report( + &self.config, + OrgReportType::Attrition, + FetchReportQuery { + report_interval: ReportInterval::Attrition(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_org_growth_report( + &self, + report_interval: GrowthReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_org_report( + &self.config, + OrgReportType::Growth, + FetchReportQuery { + report_interval: ReportInterval::Growth(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_org_reengagement_report( + &self, + report_interval: ReengagementReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_org_report( + &self.config, + OrgReportType::Reengagement, + FetchReportQuery { + report_interval: ReportInterval::Reengagement(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } + + pub async fn fetch_org_churn_report( + &self, + report_interval: ChurnReportInterval, + pagination: ReportPagination, + ) -> Result { + let result = crate::apis::report_service_api::fetch_org_report( + &self.config, + OrgReportType::Churn, + FetchReportQuery { + report_interval: ReportInterval::Churn(report_interval), + page_size: pagination.page_size, + page_number: pagination.page_number, + }, + ) + .await; + + match result { + Ok(response) => Ok(response), + Err(Error::ResponseError(response)) => { + if response.status == 401 { + return Err(FetchReportError::InvalidApiKey); + } else if response.status == 429 { + return Err(FetchReportError::PropelAuthRateLimit); + } else { + Err(FetchReportError::UnexpectedException) + } + } + Err(_) => Err(FetchReportError::UnexpectedException), + } + } +}