X-Git-Url: http://these/git/?a=blobdiff_plain;f=crates%2Fdb_schema%2Fsrc%2Fimpls%2Fcommunity.rs;h=d5d558fa27adaf5e1015f7af37495ae743b0a273;hb=a47b12bbdee274c761a912cde0baddb715674023;hp=935bfa05eaffa9585ccd288370abebddcffd056c;hpb=a610211557a15c00f9591e0515c781d46c26890f;p=lemmy.git diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 935bfa05..d5d558fa 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -1,8 +1,8 @@ use crate::{ newtypes::{CommunityId, DbUrl, PersonId}, - schema::community::dsl::{actor_id, community, deleted, local, name, removed}, + schema::{community, community_follower, instance}, source::{ - actor_language::{CommunityLanguage, SiteLanguage}, + actor_language::CommunityLanguage, community::{ Community, CommunityFollower, @@ -12,118 +12,51 @@ use crate::{ CommunityModeratorForm, CommunityPersonBan, CommunityPersonBanForm, - CommunitySafe, CommunityUpdateForm, }, }, - traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable}, + traits::{ApubActor, Bannable, Crud, Followable, Joinable}, utils::{functions::lower, get_conn, DbPool}, SubscribedType, }; -use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl, TextExpressionMethods}; +use diesel::{ + deserialize, + dsl, + dsl::insert_into, + pg::Pg, + result::Error, + sql_types, + ExpressionMethods, + NullableExpressionMethods, + QueryDsl, + Queryable, +}; use diesel_async::RunQueryDsl; -mod safe_type { - use crate::{ - schema::community::{ - actor_id, - banner, - deleted, - description, - hidden, - icon, - id, - instance_id, - local, - name, - nsfw, - posting_restricted_to_mods, - published, - removed, - title, - updated, - }, - 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, - instance_id, - ); - - 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, - instance_id, - ) - } - } -} - #[async_trait] impl Crud for Community { type InsertForm = CommunityInsertForm; type UpdateForm = CommunityUpdateForm; type IdType = CommunityId; - async fn read(pool: &DbPool, community_id: CommunityId) -> Result { - let conn = &mut get_conn(pool).await?; - community.find(community_id).first::(conn).await - } - async fn delete(pool: &DbPool, community_id: CommunityId) -> Result { + 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?; - diesel::delete(community.find(community_id)) - .execute(conn) - .await - } - async fn create(pool: &DbPool, form: &Self::InsertForm) -> Result { - let conn = &mut get_conn(pool).await?; - let community_ = insert_into(community) + // Can't do separate insert/update commands because InsertForm/UpdateForm aren't convertible + let community_ = insert_into(community::table) .values(form) - .on_conflict(actor_id) + .on_conflict(community::actor_id) .do_update() .set(form) .get_result::(conn) .await?; - let site_languages = SiteLanguage::read_local(pool).await; - if let Ok(langs) = site_languages { - // if site exists, init user with site languages - CommunityLanguage::update(pool, langs, community_.id).await?; - } else { - // otherwise, init with all languages (this only happens during tests) + // Initialize languages for new community + if is_new_community { CommunityLanguage::update(pool, vec![], community_.id).await?; } @@ -131,12 +64,12 @@ impl Crud for Community { } async fn update( - pool: &DbPool, + pool: &mut DbPool<'_>, community_id: CommunityId, form: &Self::UpdateForm, ) -> Result { let conn = &mut get_conn(pool).await?; - diesel::update(community.find(community_id)) + diesel::update(community::table.find(community_id)) .set(form) .get_result::(conn) .await @@ -147,7 +80,7 @@ impl Crud for Community { impl Joinable for CommunityModerator { type Form = CommunityModeratorForm; async fn join( - pool: &DbPool, + pool: &mut DbPool<'_>, community_moderator_form: &CommunityModeratorForm, ) -> Result { use crate::schema::community_moderator::dsl::community_moderator; @@ -159,7 +92,7 @@ impl Joinable for CommunityModerator { } async fn leave( - pool: &DbPool, + pool: &mut DbPool<'_>, community_moderator_form: &CommunityModeratorForm, ) -> Result { use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id}; @@ -174,29 +107,41 @@ impl Joinable for CommunityModerator { } } -impl DeleteableOrRemoveable for CommunitySafe { - fn blank_out_deleted_or_removed_info(mut self) -> Self { - self.title = String::new(); - 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 = String::new(); - 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 async fn delete_for_community( - pool: &DbPool, + pool: &mut DbPool<'_>, for_community_id: CommunityId, ) -> Result { use crate::schema::community_moderator::dsl::{community_id, community_moderator}; @@ -207,8 +152,19 @@ impl CommunityModerator { .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 async fn get_person_moderated_communities( - pool: &DbPool, + pool: &mut DbPool<'_>, for_person_id: PersonId, ) -> Result, Error> { use crate::schema::community_moderator::dsl::{community_id, community_moderator, person_id}; @@ -225,7 +181,7 @@ impl CommunityModerator { impl Bannable for CommunityPersonBan { type Form = CommunityPersonBanForm; async fn ban( - pool: &DbPool, + pool: &mut DbPool<'_>, community_person_ban_form: &CommunityPersonBanForm, ) -> Result { use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id}; @@ -240,7 +196,7 @@ impl Bannable for CommunityPersonBan { } async fn unban( - pool: &DbPool, + pool: &mut DbPool<'_>, community_person_ban_form: &CommunityPersonBanForm, ) -> Result { use crate::schema::community_person_ban::dsl::{community_id, community_person_ban, person_id}; @@ -269,12 +225,27 @@ impl CommunityFollower { None => SubscribedType::NotSubscribed, } } + + pub fn select_subscribed_type() -> dsl::Nullable { + community_follower::pending.nullable() + } +} + +impl Queryable, Pg> for SubscribedType { + type Row = Option; + fn build(row: Self::Row) -> deserialize::Result { + Ok(match row { + Some(true) => SubscribedType::Pending, + Some(false) => SubscribedType::Subscribed, + None => SubscribedType::NotSubscribed, + }) + } } #[async_trait] impl Followable for CommunityFollower { type Form = CommunityFollowerForm; - async fn follow(pool: &DbPool, form: &CommunityFollowerForm) -> Result { + 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) @@ -286,7 +257,7 @@ impl Followable for CommunityFollower { .await } async fn follow_accepted( - pool: &DbPool, + pool: &mut DbPool<'_>, community_id_: CommunityId, person_id_: PersonId, ) -> Result { @@ -306,7 +277,7 @@ impl Followable for CommunityFollower { .get_result::(conn) .await } - async fn unfollow(pool: &DbPool, form: &CommunityFollowerForm) -> Result { + 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( @@ -321,11 +292,14 @@ impl Followable for CommunityFollower { #[async_trait] impl ApubActor for Community { - async fn read_from_apub_id(pool: &DbPool, object_id: &DbUrl) -> Result, Error> { + 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() @@ -334,30 +308,34 @@ impl ApubActor for Community { } async fn read_from_name( - pool: &DbPool, + pool: &mut DbPool<'_>, community_name: &str, include_deleted: bool, ) -> Result { let conn = &mut get_conn(pool).await?; - let mut q = community + 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).await } async fn read_from_name_and_domain( - pool: &DbPool, + pool: &mut DbPool<'_>, community_name: &str, - protocol_domain: &str, + for_domain: &str, ) -> Result { let conn = &mut get_conn(pool).await?; - community - .filter(lower(name).eq(lower(community_name))) - .filter(actor_id.like(format!("{protocol_domain}%"))) + 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 } @@ -365,6 +343,9 @@ impl ApubActor for Community { #[cfg(test)] mod tests { + #![allow(clippy::unwrap_used)] + #![allow(clippy::indexing_slicing)] + use crate::{ source::{ community::{ @@ -390,8 +371,11 @@ mod tests { #[serial] async fn test_crud() { let pool = &build_db_pool_for_tests().await; + let pool = &mut pool.into(); - let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap(); + let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()) + .await + .unwrap(); let new_person = PersonInsertForm::builder() .name("bobbee".into()) @@ -430,6 +414,8 @@ mod tests { 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,