Skip to main content

posthog_rs/
endpoints.rs

1use std::fmt;
2
3/// US ingestion endpoint
4pub const US_INGESTION_ENDPOINT: &str = "https://us.i.posthog.com";
5
6/// EU ingestion endpoint  
7pub const EU_INGESTION_ENDPOINT: &str = "https://eu.i.posthog.com";
8
9/// Default host (US by default)
10pub const DEFAULT_HOST: &str = US_INGESTION_ENDPOINT;
11
12/// API endpoints for different operations
13#[derive(Debug, Clone)]
14pub enum Endpoint {
15    /// Event capture endpoint
16    Capture,
17    /// Batch event capture endpoint
18    Batch,
19    /// Feature flags endpoint
20    Flags,
21    /// Local evaluation endpoint
22    LocalEvaluation,
23}
24
25impl Endpoint {
26    /// Get the path for this endpoint
27    pub fn path(&self) -> &str {
28        match self {
29            Endpoint::Capture => "/i/v0/e/",
30            Endpoint::Batch => "/batch/",
31            Endpoint::Flags => "/flags/?v=2",
32            Endpoint::LocalEvaluation => "/api/feature_flag/local_evaluation/?send_cohorts",
33        }
34    }
35}
36
37impl fmt::Display for Endpoint {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "{}", self.path())
40    }
41}
42
43/// Manages PostHog API endpoints and host configuration
44#[derive(Debug, Clone)]
45pub struct EndpointManager {
46    base_host: String,
47    raw_host: String,
48}
49
50impl EndpointManager {
51    /// Create a new endpoint manager with the given host
52    pub fn new(host: Option<String>) -> Self {
53        let raw_host = host.clone().unwrap_or_else(|| DEFAULT_HOST.to_string());
54        let base_host = Self::determine_server_host(host);
55
56        Self {
57            base_host,
58            raw_host,
59        }
60    }
61
62    /// Determine the actual server host based on the provided host
63    /// Similar to posthog-python's determine_server_host function
64    pub fn determine_server_host(host: Option<String>) -> String {
65        let host_or_default = host.unwrap_or_else(|| DEFAULT_HOST.to_string());
66        let trimmed_host = host_or_default.trim_end_matches('/');
67
68        match trimmed_host {
69            "https://app.posthog.com" | "https://us.posthog.com" => {
70                US_INGESTION_ENDPOINT.to_string()
71            }
72            "https://eu.posthog.com" => EU_INGESTION_ENDPOINT.to_string(),
73            _ => trimmed_host.to_string(),
74        }
75    }
76
77    /// Get the base host URL (for constructing endpoints)
78    pub fn base_host(&self) -> &str {
79        &self.base_host
80    }
81
82    /// Get the raw host (as provided by the user, used for session replay URLs)
83    pub fn raw_host(&self) -> &str {
84        &self.raw_host
85    }
86
87    /// Build a full URL for a given endpoint
88    pub fn build_url(&self, endpoint: Endpoint) -> String {
89        format!(
90            "{}{}",
91            self.base_host.trim_end_matches('/'),
92            endpoint.path()
93        )
94    }
95
96    /// Build a URL with a custom path
97    pub fn build_custom_url(&self, path: &str) -> String {
98        let normalized_path = if path.starts_with('/') {
99            path.to_string()
100        } else {
101            format!("/{path}")
102        };
103        format!(
104            "{}{}",
105            self.base_host.trim_end_matches('/'),
106            normalized_path
107        )
108    }
109
110    /// Build the local evaluation URL with a token
111    pub fn build_local_eval_url(&self, token: &str) -> String {
112        format!(
113            "{}/api/feature_flag/local_evaluation/?token={}&send_cohorts",
114            self.base_host.trim_end_matches('/'),
115            token
116        )
117    }
118
119    /// Get the base host for API operations (without the path)
120    pub fn api_host(&self) -> String {
121        self.base_host.trim_end_matches('/').to_string()
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_determine_server_host() {
131        assert_eq!(
132            EndpointManager::determine_server_host(None),
133            US_INGESTION_ENDPOINT
134        );
135
136        assert_eq!(
137            EndpointManager::determine_server_host(Some("https://app.posthog.com".to_string())),
138            US_INGESTION_ENDPOINT
139        );
140
141        assert_eq!(
142            EndpointManager::determine_server_host(Some("https://us.posthog.com".to_string())),
143            US_INGESTION_ENDPOINT
144        );
145
146        assert_eq!(
147            EndpointManager::determine_server_host(Some("https://eu.posthog.com".to_string())),
148            EU_INGESTION_ENDPOINT
149        );
150
151        assert_eq!(
152            EndpointManager::determine_server_host(Some("https://custom.domain.com".to_string())),
153            "https://custom.domain.com"
154        );
155    }
156
157    #[test]
158    fn test_build_url() {
159        let manager = EndpointManager::new(None);
160
161        assert_eq!(
162            manager.build_url(Endpoint::Capture),
163            format!("{}/i/v0/e/", US_INGESTION_ENDPOINT)
164        );
165
166        assert_eq!(
167            manager.build_url(Endpoint::Flags),
168            format!("{}/flags/?v=2", US_INGESTION_ENDPOINT)
169        );
170    }
171
172    #[test]
173    fn test_build_custom_url() {
174        let manager = EndpointManager::new(Some("https://custom.com/".to_string()));
175
176        assert_eq!(
177            manager.build_custom_url("/api/test"),
178            "https://custom.com/api/test"
179        );
180
181        assert_eq!(
182            manager.build_custom_url("api/test"),
183            "https://custom.com/api/test"
184        );
185    }
186}