edgee_sdk/
lib.rs

1use serde::{Deserialize, Serialize};
2
3pub mod token;
4
5#[derive(Debug, Serialize, Deserialize, Clone)]
6pub struct Autocapture {
7    #[serde(default)]
8    pub pageview: bool,
9    #[serde(default)]
10    pub spa_pageview: bool,
11    #[serde(default)]
12    pub engagement: bool,
13    #[serde(default)]
14    pub click: bool,
15    #[serde(default)]
16    pub form: bool,
17    #[serde(default)]
18    pub scroll: bool,
19}
20
21impl Default for Autocapture {
22    fn default() -> Self {
23        Self {
24            pageview: true,
25            spa_pageview: false,
26            engagement: false,
27            click: false,
28            form: false,
29            scroll: false,
30        }
31    }
32}
33
34/// Retrieves and customizes the SDK content.
35///
36/// This function gets the SDK content based on the URL and customizes it for the given host and autocapture settings.
37/// For URLs ending in "sdk.js", it returns the latest version. Otherwise, it extracts and returns a specific version.
38///
39/// # Arguments
40///
41/// * `url` - A string slice containing the URL path to get the SDK from (e.g. "/_edgee/sdk.js" or "/_edgee/edgee.v1.5.0.js")
42/// * `host` - A string slice containing the host domain to customize the SDK for
43/// * `autocapture` - An Autocapture struct specifying which events to automatically capture
44/// * `edgee_cookie` - A string slice containing the name of the Edgee cookie
45///
46/// # Returns
47///
48/// * `Ok(String)` - The customized SDK content if successful
49/// * `Err(&'static str)` - An error message if the SDK content could not be retrieved
50///
51/// # Example
52///
53/// ```rust
54/// use edgee_sdk::Autocapture;
55/// let url = "/_edgee/edgee.v1.7.8.js";
56/// let host = "example.com";
57/// let sdk_content = edgee_sdk::get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
58/// assert!(sdk_content.is_ok());
59/// ```
60pub fn get_sdk(
61    url: &str,
62    host: &str,
63    autocapture: Autocapture,
64    edgee_cookie: &str,
65    edgee_cookie_domain: &str,
66) -> Result<String, &'static str> {
67    let content = match url.contains("shopify.") {
68        true => get_shopify_sdk_content(url)?,
69        false => get_sdk_content(url)?,
70    };
71    Ok(dynamize_sdk(
72        content,
73        host,
74        autocapture,
75        edgee_cookie,
76        edgee_cookie_domain,
77    ))
78}
79
80pub fn get_sdk_for_unsafe_user(url: &str) -> Result<String, &'static str> {
81    let content = match url.contains("shopify.") {
82        true => get_shopify_sdk_content(url)?,
83        false => get_sdk_content(url)?,
84    };
85    Ok(content.replace("{{safe}}", "unsafe"))
86}
87
88fn get_sdk_content(url: &str) -> Result<&str, &'static str> {
89    let default_version = include_str!("../release/edgee.v1.7.8.js").trim();
90    if url.ends_with("sdk.js") {
91        return Ok(default_version);
92    }
93
94    let Some((_, part)) = url.rsplit_once("edgee.v") else {
95        return Ok(default_version);
96    };
97    let Some(part) = part.strip_suffix(".js") else {
98        return Ok(default_version);
99    };
100
101    let content = match part {
102        "1.7.6" => include_str!("../release/edgee.v1.7.6.js").trim(),
103        "1.7.7" => include_str!("../release/edgee.v1.7.7.js").trim(),
104        "1.7.8" => include_str!("../release/edgee.v1.7.8.js").trim(),
105        // Add more versions as needed
106        _ => return Ok(default_version),
107    };
108
109    Ok(content)
110}
111
112fn get_shopify_sdk_content(url: &str) -> Result<&str, &'static str> {
113    let default_version = include_str!("../release/shopify.v1.7.8.js").trim();
114    if url.ends_with("shopify.js") {
115        return Ok(default_version);
116    }
117
118    let Some((_, part)) = url.rsplit_once("shopify.v") else {
119        return Ok(default_version);
120    };
121    let Some(part) = part.strip_suffix(".js") else {
122        return Ok(default_version);
123    };
124
125    let content = match part {
126        "1.7.8" => include_str!("../release/shopify.v1.7.8.js").trim(),
127        // Add more versions as needed
128        _ => return Ok(default_version),
129    };
130    Ok(content)
131}
132
133fn dynamize_sdk(
134    sdk: &str,
135    host: &str,
136    autocapture: Autocapture,
137    edgee_cookie: &str,
138    edgee_cookie_domain: &str,
139) -> String {
140    let token = token::generate(host);
141    let mut sdk = sdk.replace("{{token}}", token.as_str());
142    sdk = sdk.replace("{{ecn}}", edgee_cookie);
143    sdk = sdk.replace("{{ecd}}", edgee_cookie_domain);
144
145    if !autocapture.pageview {
146        sdk = sdk.replace("{{ac_pageview}}", "n");
147    }
148    if autocapture.spa_pageview {
149        sdk = sdk.replace("{{ac_spa_pageview}}", "y");
150    }
151    if autocapture.engagement {
152        sdk = sdk.replace("{{ac_engagement}}", "y");
153    }
154    if autocapture.click {
155        sdk = sdk.replace("{{ac_click}}", "y");
156    }
157    if autocapture.form {
158        sdk = sdk.replace("{{ac_form}}", "y");
159    }
160    if autocapture.scroll {
161        sdk = sdk.replace("{{ac_scroll}}", "y");
162    }
163    sdk.to_string()
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn retrieves_sdk_content_for_valid_url() {
172        let url = "/sdk.js";
173        let host = "example.com";
174        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
175        assert!(result.is_ok());
176        assert!(result.unwrap().contains("{{side}}"));
177    }
178
179    #[test]
180    fn retrieves_sdk_with_custom_edgee_cookie_name() {
181        let url = "/sdk.js";
182        let host = "example.com";
183        let result = get_sdk(
184            url,
185            host,
186            Autocapture::default(),
187            "test_edgee_cookie_name",
188            "example.com",
189        );
190        assert!(result.is_ok());
191        assert!(result.unwrap().contains("test_edgee_cookie_name"));
192    }
193
194    #[test]
195    fn retrieves_versioned_sdk_content() {
196        let url = "/edgee.v1.5.0.js";
197        let host = "example.com";
198        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
199        assert!(result.is_ok());
200        assert!(result.unwrap().contains("{{side}}"));
201    }
202    #[test]
203    fn returns_error_for_invalid_url_format() {
204        let url = "/invalid.js";
205        let host = "example.com";
206        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
207        assert!(result.is_ok());
208    }
209
210    #[test]
211    fn returns_error_for_unsupported_version() {
212        let url = "/edgee.v2.0.0.js";
213        let host = "example.com";
214        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
215        assert!(result.is_ok());
216    }
217}