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