headless_lms_models/
page_visit_datum_daily_visit_hashing_keys.rs1use chrono::NaiveDate;
2use headless_lms_utils::page_visit_hasher::hash_anonymous_identifier;
3
4use crate::prelude::*;
5
6pub struct GenerateAnonymousIdentifierInput {
7 pub course_id: Uuid,
8 pub user_agent: String,
9 pub ip_address: String,
10}
11pub async fn generate_anonymous_identifier(
12 conn: &mut PgConnection,
13 input: GenerateAnonymousIdentifierInput,
14) -> ModelResult<String> {
15 let key_for_the_day = get_key_for_the_day(conn).await?;
16 Ok(hash_anonymous_identifier(
17 input.course_id,
18 &key_for_the_day,
19 &input.user_agent,
20 &input.ip_address,
21 ))
22}
23
24pub async fn get_key_for_the_day(conn: &mut PgConnection) -> ModelResult<Vec<u8>> {
25 let now = Utc::now();
26 let valid_for_date = now.date_naive();
27 let res = try_get_key_for_the_day_internal(conn, valid_for_date).await?;
28 match res {
29 Some(hashing_key) => Ok(hashing_key),
30 None => {
31 try_insert_key_for_the_day_internal(conn, valid_for_date).await?;
32 let second_try = try_get_key_for_the_day_internal(conn, valid_for_date).await?;
33 match second_try {
34 Some(hashing_key) => Ok(hashing_key),
35 None => Err(ModelError::new(
36 ModelErrorType::Generic,
37 "Failed to get hashing key for the day".to_string(),
38 None,
39 )),
40 }
41 }
42 }
43}
44
45async fn try_get_key_for_the_day_internal(
46 conn: &mut PgConnection,
47 valid_for_date: NaiveDate,
48) -> ModelResult<Option<Vec<u8>>> {
49 let res = sqlx::query!(
50 "
51SELECT hashing_key FROM page_visit_datum_daily_visit_hashing_keys
52WHERE valid_for_date = $1
53 ",
54 valid_for_date
55 )
56 .fetch_optional(conn)
57 .await?;
58 Ok(res.map(|r| r.hashing_key))
59}
60
61async fn try_insert_key_for_the_day_internal(
62 conn: &mut PgConnection,
63 valid_for_date: NaiveDate,
64) -> ModelResult<()> {
65 sqlx::query!(
66 "
67INSERT INTO page_visit_datum_daily_visit_hashing_keys(valid_for_date)
68VALUES ($1)
69ON CONFLICT (valid_for_date) DO NOTHING
70 ",
71 valid_for_date
72 )
73 .execute(&mut *conn)
74 .await?;
75
76 sqlx::query!(
78 "
79DELETE FROM page_visit_datum_daily_visit_hashing_keys WHERE valid_for_date < $1
80 ",
81 valid_for_date
82 )
83 .execute(&mut *conn)
84 .await?;
85 Ok(())
86}