From 53f848dee8b3ab62381a55e7387cdefe5136330d Mon Sep 17 00:00:00 2001 From: Bailey Townsend Date: Tue, 17 Mar 2026 18:18:11 -0500 Subject: [PATCH] add new build additional hook --- shared/src/advent/challenges/day_two.rs | 145 +++++++++--------------- shared/src/advent/mod.rs | 58 +++++++--- 2 files changed, 95 insertions(+), 108 deletions(-) diff --git a/shared/src/advent/challenges/day_two.rs b/shared/src/advent/challenges/day_two.rs index 86114f1..753e6ff 100644 --- a/shared/src/advent/challenges/day_two.rs +++ b/shared/src/advent/challenges/day_two.rs @@ -1,7 +1,5 @@ use crate::advent::day::Day; -use crate::advent::{ - AdventChallenge, AdventError, AdventPart, ChallengeCheckResponse, get_random_token, -}; +use crate::advent::{AdventChallenge, AdventError, AdventPart, ChallengeCheckResponse}; use crate::atrium::safe_check_unknown_record_parse; use crate::lexicons::codes::advent; use crate::lexicons::record::KnownRecord; @@ -31,100 +29,65 @@ impl AdventChallenge for DayTwo { true } - /// We are overriding the start challenge and using extra code - async fn start_challenge(&self, did: String, part: AdventPart) -> Result { - let code = get_random_token(); - - // For part one, create a record via the challenge agent and store the at_uri - let additional_context: Option = match part { + /// Create a record via the challenge agent and return the at_uri as additional context + async fn build_additional_context( + &self, + did: &str, + part: &AdventPart, + code: &str, + ) -> Result, AdventError> { + match part { AdventPart::One => { - match &self.challenge_agent { - Some(agent) => { - let agent_did = agent.did().await.ok_or_else(|| { - AdventError::ShouldNotHappen( - "Challenge agent has no DID".to_string(), - ) - })?; - - let record_data = advent::challenge::day::RecordData { - part_one: code.clone(), - part_two: None, - created_at: None, - }; - let known_record: KnownRecord = record_data.into(); - let record_value: atrium_api::types::Unknown = known_record.into(); - - // Use a unique rkey per user based on the did to avoid collisions - let rkey = did.replace(":", "_").replace(".", "_"); - - let put_result = agent - .api - .com - .atproto - .repo - .put_record( - atrium_api::com::atproto::repo::put_record::InputData { - collection: advent::challenge::Day::NSID.parse().unwrap(), - repo: agent_did.as_ref().parse().unwrap(), - rkey: rkey.parse().unwrap(), - record: record_value, - swap_commit: None, - swap_record: None, - validate: Some(false), - } - .into(), - ) - .await; - - match put_result { - Ok(output) => Some(json!({"at_uri": output.uri})), - Err(e) => { - log::error!("Failed to create challenge record via agent: {e}"); - None - } + let Some(agent) = &self.challenge_agent else { + log::warn!( + "No challenge agent configured, skipping record creation for day two" + ); + return Ok(None); + }; + + let agent_did = agent.did().await.ok_or_else(|| { + AdventError::ShouldNotHappen("Challenge agent has no DID".to_string()) + })?; + + let record_data = advent::challenge::day::RecordData { + part_one: code.to_string(), + part_two: None, + created_at: None, + }; + let known_record: KnownRecord = record_data.into(); + let record_value: atrium_api::types::Unknown = known_record.into(); + + // Use a unique rkey per user based on the did to avoid collisions + // let rkey = did.replace(":", "_").replace(".", "_"); + + let create_record_result = agent + .api + .com + .atproto + .repo + .create_record( + atrium_api::com::atproto::repo::create_record::InputData { + collection: advent::challenge::Day::NSID.parse().unwrap(), + repo: agent_did.as_ref().parse().unwrap(), + rkey: None, + record: record_value, + swap_commit: None, + validate: Some(false), } - } - None => { - log::warn!("No challenge agent configured, skipping record creation for day two"); - None + .into(), + ) + .await; + + match create_record_result { + Ok(output) => Ok(Some(json!({"at_uri": output.uri}))), + Err(e) => { + log::error!("Failed to create challenge record via agent: {e}"); + Ok(None) } } } - AdventPart::Two => None, - }; - - match part { - AdventPart::One => { - sqlx::query( - "INSERT INTO challenges (user_did, day, time_started, verification_code_one, additional_context) - VALUES ($1, $2, NOW(), $3, $4) - ON CONFLICT (user_did, day) - DO UPDATE SET verification_code_one = $3, additional_context = $4 - WHERE challenges.user_did = $1 AND challenges.day = $2", - ) - .bind(&did) - .bind(self.day() as i16) - .bind(&code) - .bind(&additional_context) - .execute(self.pool()) - .await?; - } - //TODO just going leave these as an update. It should never ideally be an insert - AdventPart::Two => { - sqlx::query( - "UPDATE challenges - SET verification_code_two = $3 - WHERE challenges.user_did = $1 AND challenges.day = $2", - ) - .bind(&did) - .bind(self.day() as i16) - .bind(&code) - .execute(self.pool()) - .await?; - } + AdventPart::Two => Ok(None), } - - Ok(code) } async fn check_part_one( diff --git a/shared/src/advent/mod.rs b/shared/src/advent/mod.rs index 8814613..a122172 100644 --- a/shared/src/advent/mod.rs +++ b/shared/src/advent/mod.rs @@ -224,28 +224,52 @@ pub trait AdventChallenge { .await?) } + /// Hook for challenges to build additional context before the DB write. + /// Override this to store extra data (e.g. an at_uri) in the additional_context column. + async fn build_additional_context( + &self, + _did: &str, + _part: &AdventPart, + _code: &str, + ) -> Result, AdventError> { + Ok(None) + } + async fn start_challenge(&self, did: String, part: AdventPart) -> Result { let code = get_random_token(); + let additional_context = self.build_additional_context(&did, &part, &code).await?; + match part { - AdventPart::One => sqlx::query( - "INSERT INTO challenges (user_did, day, time_started, verification_code_one) - VALUES ($1, $2, NOW(), $3) - ON CONFLICT (user_did, day) - DO UPDATE SET verification_code_one = $3 - WHERE challenges.user_did = $1 AND challenges.day = $2", - ), + AdventPart::One => { + sqlx::query( + "INSERT INTO challenges (user_did, day, time_started, verification_code_one, additional_context) + VALUES ($1, $2, NOW(), $3, $4) + ON CONFLICT (user_did, day) + DO UPDATE SET verification_code_one = $3, additional_context = COALESCE($4, challenges.additional_context) + WHERE challenges.user_did = $1 AND challenges.day = $2", + ) + .bind(&did) + .bind(self.day() as i16) + .bind(&code) + .bind(&additional_context) + .execute(self.pool()) + .await?; + } //TODO just going leave these as an update. It should never ideally be an insert - AdventPart::Two => sqlx::query( - "UPDATE challenges - SET verification_code_two = $3 - WHERE challenges.user_did = $1 AND challenges.day = $2", - ), + AdventPart::Two => { + sqlx::query( + "UPDATE challenges + SET verification_code_two = $3 + WHERE challenges.user_did = $1 AND challenges.day = $2", + ) + .bind(&did) + .bind(self.day() as i16) + .bind(&code) + .execute(self.pool()) + .await?; + } } - .bind(did) - .bind(self.day() as i16) - .bind(code.clone()) - .execute(self.pool()) - .await?; + Ok(code) } -- 2.51.2