Skip to content

Commit cfdcae8

Browse files
committed
feat: add identity_key to errors
1 parent 6d1a0dd commit cfdcae8

5 files changed

Lines changed: 83 additions & 7 deletions

File tree

src/batch_queue/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,7 @@ mod tests {
10951095
count: 3,
10961096
data_entry_id: Uuid::new_v4(),
10971097
session_id: None,
1098+
identity_key: None,
10981099
build_id: None,
10991100
created_at: Utc::now(),
11001101
},

src/handler/collect.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{
22
MODS_EVENT_FIELDS, check_ip_allowed, error_response, extract_known_fields, get_authorization,
33
get_client_ip, get_country, insert_error_entries, insert_mods_event, load_project_context,
4-
success_response,
4+
resolve_identity_key, success_response,
55
};
66
use crate::batch_queue::{FailedRequest, RequestType, TrackingContext};
77
use crate::models::{AppState, Request};
@@ -71,7 +71,7 @@ pub async fn collect(
7171
id,
7272
mut data,
7373
errors,
74-
session_id: _,
74+
session_id,
7575
} = req;
7676

7777
let server_id = match id.value().parse::<Uuid>() {
@@ -115,12 +115,21 @@ pub async fn collect(
115115
}
116116

117117
if let Some(errors) = errors {
118-
for error in errors {
118+
let fallback_identity = server_id.to_string();
119+
for mut error in errors {
120+
if error.session_id.is_none() {
121+
error.session_id = session_id.clone();
122+
}
123+
let identity_key = resolve_identity_key(
124+
error.session_id.as_deref(),
125+
Some(fallback_identity.as_str()),
126+
);
119127
if let Err(e) = insert_error_entries(
120128
&state.batch_queue,
121129
ctx.project_id,
122130
data_entry_id,
123131
error,
132+
identity_key,
124133
Some(tracking_ctx.clone()),
125134
)
126135
.await

src/handler/mod.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ pub async fn insert_error_entries(
492492
project_id: Uuid,
493493
data_entry_id: Uuid,
494494
data: ErrorTracking,
495+
identity_key: Option<String>,
495496
tracking_ctx: Option<TrackingContext>,
496497
) -> Result<(), HandlerResponse> {
497498
let mut error_rows = Vec::new();
@@ -517,6 +518,7 @@ pub async fn insert_error_entries(
517518
count: occurrence_count,
518519
data_entry_id,
519520
session_id: data.session_id.clone(),
521+
identity_key,
520522
build_id: data.build_id.clone(),
521523
created_at,
522524
};
@@ -537,6 +539,20 @@ pub async fn insert_error_entries(
537539
Ok(())
538540
}
539541

542+
pub fn resolve_identity_key(
543+
session_id: Option<&str>,
544+
fallback_identifier: Option<&str>,
545+
) -> Option<String> {
546+
session_id
547+
.filter(|value| !value.is_empty())
548+
.map(str::to_string)
549+
.or_else(|| {
550+
fallback_identifier
551+
.filter(|value| !value.is_empty())
552+
.map(str::to_string)
553+
})
554+
}
555+
540556
pub async fn process_failed_request(
541557
batch_queue: &BatchQueue,
542558
pool: &sqlx::PgPool,
@@ -565,7 +581,7 @@ async fn process_collect_request(
565581
id,
566582
mut data,
567583
errors,
568-
session_id: _,
584+
session_id,
569585
} = req;
570586

571587
let server_id = id
@@ -598,12 +614,21 @@ async fn process_collect_request(
598614
if ctx.error_tracking_enabled
599615
&& let Some(errors) = errors
600616
{
601-
for error in errors {
617+
let fallback_identity = server_id.to_string();
618+
for mut error in errors {
619+
if error.session_id.is_none() {
620+
error.session_id = session_id.clone();
621+
}
622+
let identity_key = resolve_identity_key(
623+
error.session_id.as_deref(),
624+
Some(fallback_identity.as_str()),
625+
);
602626
insert_error_entries(
603627
batch_queue,
604628
ctx.project_id,
605629
data_entry_id,
606630
error,
631+
identity_key,
607632
Some(tracking_ctx.clone()),
608633
)
609634
.await
@@ -694,6 +719,7 @@ async fn process_web_request(
694719
token: token.into(),
695720
organization_id: ctx.organization_id.as_deref().map(Into::into),
696721
};
722+
let fallback_identity = resolved_user_id.to_string();
697723

698724
let data_entry_id = insert_web_event(
699725
batch_queue,
@@ -714,11 +740,19 @@ async fn process_web_request(
714740
if error.session_id.is_none() {
715741
error.session_id = parsed.session_id.clone();
716742
}
743+
if error.build_id.is_none() {
744+
error.build_id = parsed.build_id.clone();
745+
}
746+
let identity_key = resolve_identity_key(
747+
error.session_id.as_deref(),
748+
Some(fallback_identity.as_str()),
749+
);
717750
insert_error_entries(
718751
batch_queue,
719752
ctx.project_id,
720753
data_entry_id,
721754
error,
755+
identity_key,
722756
Some(tracking_ctx.clone()),
723757
)
724758
.await
@@ -1102,4 +1136,29 @@ mod tests {
11021136
);
11031137
}
11041138
}
1139+
1140+
mod identity_resolution {
1141+
use super::*;
1142+
1143+
#[test]
1144+
fn prefers_session_id_when_present() {
1145+
assert_eq!(
1146+
resolve_identity_key(Some("session-1"), Some("fallback-1")),
1147+
Some("session-1".to_string())
1148+
);
1149+
}
1150+
1151+
#[test]
1152+
fn falls_back_when_session_missing() {
1153+
assert_eq!(
1154+
resolve_identity_key(None, Some("fallback-1")),
1155+
Some("fallback-1".to_string())
1156+
);
1157+
}
1158+
1159+
#[test]
1160+
fn ignores_empty_values() {
1161+
assert_eq!(resolve_identity_key(Some(""), Some("")), None);
1162+
}
1163+
}
11051164
}

src/handler/web.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::{
22
EncodingQuery, WEB_EVENT_FIELDS, check_ip_allowed, decompress_body, error_response,
33
extract_known_fields, get_authorization, get_client_ip, get_country, get_request_origin,
4-
insert_error_entries, insert_web_event, load_project_context, success_response,
5-
validate_domain,
4+
insert_error_entries, insert_web_event, load_project_context, resolve_identity_key,
5+
success_response, validate_domain,
66
};
77
use crate::batch_queue::{FailedRequest, RequestType, TrackingContext};
88
use crate::models::{AppState, ErrorTracking};
@@ -178,6 +178,7 @@ pub async fn web(
178178
token: token.into(),
179179
organization_id: ctx.organization_id.as_deref().map(Into::into),
180180
};
181+
let fallback_identity = resolved_user_id.to_string();
181182

182183
let data_entry_id = if is_debounced {
183184
None
@@ -208,11 +209,16 @@ pub async fn web(
208209
if error.build_id.is_none() {
209210
error.build_id = build_id.clone();
210211
}
212+
let identity_key = resolve_identity_key(
213+
error.session_id.as_deref(),
214+
Some(fallback_identity.as_str()),
215+
);
211216
if let Err(e) = insert_error_entries(
212217
&state.batch_queue,
213218
ctx.project_id,
214219
data_entry_id.unwrap_or_else(Uuid::new_v4),
215220
error,
221+
identity_key,
216222
Some(tracking_ctx.clone()),
217223
)
218224
.await

src/tinybird.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub struct ErrorTrackingRow {
8181
pub count: u32,
8282
pub data_entry_id: Uuid,
8383
pub session_id: Option<String>,
84+
pub identity_key: Option<String>,
8485
pub build_id: Option<String>,
8586
#[serde(with = "chrono::serde::ts_milliseconds")]
8687
pub created_at: DateTime<Utc>,

0 commit comments

Comments
 (0)