X-Git-Url: http://these/git/?a=blobdiff_plain;f=crates%2Fdb_schema%2Fsrc%2Fimpls%2Fcommunity.rs;h=258e4150442b9473de31a10583381408209f6662;hb=92568956353f21649ed9aff68b42699c9d036f30;hp=45675aa368329851fd456e50d3dd9166a609f7b8;hpb=63fff96275a612a72d3736e93d5913467da028cc;p=lemmy.git diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 45675aa3..258e4150 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -1,252 +1,216 @@ use crate::{ newtypes::{CommunityId, DbUrl, PersonId}, - source::community::{ - Community, - CommunityFollower, - CommunityFollowerForm, - CommunityForm, - CommunityModerator, - CommunityModeratorForm, - CommunityPersonBan, - CommunityPersonBanForm, - CommunitySafe, + schema::{community, instance}, + source::{ + actor_language::CommunityLanguage, + community::{ + Community, + CommunityFollower, + CommunityFollowerForm, + CommunityInsertForm, + CommunityModerator, + CommunityModeratorForm, + CommunityPersonBan, + CommunityPersonBanForm, + CommunityUpdateForm, + }, }, - traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable}, - utils::{functions::lower, naive_now}, + traits::{ApubActor, Bannable, Crud, Followable, Joinable}, + utils::{functions::lower, get_conn, DbPool}, SubscribedType, }; -use diesel::{ - dsl::*, - result::Error, - ExpressionMethods, - PgConnection, - QueryDsl, - RunQueryDsl, - TextExpressionMethods, -}; - -mod safe_type { - use crate::{schema::community::*, source::community::Community, traits::ToSafe}; - - type Columns = ( - id, - name, - title, - description, - removed, - published, - updated, - deleted, - nsfw, - actor_id, - local, - icon, - banner, - hidden, - posting_restricted_to_mods, - ); - - impl ToSafe for Community { - type SafeColumns = Columns; - fn safe_columns_tuple() -> Self::SafeColumns { - ( - id, - name, - title, - description, - removed, - published, - updated, - deleted, - nsfw, - actor_id, - local, - icon, - banner, - hidden, - posting_restricted_to_mods, - ) - } - } -} +use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl}; +use diesel_async::RunQueryDsl; +#[async_trait] impl Crud for Community { - type Form = CommunityForm; + type InsertForm = CommunityInsertForm; + type UpdateForm = CommunityUpdateForm; type IdType = CommunityId; - fn read(conn: &PgConnection, community_id: CommunityId) -> Result { - use crate::schema::community::dsl::*; - community.find(community_id).first::(conn) - } - - fn delete(conn: &PgConnection, community_id: CommunityId) -> Result { - use crate::schema::community::dsl::*; - diesel::delete(community.find(community_id)).execute(conn) - } - - fn create(conn: &PgConnection, new_community: &CommunityForm) -> Result { - use crate::schema::community::dsl::*; - insert_into(community) - .values(new_community) - .get_result::(conn) + async fn read(pool: &mut DbPool<'_>, community_id: CommunityId) -> Result { + let conn = &mut get_conn(pool).await?; + community::table + .find(community_id) + .first::(conn) + .await } - fn update( - conn: &PgConnection, - community_id: CommunityId, - new_community: &CommunityForm, - ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set(new_community) - .get_result::(conn) + async fn delete(pool: &mut DbPool<'_>, community_id: CommunityId) -> Result { + let conn = &mut get_conn(pool).await?; + diesel::delete(community::table.find(community_id)) + .execute(conn) + .await } -} -impl Community { - pub fn update_deleted( - conn: &PgConnection, - community_id: CommunityId, - new_deleted: bool, - ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set((deleted.eq(new_deleted), updated.eq(naive_now()))) - .get_result::(conn) - } + async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result { + let is_new_community = match &form.actor_id { + Some(id) => Community::read_from_apub_id(pool, id).await?.is_none(), + None => true, + }; + let conn = &mut get_conn(pool).await?; - pub fn update_removed( - conn: &PgConnection, - community_id: CommunityId, - new_removed: bool, - ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set((removed.eq(new_removed), updated.eq(naive_now()))) + // Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible + let community_ = insert_into(community::table) + .values(form) + .on_conflict(community::actor_id) + .do_update() + .set(form) .get_result::(conn) - } + .await?; - pub fn distinct_federated_communities(conn: &PgConnection) -> Result, Error> { - use crate::schema::community::dsl::*; - community.select(actor_id).distinct().load::(conn) - } + // Initialize languages for new community + if is_new_community { + CommunityLanguage::update(pool, vec![], community_.id).await?; + } - pub fn upsert(conn: &PgConnection, community_form: &CommunityForm) -> Result { - use crate::schema::community::dsl::*; - insert_into(community) - .values(community_form) - .on_conflict(actor_id) - .do_update() - .set(community_form) - .get_result::(conn) + Ok(community_) } - pub fn remove_avatar_and_banner( - conn: &PgConnection, + async fn update( + pool: &mut DbPool<'_>, community_id: CommunityId, + form: &Self::UpdateForm, ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set(( - icon.eq::>(None), - banner.eq::>(None), - )) + let conn = &mut get_conn(pool).await?; + diesel::update(community::table.find(community_id)) + .set(form) .get_result::(conn) + .await } } +#[async_trait] impl Joinable for CommunityModerator { type Form = CommunityModeratorForm; - fn join( - conn: &PgConnection, + async fn join( + pool: &mut DbPool<'_>, community_moderator_form: &CommunityModeratorForm, ) -> Result { - use crate::schema::community_moderator::dsl::*; + use crate::schema::community_moderator::dsl::community_moderator; + let conn = &mut get_conn(pool).await?; insert_into(community_moderator) .values(community_moderator_form) .get_result::(conn) + .await } - fn leave( - conn: &PgConnection, + async fn leave( + pool: &mut DbPool<'_>, community_moderator_form: &CommunityModeratorForm, ) -> Result { - use crate::schema::community_moderator::dsl::*; + use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id}; + let conn = &mut get_conn(pool).await?; diesel::delete( community_moderator .filter(community_id.eq(community_moderator_form.community_id)) .filter(person_id.eq(community_moderator_form.person_id)), ) .execute(conn) + .await } } -impl DeleteableOrRemoveable for CommunitySafe { - fn blank_out_deleted_or_removed_info(mut self) -> Self { - self.title = "".into(); - self.description = None; - self.icon = None; - self.banner = None; - self - } +pub enum CollectionType { + Moderators, + Featured, } -impl DeleteableOrRemoveable for Community { - fn blank_out_deleted_or_removed_info(mut self) -> Self { - self.title = "".into(); - self.description = None; - self.icon = None; - self.banner = None; - self +impl Community { + /// Get the community which has a given moderators or featured url, also return the collection type + pub async fn get_by_collection_url( + pool: &mut DbPool<'_>, + url: &DbUrl, + ) -> Result<(Community, CollectionType), Error> { + use crate::schema::community::dsl::{featured_url, moderators_url}; + use CollectionType::*; + let conn = &mut get_conn(pool).await?; + let res = community::table + .filter(moderators_url.eq(url)) + .first::(conn) + .await; + if let Ok(c) = res { + return Ok((c, Moderators)); + } + let res = community::table + .filter(featured_url.eq(url)) + .first::(conn) + .await; + if let Ok(c) = res { + return Ok((c, Featured)); + } + Err(diesel::NotFound) } } impl CommunityModerator { - pub fn delete_for_community( - conn: &PgConnection, + pub async fn delete_for_community( + pool: &mut DbPool<'_>, for_community_id: CommunityId, ) -> Result { - use crate::schema::community_moderator::dsl::*; - diesel::delete(community_moderator.filter(community_id.eq(for_community_id))).execute(conn) + use crate::schema::community_moderator::dsl::{community_id, community_moderator}; + let conn = &mut get_conn(pool).await?; + + diesel::delete(community_moderator.filter(community_id.eq(for_community_id))) + .execute(conn) + .await + } + + pub async fn leave_all_communities( + pool: &mut DbPool<'_>, + for_person_id: PersonId, + ) -> Result { + use crate::schema::community_moderator::dsl::{community_moderator, person_id}; + let conn = &mut get_conn(pool).await?; + diesel::delete(community_moderator.filter(person_id.eq(for_person_id))) + .execute(conn) + .await } - pub fn get_person_moderated_communities( - conn: &PgConnection, + pub async fn get_person_moderated_communities( + pool: &mut DbPool<'_>, for_person_id: PersonId, ) -> Result, Error> { - use crate::schema::community_moderator::dsl::*; + use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id}; + let conn = &mut get_conn(pool).await?; community_moderator .filter(person_id.eq(for_person_id)) .select(community_id) .load::(conn) + .await } } +#[async_trait] impl Bannable for CommunityPersonBan { type Form = CommunityPersonBanForm; - fn ban( - conn: &PgConnection, + async fn ban( + pool: &mut DbPool<'_>, community_person_ban_form: &CommunityPersonBanForm, ) -> Result { - use crate::schema::community_person_ban::dsl::*; + use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id}; + let conn = &mut get_conn(pool).await?; insert_into(community_person_ban) .values(community_person_ban_form) .on_conflict((community_id, person_id)) .do_update() .set(community_person_ban_form) .get_result::(conn) + .await } - fn unban( - conn: &PgConnection, + async fn unban( + pool: &mut DbPool<'_>, community_person_ban_form: &CommunityPersonBanForm, ) -> Result { - use crate::schema::community_person_ban::dsl::*; + use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id}; + let conn = &mut get_conn(pool).await?; diesel::delete( community_person_ban .filter(community_id.eq(community_person_ban_form.community_id)) .filter(person_id.eq(community_person_ban_form.person_id)), ) .execute(conn) + .await } } @@ -254,7 +218,7 @@ impl CommunityFollower { pub fn to_subscribed_type(follower: &Option) -> SubscribedType { match follower { Some(f) => { - if f.pending.unwrap_or(false) { + if f.pending { SubscribedType::Pending } else { SubscribedType::Subscribed @@ -266,131 +230,157 @@ impl CommunityFollower { } } +#[async_trait] impl Followable for CommunityFollower { type Form = CommunityFollowerForm; - fn follow( - conn: &PgConnection, - community_follower_form: &CommunityFollowerForm, - ) -> Result { - use crate::schema::community_follower::dsl::*; + async fn follow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result { + use crate::schema::community_follower::dsl::{community_follower, community_id, person_id}; + let conn = &mut get_conn(pool).await?; insert_into(community_follower) - .values(community_follower_form) + .values(form) .on_conflict((community_id, person_id)) .do_update() - .set(community_follower_form) + .set(form) .get_result::(conn) + .await } - fn follow_accepted( - conn: &PgConnection, + async fn follow_accepted( + pool: &mut DbPool<'_>, community_id_: CommunityId, person_id_: PersonId, - ) -> Result - where - Self: Sized, - { - use crate::schema::community_follower::dsl::*; + ) -> Result { + use crate::schema::community_follower::dsl::{ + community_follower, + community_id, + pending, + person_id, + }; + let conn = &mut get_conn(pool).await?; diesel::update( community_follower .filter(community_id.eq(community_id_)) .filter(person_id.eq(person_id_)), ) - .set(pending.eq(true)) + .set(pending.eq(false)) .get_result::(conn) + .await } - fn unfollow( - conn: &PgConnection, - community_follower_form: &CommunityFollowerForm, - ) -> Result { - use crate::schema::community_follower::dsl::*; + async fn unfollow(pool: &mut DbPool<'_>, form: &CommunityFollowerForm) -> Result { + use crate::schema::community_follower::dsl::{community_follower, community_id, person_id}; + let conn = &mut get_conn(pool).await?; diesel::delete( community_follower - .filter(community_id.eq(&community_follower_form.community_id)) - .filter(person_id.eq(&community_follower_form.person_id)), + .filter(community_id.eq(&form.community_id)) + .filter(person_id.eq(&form.person_id)), ) .execute(conn) - } - // TODO: this function name only makes sense if you call it with a remote community. for a local - // community, it will also return true if only remote followers exist - fn has_local_followers(conn: &PgConnection, community_id_: CommunityId) -> Result { - use crate::schema::community_follower::dsl::*; - diesel::select(exists( - community_follower.filter(community_id.eq(community_id_)), - )) - .get_result(conn) + .await } } +#[async_trait] impl ApubActor for Community { - fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result, Error> { - use crate::schema::community::dsl::*; + async fn read_from_apub_id( + pool: &mut DbPool<'_>, + object_id: &DbUrl, + ) -> Result, Error> { + let conn = &mut get_conn(pool).await?; Ok( - community - .filter(actor_id.eq(object_id)) + community::table + .filter(community::actor_id.eq(object_id)) .first::(conn) + .await .ok() .map(Into::into), ) } - fn read_from_name( - conn: &PgConnection, + async fn read_from_name( + pool: &mut DbPool<'_>, community_name: &str, include_deleted: bool, ) -> Result { - use crate::schema::community::dsl::*; - let mut q = community + let conn = &mut get_conn(pool).await?; + let mut q = community::table .into_boxed() - .filter(local.eq(true)) - .filter(lower(name).eq(lower(community_name))); + .filter(community::local.eq(true)) + .filter(lower(community::name).eq(community_name.to_lowercase())); if !include_deleted { - q = q.filter(deleted.eq(false)).filter(removed.eq(false)); + q = q + .filter(community::deleted.eq(false)) + .filter(community::removed.eq(false)); } - q.first::(conn) + q.first::(conn).await } - fn read_from_name_and_domain( - conn: &PgConnection, + async fn read_from_name_and_domain( + pool: &mut DbPool<'_>, community_name: &str, - protocol_domain: &str, + for_domain: &str, ) -> Result { - use crate::schema::community::dsl::*; - community - .filter(lower(name).eq(lower(community_name))) - .filter(actor_id.like(format!("{}%", protocol_domain))) + let conn = &mut get_conn(pool).await?; + community::table + .inner_join(instance::table) + .filter(lower(community::name).eq(community_name.to_lowercase())) + .filter(instance::domain.eq(for_domain)) + .select(community::all_columns) .first::(conn) + .await } } #[cfg(test)] mod tests { + #![allow(clippy::unwrap_used)] + #![allow(clippy::indexing_slicing)] + use crate::{ - source::{community::*, person::*}, + source::{ + community::{ + Community, + CommunityFollower, + CommunityFollowerForm, + CommunityInsertForm, + CommunityModerator, + CommunityModeratorForm, + CommunityPersonBan, + CommunityPersonBanForm, + CommunityUpdateForm, + }, + instance::Instance, + person::{Person, PersonInsertForm}, + }, traits::{Bannable, Crud, Followable, Joinable}, - utils::establish_unpooled_connection, + utils::build_db_pool_for_tests, }; use serial_test::serial; - #[test] + #[tokio::test] #[serial] - fn test_crud() { - let conn = establish_unpooled_connection(); + async fn test_crud() { + let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); - let new_person = PersonForm { - name: "bobbee".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) + .await + .unwrap(); - let inserted_person = Person::create(&conn, &new_person).unwrap(); + let new_person = PersonInsertForm::builder() + .name("bobbee".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); - let new_community = CommunityForm { - name: "TIL".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let inserted_person = Person::create(pool, &new_person).await.unwrap(); + + let new_community = CommunityInsertForm::builder() + .name("TIL".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); - let inserted_community = Community::create(&conn, &new_community).unwrap(); + let inserted_community = Community::create(pool, &new_community).await.unwrap(); let expected_community = Community { id: inserted_community.id, @@ -402,18 +392,21 @@ mod tests { deleted: false, published: inserted_community.published, updated: None, - actor_id: inserted_community.actor_id.to_owned(), + actor_id: inserted_community.actor_id.clone(), local: true, private_key: None, public_key: "pubkey".to_owned(), last_refreshed_at: inserted_community.published, icon: None, banner: None, - followers_url: inserted_community.followers_url.to_owned(), - inbox_url: inserted_community.inbox_url.to_owned(), + followers_url: inserted_community.followers_url.clone(), + inbox_url: inserted_community.inbox_url.clone(), shared_inbox_url: None, + moderators_url: None, + featured_url: None, hidden: false, posting_restricted_to_mods: false, + instance_id: inserted_instance.id, }; let community_follower_form = CommunityFollowerForm { @@ -422,14 +415,15 @@ mod tests { pending: false, }; - let inserted_community_follower = - CommunityFollower::follow(&conn, &community_follower_form).unwrap(); + let inserted_community_follower = CommunityFollower::follow(pool, &community_follower_form) + .await + .unwrap(); let expected_community_follower = CommunityFollower { id: inserted_community_follower.id, community_id: inserted_community.id, person_id: inserted_person.id, - pending: Some(false), + pending: false, published: inserted_community_follower.published, }; @@ -438,8 +432,9 @@ mod tests { person_id: inserted_person.id, }; - let inserted_community_moderator = - CommunityModerator::join(&conn, &community_moderator_form).unwrap(); + let inserted_community_moderator = CommunityModerator::join(pool, &community_moderator_form) + .await + .unwrap(); let expected_community_moderator = CommunityModerator { id: inserted_community_moderator.id, @@ -454,8 +449,9 @@ mod tests { expires: None, }; - let inserted_community_person_ban = - CommunityPersonBan::ban(&conn, &community_person_ban_form).unwrap(); + let inserted_community_person_ban = CommunityPersonBan::ban(pool, &community_person_ban_form) + .await + .unwrap(); let expected_community_person_ban = CommunityPersonBan { id: inserted_community_person_ban.id, @@ -465,14 +461,29 @@ mod tests { expires: None, }; - let read_community = Community::read(&conn, inserted_community.id).unwrap(); - let updated_community = - Community::update(&conn, inserted_community.id, &new_community).unwrap(); - let ignored_community = CommunityFollower::unfollow(&conn, &community_follower_form).unwrap(); - let left_community = CommunityModerator::leave(&conn, &community_moderator_form).unwrap(); - let unban = CommunityPersonBan::unban(&conn, &community_person_ban_form).unwrap(); - let num_deleted = Community::delete(&conn, inserted_community.id).unwrap(); - Person::delete(&conn, inserted_person.id).unwrap(); + let read_community = Community::read(pool, inserted_community.id).await.unwrap(); + + let update_community_form = CommunityUpdateForm::builder() + .title(Some("nada".to_owned())) + .build(); + let updated_community = Community::update(pool, inserted_community.id, &update_community_form) + .await + .unwrap(); + + let ignored_community = CommunityFollower::unfollow(pool, &community_follower_form) + .await + .unwrap(); + let left_community = CommunityModerator::leave(pool, &community_moderator_form) + .await + .unwrap(); + let unban = CommunityPersonBan::unban(pool, &community_person_ban_form) + .await + .unwrap(); + let num_deleted = Community::delete(pool, inserted_community.id) + .await + .unwrap(); + Person::delete(pool, inserted_person.id).await.unwrap(); + Instance::delete(pool, inserted_instance.id).await.unwrap(); assert_eq!(expected_community, read_community); assert_eq!(expected_community, inserted_community);