Add first day of the week feature to user settings and calendar functionality

This commit is contained in:
Greg Burri 2025-04-19 12:01:46 +02:00
parent 39f5b968b4
commit fdbf2e4f27
15 changed files with 191 additions and 42 deletions

View file

@ -77,7 +77,7 @@ FROM [UserLoginToken] WHERE [token] = $1
pub async fn load_user(&self, user_id: i64) -> Result<Option<model::User>> {
sqlx::query_as(
"SELECT [id], [email], [name], [default_servings], [lang], [is_admin] FROM [User] WHERE [id] = $1",
"SELECT [id], [email], [name], [default_servings], [first_day_of_the_week], [lang], [is_admin] FROM [User] WHERE [id] = $1",
)
.bind(user_id)
.fetch_optional(&self.pool)
@ -93,16 +93,17 @@ FROM [UserLoginToken] WHERE [token] = $1
new_email: Option<&str>,
new_name: Option<&str>,
new_default_servings: Option<u32>,
new_first_day_of_the_week: Option<Weekday>,
new_password: Option<&str>,
) -> Result<UpdateUserResult> {
let mut tx = self.tx().await?;
let hashed_new_password = new_password.map(|p| hash(p).unwrap());
let (email, name, default_servings, hashed_password) = sqlx::query_as::<
let (email, name, default_servings, first_day_of_the_week, hashed_password) = sqlx::query_as::<
_,
(String, String, u32, String),
(String, String, u32, u8, String),
>(
"SELECT [email], [name], [default_servings], [password] FROM [User] WHERE [id] = $1",
"SELECT [email], [name], [default_servings], [first_day_of_the_week], [password] FROM [User] WHERE [id] = $1",
)
.bind(user_id)
.fetch_one(&mut *tx)
@ -148,7 +149,7 @@ WHERE [id] = $1
sqlx::query(
r#"
UPDATE [User]
SET [email] = $2, [name] = $3, [default_servings] = $4, [password] = $5
SET [email] = $2, [name] = $3, [default_servings] = $4, [first_day_of_the_week] = $5, [password] = $6
WHERE [id] = $1
"#,
)
@ -156,6 +157,7 @@ WHERE [id] = $1
.bind(new_email.unwrap_or(&email))
.bind(new_name.map(str::trim).unwrap_or(&name))
.bind(new_default_servings.unwrap_or(default_servings))
.bind(new_first_day_of_the_week.map(|d| d as u8).unwrap_or(first_day_of_the_week))
.bind(hashed_new_password.unwrap_or(hashed_password))
.execute(&mut *tx)
.await?;
@ -179,8 +181,13 @@ WHERE [id] = $1
.map_err(DBError::from)
}
pub async fn sign_up(&self, email: &str, password: &str) -> Result<SignUpResult> {
self.sign_up_with_given_time(email, password, Utc::now())
pub async fn sign_up(
&self,
email: &str,
password: &str,
first_day_of_the_week: Weekday,
) -> Result<SignUpResult> {
self.sign_up_with_given_time(email, password, first_day_of_the_week, Utc::now())
.await
}
@ -188,6 +195,7 @@ WHERE [id] = $1
&self,
email: &str,
password: &str,
first_day_of_the_week: Weekday,
datetime: DateTime<Utc>,
) -> Result<SignUpResult> {
let mut tx = self.tx().await?;
@ -229,11 +237,12 @@ WHERE [id] = $1
sqlx::query(
r#"
INSERT INTO [User]
([email], [creation_datetime], [validation_token], [validation_token_datetime], [password])
VALUES ($1, $2, $3, $4, $5)
([email], [first_day_of_the_week], [creation_datetime], [validation_token], [validation_token_datetime], [password])
VALUES ($1, $2, $3, $4, $5, $6)
"#,
)
.bind(email)
.bind(first_day_of_the_week as u8)
.bind(Utc::now())
.bind(&token)
.bind(datetime)
@ -525,7 +534,10 @@ mod tests {
#[tokio::test]
async fn sign_up() -> Result<()> {
let connection = Connection::new_in_memory().await?;
match connection.sign_up("paul@atreides.com", "12345").await? {
match connection
.sign_up("paul@atreides.com", "12345", Weekday::Mon)
.await?
{
SignUpResult::UserCreatedWaitingForValidation(_) => (), // Nominal case.
other => panic!("{:?}", other),
}
@ -550,7 +562,10 @@ INSERT INTO
NULL
);
"#)).await?;
match connection.sign_up("paul@atreides.com", "12345").await? {
match connection
.sign_up("paul@atreides.com", "12345", Weekday::Mon)
.await?
{
SignUpResult::UserAlreadyExists => (), // Nominal case.
other => panic!("{:?}", other),
}
@ -564,7 +579,7 @@ INSERT INTO
let email = "paul@atreides.com";
let password = "12345";
match connection.sign_up(email, password).await? {
match connection.sign_up(email, password, Weekday::Mon).await? {
SignUpResult::UserCreatedWaitingForValidation(_) => (), // Nominal case.
other => panic!("{:?}", other),
}
@ -600,7 +615,10 @@ VALUES (
)
"#
).bind(token)).await?;
match connection.sign_up("paul@atreides.com", "12345").await? {
match connection
.sign_up("paul@atreides.com", "12345", Weekday::Mon)
.await?
{
SignUpResult::UserCreatedWaitingForValidation(_) => (), // Nominal case.
other => panic!("{:?}", other),
}
@ -610,7 +628,10 @@ VALUES (
#[tokio::test]
async fn sign_up_then_send_validation_at_time() -> Result<()> {
let connection = Connection::new_in_memory().await?;
let validation_token = match connection.sign_up("paul@atreides.com", "12345").await? {
let validation_token = match connection
.sign_up("paul@atreides.com", "12345", Weekday::Mon)
.await?
{
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
other => panic!("{:?}", other),
};
@ -633,7 +654,12 @@ VALUES (
async fn sign_up_then_send_validation_too_late() -> Result<()> {
let connection = Connection::new_in_memory().await?;
let validation_token = match connection
.sign_up_with_given_time("paul@atreides.com", "12345", Utc::now() - Duration::days(1))
.sign_up_with_given_time(
"paul@atreides.com",
"12345",
Weekday::Mon,
Utc::now() - Duration::days(1),
)
.await?
{
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
@ -657,7 +683,10 @@ VALUES (
#[tokio::test]
async fn sign_up_then_send_validation_with_bad_token() -> Result<()> {
let connection = Connection::new_in_memory().await?;
let _validation_token = match connection.sign_up("paul@atreides.com", "12345").await? {
let _validation_token = match connection
.sign_up("paul@atreides.com", "12345", Weekday::Mon)
.await?
{
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
other => panic!("{:?}", other),
};
@ -685,7 +714,7 @@ VALUES (
let password = "12345";
// Sign up.
let validation_token = match connection.sign_up(email, password).await? {
let validation_token = match connection.sign_up(email, password, Weekday::Mon).await? {
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
other => panic!("{:?}", other),
};
@ -724,7 +753,7 @@ VALUES (
let password = "12345";
// Sign up.
let validation_token = match connection.sign_up(email, password).await? {
let validation_token = match connection.sign_up(email, password, Weekday::Mon).await? {
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
other => panic!("{:?}", other),
};
@ -778,7 +807,7 @@ VALUES (
let password = "12345";
// Sign up.
let validation_token = match connection.sign_up(email, password).await? {
let validation_token = match connection.sign_up(email, password, Weekday::Mon).await? {
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
other => panic!("{:?}", other),
};
@ -855,7 +884,7 @@ VALUES (
let new_password = "54321";
// Sign up.
let validation_token = match connection.sign_up(email, password).await? {
let validation_token = match connection.sign_up(email, password, Weekday::Mon).await? {
SignUpResult::UserCreatedWaitingForValidation(token) => token, // Nominal case.
other => panic!("{:?}", other),
};
@ -946,6 +975,7 @@ VALUES
Some("muaddib@fremen.com"),
Some("muaddib"),
None,
None,
Some("Chani"),
)
.await?

View file

@ -8,6 +8,10 @@ pub struct User {
pub name: String,
pub email: String,
pub default_servings: u32,
#[sqlx(try_from = "u8")]
pub first_day_of_the_week: Weekday,
pub lang: String,
pub is_admin: bool,
}