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.9.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.9.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        "1.7.9" => include_str!("../release/edgee.v1.7.9.js").trim(),
106        // Add more versions as needed
107        _ => return Ok(default_version),
108    };
109
110    Ok(content)
111}
112
113fn get_shopify_sdk_content(url: &str) -> Result<&str, &'static str> {
114    let default_version = include_str!("../release/shopify.v1.7.9.js").trim();
115    if url.ends_with("shopify.js") {
116        return Ok(default_version);
117    }
118
119    let Some((_, part)) = url.rsplit_once("shopify.v") else {
120        return Ok(default_version);
121    };
122    let Some(part) = part.strip_suffix(".js") else {
123        return Ok(default_version);
124    };
125
126    let content = match part {
127        "1.7.8" => include_str!("../release/shopify.v1.7.8.js").trim(),
128        "1.7.9" => include_str!("../release/shopify.v1.7.9.js").trim(),
129        // Add more versions as needed
130        _ => return Ok(default_version),
131    };
132    Ok(content)
133}
134
135fn dynamize_sdk(
136    sdk: &str,
137    host: &str,
138    autocapture: Autocapture,
139    edgee_cookie: &str,
140    edgee_cookie_domain: &str,
141) -> String {
142    let token = token::generate(host);
143    let mut sdk = sdk.replace("{{token}}", token.as_str());
144    sdk = sdk.replace("{{ecn}}", edgee_cookie);
145    sdk = sdk.replace("{{ecd}}", edgee_cookie_domain);
146
147    if !autocapture.pageview {
148        sdk = sdk.replace("{{ac_pageview}}", "n");
149    }
150    if autocapture.spa_pageview {
151        sdk = sdk.replace("{{ac_spa_pageview}}", "y");
152    }
153    if autocapture.engagement {
154        sdk = sdk.replace("{{ac_engagement}}", "y");
155    }
156    if autocapture.click {
157        sdk = sdk.replace("{{ac_click}}", "y");
158    }
159    if autocapture.form {
160        sdk = sdk.replace("{{ac_form}}", "y");
161    }
162    if autocapture.scroll {
163        sdk = sdk.replace("{{ac_scroll}}", "y");
164    }
165    sdk.to_string()
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn retrieves_sdk_content_for_valid_url() {
174        let url = "/sdk.js";
175        let host = "example.com";
176        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
177        assert!(result.is_ok());
178        assert!(result.unwrap().contains("{{side}}"));
179    }
180
181    #[test]
182    fn retrieves_sdk_with_custom_edgee_cookie_name() {
183        let url = "/sdk.js";
184        let host = "example.com";
185        let result = get_sdk(
186            url,
187            host,
188            Autocapture::default(),
189            "test_edgee_cookie_name",
190            "example.com",
191        );
192        assert!(result.is_ok());
193        assert!(result.unwrap().contains("test_edgee_cookie_name"));
194    }
195
196    #[test]
197    fn retrieves_versioned_sdk_content() {
198        let url = "/edgee.v1.5.0.js";
199        let host = "example.com";
200        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
201        assert!(result.is_ok());
202        assert!(result.unwrap().contains("{{side}}"));
203    }
204    #[test]
205    fn returns_error_for_invalid_url_format() {
206        let url = "/invalid.js";
207        let host = "example.com";
208        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
209        assert!(result.is_ok());
210    }
211
212    #[test]
213    fn returns_error_for_unsupported_version() {
214        let url = "/edgee.v2.0.0.js";
215        let host = "example.com";
216        let result = get_sdk(url, host, Autocapture::default(), "edgee", "example.com");
217        assert!(result.is_ok());
218    }
219}