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
34pub fn get_sdk(
61 url: &str,
62 host: &str,
63 autocapture: Autocapture,
64 edgee_cookie: &str,
65 edgee_cookie_domain: &str,
66 cookies_for_sdk_injection: Option<String>,
67) -> Result<String, &'static str> {
68 let content = match url.contains("shopify.") {
69 true => get_shopify_sdk_content(url)?,
70 false => get_sdk_content(url)?,
71 };
72 Ok(dynamize_sdk(
73 content,
74 host,
75 autocapture,
76 edgee_cookie,
77 edgee_cookie_domain,
78 cookies_for_sdk_injection,
79 ))
80}
81
82pub fn get_sdk_for_unsafe_user(url: &str) -> Result<String, &'static str> {
83 let content = match url.contains("shopify.") {
84 true => get_shopify_sdk_content(url)?,
85 false => get_sdk_content(url)?,
86 };
87 Ok(content.replace("{{safe}}", "unsafe"))
88}
89
90fn get_sdk_content(url: &str) -> Result<&str, &'static str> {
91 let default_version = include_str!("../release/edgee.v1.8.0.js").trim();
92 if url.ends_with("sdk.js") {
93 return Ok(default_version);
94 }
95
96 let Some((_, part)) = url.rsplit_once("edgee.v") else {
97 return Ok(default_version);
98 };
99 let Some(part) = part.strip_suffix(".js") else {
100 return Ok(default_version);
101 };
102
103 let content = match part {
104 "1.7.6" => include_str!("../release/edgee.v1.7.6.js").trim(),
105 "1.7.7" => include_str!("../release/edgee.v1.7.7.js").trim(),
106 "1.7.8" => include_str!("../release/edgee.v1.7.8.js").trim(),
107 "1.7.9" => include_str!("../release/edgee.v1.7.9.js").trim(),
108 "1.8.0" => include_str!("../release/edgee.v1.8.0.js").trim(),
109 _ => return Ok(default_version),
111 };
112
113 Ok(content)
114}
115
116fn get_shopify_sdk_content(url: &str) -> Result<&str, &'static str> {
117 let default_version = include_str!("../release/shopify.v1.7.9.js").trim();
118 if url.ends_with("shopify.js") {
119 return Ok(default_version);
120 }
121
122 let Some((_, part)) = url.rsplit_once("shopify.v") else {
123 return Ok(default_version);
124 };
125 let Some(part) = part.strip_suffix(".js") else {
126 return Ok(default_version);
127 };
128
129 let content = match part {
130 "1.7.8" => include_str!("../release/shopify.v1.7.8.js").trim(),
131 "1.7.9" => include_str!("../release/shopify.v1.7.9.js").trim(),
132 "1.8.0" => include_str!("../release/shopify.v1.8.0.js").trim(),
133 _ => return Ok(default_version),
135 };
136 Ok(content)
137}
138
139fn dynamize_sdk(
140 sdk: &str,
141 host: &str,
142 autocapture: Autocapture,
143 edgee_cookie: &str,
144 edgee_cookie_domain: &str,
145 cookies_for_sdk_injection: Option<String>,
146) -> String {
147 let token = token::generate(host);
148 let mut sdk = sdk.replace("{{token}}", token.as_str());
149 sdk = sdk.replace("{{ecn}}", edgee_cookie);
150 sdk = sdk.replace("{{ecd}}", edgee_cookie_domain);
151
152 let cookies_for_sdk_injection = cookies_for_sdk_injection.unwrap_or("".to_string());
153 sdk = sdk.replace("{{ctf}}", cookies_for_sdk_injection.as_str());
154
155 if !autocapture.pageview {
156 sdk = sdk.replace("{{ac_pageview}}", "n");
157 }
158 if autocapture.spa_pageview {
159 sdk = sdk.replace("{{ac_spa_pageview}}", "y");
160 }
161 if autocapture.engagement {
162 sdk = sdk.replace("{{ac_engagement}}", "y");
163 }
164 if autocapture.click {
165 sdk = sdk.replace("{{ac_click}}", "y");
166 }
167 if autocapture.form {
168 sdk = sdk.replace("{{ac_form}}", "y");
169 }
170 if autocapture.scroll {
171 sdk = sdk.replace("{{ac_scroll}}", "y");
172 }
173 sdk.to_string()
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn retrieves_sdk_content_for_valid_url() {
182 let url = "/sdk.js";
183 let host = "example.com";
184 let result = get_sdk(
185 url,
186 host,
187 Autocapture::default(),
188 "edgee",
189 "example.com",
190 None,
191 );
192 assert!(result.is_ok());
193 assert!(result.unwrap().contains("{{side}}"));
194 }
195
196 #[test]
197 fn retrieves_sdk_with_custom_edgee_cookie_name() {
198 let url = "/sdk.js";
199 let host = "example.com";
200 let result = get_sdk(
201 url,
202 host,
203 Autocapture::default(),
204 "test_edgee_cookie_name",
205 "example.com",
206 None,
207 );
208 assert!(result.is_ok());
209 assert!(result.unwrap().contains("test_edgee_cookie_name"));
210 }
211
212 #[test]
213 fn retrieves_versioned_sdk_content() {
214 let url = "/edgee.v1.5.0.js";
215 let host = "example.com";
216 let result = get_sdk(
217 url,
218 host,
219 Autocapture::default(),
220 "edgee",
221 "example.com",
222 None,
223 );
224 assert!(result.is_ok());
225 assert!(result.unwrap().contains("{{side}}"));
226 }
227 #[test]
228 fn returns_error_for_invalid_url_format() {
229 let url = "/invalid.js";
230 let host = "example.com";
231 let result = get_sdk(
232 url,
233 host,
234 Autocapture::default(),
235 "edgee",
236 "example.com",
237 None,
238 );
239 assert!(result.is_ok());
240 }
241
242 #[test]
243 fn returns_error_for_unsupported_version() {
244 let url = "/edgee.v2.0.0.js";
245 let host = "example.com";
246 let result = get_sdk(
247 url,
248 host,
249 Autocapture::default(),
250 "edgee",
251 "example.com",
252 None,
253 );
254 assert!(result.is_ok());
255 }
256
257 #[test]
258 fn dynamizes_sdk_with_cookies_for_sdk_injection() {
259 let url = "/edgee.v2.0.0.js";
260 let host = "example.com";
261 let cookies_for_sdk_injection = Some("s,api_key_test".to_string());
262 let result = get_sdk(
263 url,
264 host,
265 Autocapture::default(),
266 "edgee",
267 "example.com",
268 cookies_for_sdk_injection,
269 );
270 assert!(result.is_ok());
271 assert!(result.unwrap().contains("s,api_key_test"));
272 }
273}