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.3.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 "1.8.1" => include_str!("../release/edgee.v1.8.1.js").trim(),
110 "1.8.2" => include_str!("../release/edgee.v1.8.2.js").trim(),
111 "1.8.3" => include_str!("../release/edgee.v1.8.3.js").trim(),
112 _ => return Ok(default_version),
114 };
115
116 Ok(content)
117}
118
119fn get_shopify_sdk_content(url: &str) -> Result<&str, &'static str> {
120 let default_version = include_str!("../release/shopify.v1.8.3.js").trim();
121 if url.ends_with("shopify.js") {
122 return Ok(default_version);
123 }
124
125 let Some((_, part)) = url.rsplit_once("shopify.v") else {
126 return Ok(default_version);
127 };
128 let Some(part) = part.strip_suffix(".js") else {
129 return Ok(default_version);
130 };
131
132 let content = match part {
133 "1.7.8" => include_str!("../release/shopify.v1.7.8.js").trim(),
134 "1.7.9" => include_str!("../release/shopify.v1.7.9.js").trim(),
135 "1.8.0" => include_str!("../release/shopify.v1.8.0.js").trim(),
136 "1.8.1" => include_str!("../release/shopify.v1.8.1.js").trim(),
137 "1.8.2" => include_str!("../release/shopify.v1.8.2.js").trim(),
138 "1.8.3" => include_str!("../release/shopify.v1.8.3.js").trim(),
139 _ => return Ok(default_version),
141 };
142 Ok(content)
143}
144
145fn dynamize_sdk(
146 sdk: &str,
147 host: &str,
148 autocapture: Autocapture,
149 edgee_cookie: &str,
150 edgee_cookie_domain: &str,
151 cookies_for_sdk_injection: Option<String>,
152) -> String {
153 let token = token::generate(host);
154 let mut sdk = sdk.replace("{{token}}", token.as_str());
155 sdk = sdk.replace("{{ecn}}", edgee_cookie);
156 sdk = sdk.replace("{{ecd}}", edgee_cookie_domain);
157
158 let cookies_for_sdk_injection = cookies_for_sdk_injection.unwrap_or("".to_string());
159 sdk = sdk.replace("{{ctf}}", cookies_for_sdk_injection.as_str());
160
161 if !autocapture.pageview {
162 sdk = sdk.replace("{{ac_pageview}}", "n");
163 }
164 if autocapture.spa_pageview {
165 sdk = sdk.replace("{{ac_spa_pageview}}", "y");
166 }
167 if autocapture.engagement {
168 sdk = sdk.replace("{{ac_engagement}}", "y");
169 }
170 if autocapture.click {
171 sdk = sdk.replace("{{ac_click}}", "y");
172 }
173 if autocapture.form {
174 sdk = sdk.replace("{{ac_form}}", "y");
175 }
176 if autocapture.scroll {
177 sdk = sdk.replace("{{ac_scroll}}", "y");
178 }
179 sdk.to_string()
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185
186 #[test]
187 fn retrieves_sdk_content_for_valid_url() {
188 let url = "/sdk.js";
189 let host = "example.com";
190 let result = get_sdk(
191 url,
192 host,
193 Autocapture::default(),
194 "edgee",
195 "example.com",
196 None,
197 );
198 assert!(result.is_ok());
199 assert!(result.unwrap().contains("{{side}}"));
200 }
201
202 #[test]
203 fn retrieves_sdk_with_custom_edgee_cookie_name() {
204 let url = "/sdk.js";
205 let host = "example.com";
206 let result = get_sdk(
207 url,
208 host,
209 Autocapture::default(),
210 "test_edgee_cookie_name",
211 "example.com",
212 None,
213 );
214 assert!(result.is_ok());
215 assert!(result.unwrap().contains("test_edgee_cookie_name"));
216 }
217
218 #[test]
219 fn retrieves_versioned_sdk_content() {
220 let url = "/edgee.v1.5.0.js";
221 let host = "example.com";
222 let result = get_sdk(
223 url,
224 host,
225 Autocapture::default(),
226 "edgee",
227 "example.com",
228 None,
229 );
230 assert!(result.is_ok());
231 assert!(result.unwrap().contains("{{side}}"));
232 }
233 #[test]
234 fn returns_error_for_invalid_url_format() {
235 let url = "/invalid.js";
236 let host = "example.com";
237 let result = get_sdk(
238 url,
239 host,
240 Autocapture::default(),
241 "edgee",
242 "example.com",
243 None,
244 );
245 assert!(result.is_ok());
246 }
247
248 #[test]
249 fn returns_error_for_unsupported_version() {
250 let url = "/edgee.v2.0.0.js";
251 let host = "example.com";
252 let result = get_sdk(
253 url,
254 host,
255 Autocapture::default(),
256 "edgee",
257 "example.com",
258 None,
259 );
260 assert!(result.is_ok());
261 }
262
263 #[test]
264 fn dynamizes_sdk_with_cookies_for_sdk_injection() {
265 let url = "/edgee.v2.0.0.js";
266 let host = "example.com";
267 let cookies_for_sdk_injection = Some("s,api_key_test".to_string());
268 let result = get_sdk(
269 url,
270 host,
271 Autocapture::default(),
272 "edgee",
273 "example.com",
274 cookies_for_sdk_injection,
275 );
276 assert!(result.is_ok());
277 assert!(result.unwrap().contains("s,api_key_test"));
278 }
279}