diff --git a/common/src/storage/types/user.rs b/common/src/storage/types/user.rs index bab9272..64d7574 100644 --- a/common/src/storage/types/user.rs +++ b/common/src/storage/types/user.rs @@ -85,10 +85,20 @@ stored_object!( admin: bool, #[serde(default)] timezone: String, - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_theme_or_default")] theme: Theme }); +fn deserialize_theme_or_default<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let raw = Option::::deserialize(deserializer)?; + Ok(raw + .and_then(|value| Theme::from_str(value.as_str()).ok()) + .unwrap_or_default()) +} + #[async_trait] impl Authentication> for User { async fn load_user(userid: String, db: Option<&Surreal>) -> Result { @@ -1022,6 +1032,42 @@ mod tests { assert!(not_found.is_none()); } + #[tokio::test] + async fn test_set_api_key_with_none_theme() { + let db = setup_test_db().await; + + let user = User::create_new( + "legacy_theme@example.com".to_string(), + "apikey_password".to_string(), + &db, + "UTC".to_string(), + "system".to_string(), + ) + .await + .expect("Failed to create user"); + + db.client + .query("UPDATE type::thing('user', $id) SET theme = NONE") + .bind(("id", user.id.clone())) + .await + .expect("Failed to set user theme to NONE"); + + let api_key = User::set_api_key(&user.id, &db) + .await + .expect("set_api_key should tolerate NONE theme"); + + assert!(api_key.starts_with("sk_")); + + let updated_user = db + .get_item::(&user.id) + .await + .expect("Failed to retrieve user") + .expect("User should still exist"); + + assert_eq!(updated_user.theme, Theme::System); + assert_eq!(updated_user.api_key, Some(api_key)); + } + #[tokio::test] async fn test_password_update() { // Setup test database