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