From 1776de2f45b698111ad412858aceffb38e754e56 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 2 Dec 2024 16:10:59 -0500 Subject: [PATCH] Removing pointless list routes, reorganizing tests. --- crates/api/src/reports/comment_report/list.rs | 37 -- crates/api/src/reports/comment_report/mod.rs | 1 - crates/api/src/reports/post_report/list.rs | 37 -- crates/api/src/reports/post_report/mod.rs | 1 - .../reports/private_message_report/list.rs | 35 -- .../src/reports/private_message_report/mod.rs | 1 - crates/db_views/src/comment_report_view.rs | 489 +----------------- crates/db_views/src/post_report_view.rs | 356 +------------ .../src/private_message_report_view.rs | 195 +------ crates/db_views/src/report_combined_view.rs | 444 ++++++++++++++-- src/api_routes_http.rs | 25 +- 11 files changed, 462 insertions(+), 1159 deletions(-) delete mode 100644 crates/api/src/reports/comment_report/list.rs delete mode 100644 crates/api/src/reports/post_report/list.rs delete mode 100644 crates/api/src/reports/private_message_report/list.rs diff --git a/crates/api/src/reports/comment_report/list.rs b/crates/api/src/reports/comment_report/list.rs deleted file mode 100644 index f4a611698..000000000 --- a/crates/api/src/reports/comment_report/list.rs +++ /dev/null @@ -1,37 +0,0 @@ -use actix_web::web::{Data, Json, Query}; -use lemmy_api_common::{ - context::LemmyContext, - reports::comment::{ListCommentReports, ListCommentReportsResponse}, - utils::check_community_mod_of_any_or_admin_action, -}; -use lemmy_db_views::{comment_report_view::CommentReportQuery, structs::LocalUserView}; -use lemmy_utils::error::LemmyResult; - -/// Lists comment reports for a community if an id is supplied -/// or returns all comment reports for communities a user moderates -#[tracing::instrument(skip(context))] -pub async fn list_comment_reports( - data: Query, - context: Data, - local_user_view: LocalUserView, -) -> LemmyResult> { - let community_id = data.community_id; - let comment_id = data.comment_id; - let unresolved_only = data.unresolved_only.unwrap_or_default(); - - check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?; - - let page = data.page; - let limit = data.limit; - let comment_reports = CommentReportQuery { - community_id, - comment_id, - unresolved_only, - page, - limit, - } - .list(&mut context.pool(), &local_user_view) - .await?; - - Ok(Json(ListCommentReportsResponse { comment_reports })) -} diff --git a/crates/api/src/reports/comment_report/mod.rs b/crates/api/src/reports/comment_report/mod.rs index 3bb1a9b46..c85613aa6 100644 --- a/crates/api/src/reports/comment_report/mod.rs +++ b/crates/api/src/reports/comment_report/mod.rs @@ -1,3 +1,2 @@ pub mod create; -pub mod list; pub mod resolve; diff --git a/crates/api/src/reports/post_report/list.rs b/crates/api/src/reports/post_report/list.rs deleted file mode 100644 index da3c0cd94..000000000 --- a/crates/api/src/reports/post_report/list.rs +++ /dev/null @@ -1,37 +0,0 @@ -use actix_web::web::{Data, Json, Query}; -use lemmy_api_common::{ - context::LemmyContext, - reports::post::{ListPostReports, ListPostReportsResponse}, - utils::check_community_mod_of_any_or_admin_action, -}; -use lemmy_db_views::{post_report_view::PostReportQuery, structs::LocalUserView}; -use lemmy_utils::error::LemmyResult; - -/// Lists post reports for a community if an id is supplied -/// or returns all post reports for communities a user moderates -#[tracing::instrument(skip(context))] -pub async fn list_post_reports( - data: Query, - context: Data, - local_user_view: LocalUserView, -) -> LemmyResult> { - let community_id = data.community_id; - let post_id = data.post_id; - let unresolved_only = data.unresolved_only.unwrap_or_default(); - - check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?; - - let page = data.page; - let limit = data.limit; - let post_reports = PostReportQuery { - community_id, - post_id, - unresolved_only, - page, - limit, - } - .list(&mut context.pool(), &local_user_view) - .await?; - - Ok(Json(ListPostReportsResponse { post_reports })) -} diff --git a/crates/api/src/reports/post_report/mod.rs b/crates/api/src/reports/post_report/mod.rs index 3bb1a9b46..c85613aa6 100644 --- a/crates/api/src/reports/post_report/mod.rs +++ b/crates/api/src/reports/post_report/mod.rs @@ -1,3 +1,2 @@ pub mod create; -pub mod list; pub mod resolve; diff --git a/crates/api/src/reports/private_message_report/list.rs b/crates/api/src/reports/private_message_report/list.rs deleted file mode 100644 index 61dbd6b97..000000000 --- a/crates/api/src/reports/private_message_report/list.rs +++ /dev/null @@ -1,35 +0,0 @@ -use actix_web::web::{Data, Json, Query}; -use lemmy_api_common::{ - context::LemmyContext, - reports::private_message::{ListPrivateMessageReports, ListPrivateMessageReportsResponse}, - utils::is_admin, -}; -use lemmy_db_views::{ - private_message_report_view::PrivateMessageReportQuery, - structs::LocalUserView, -}; -use lemmy_utils::error::LemmyResult; - -#[tracing::instrument(skip(context))] -pub async fn list_pm_reports( - data: Query, - context: Data, - local_user_view: LocalUserView, -) -> LemmyResult> { - is_admin(&local_user_view)?; - - let unresolved_only = data.unresolved_only.unwrap_or_default(); - let page = data.page; - let limit = data.limit; - let private_message_reports = PrivateMessageReportQuery { - unresolved_only, - page, - limit, - } - .list(&mut context.pool()) - .await?; - - Ok(Json(ListPrivateMessageReportsResponse { - private_message_reports, - })) -} diff --git a/crates/api/src/reports/private_message_report/mod.rs b/crates/api/src/reports/private_message_report/mod.rs index 3bb1a9b46..c85613aa6 100644 --- a/crates/api/src/reports/private_message_report/mod.rs +++ b/crates/api/src/reports/private_message_report/mod.rs @@ -1,3 +1,2 @@ pub mod create; -pub mod list; pub mod resolve; diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index b4a23a0da..6154b9b56 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -1,7 +1,6 @@ -use crate::structs::{CommentReportView, LocalUserView}; +use crate::structs::CommentReportView; use diesel::{ dsl::now, - pg::Pg, result::Error, BoolExpressionMethods, ExpressionMethods, @@ -12,7 +11,7 @@ use diesel::{ use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - newtypes::{CommentId, CommentReportId, CommunityId, PersonId}, + newtypes::{CommentReportId, PersonId}, schema::{ comment, comment_actions, @@ -26,26 +25,21 @@ use lemmy_db_schema::{ post, }, source::community::CommunityFollower, - utils::{ - actions, - actions_alias, - functions::coalesce, - get_conn, - limit_and_offset, - DbConn, - DbPool, - ListFn, - Queries, - ReadFn, - }, + utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool}, }; -fn queries<'a>() -> Queries< - impl ReadFn<'a, CommentReportView, (CommentReportId, PersonId)>, - impl ListFn<'a, CommentReportView, (CommentReportQuery, &'a LocalUserView)>, -> { - let all_joins = |query: comment_report::BoxedQuery<'a, Pg>, my_person_id: PersonId| { - query +impl CommentReportView { + /// returns the CommentReportView for the provided report_id + /// + /// * `report_id` - the report id to obtain + pub async fn read( + pool: &mut DbPool<'_>, + report_id: CommentReportId, + my_person_id: PersonId, + ) -> Result { + let conn = &mut get_conn(pool).await?; + comment_report::table + .find(report_id) .inner_join(comment::table) .inner_join(post::table.on(comment::post_id.eq(post::id))) .inner_join(community::table.on(post::community_id.eq(community::id))) @@ -117,456 +111,7 @@ fn queries<'a>() -> Queries< comment_actions::like_score.nullable(), aliases::person2.fields(person::all_columns).nullable(), )) - }; - - let read = move |mut conn: DbConn<'a>, (report_id, my_person_id): (CommentReportId, PersonId)| async move { - all_joins( - comment_report::table.find(report_id).into_boxed(), - my_person_id, - ) - .first(&mut conn) - .await - }; - - let list = move |mut conn: DbConn<'a>, - (options, user): (CommentReportQuery, &'a LocalUserView)| async move { - let mut query = all_joins(comment_report::table.into_boxed(), user.person.id); - - if let Some(community_id) = options.community_id { - query = query.filter(post::community_id.eq(community_id)); - } - - if let Some(comment_id) = options.comment_id { - query = query.filter(comment_report::comment_id.eq(comment_id)); - } - - // If viewing all reports, order by newest, but if viewing unresolved only, show the oldest - // first (FIFO) - if options.unresolved_only { - query = query - .filter(comment_report::resolved.eq(false)) - .order_by(comment_report::published.asc()); - } else { - query = query.order_by(comment_report::published.desc()); - } - - let (limit, offset) = limit_and_offset(options.page, options.limit)?; - - query = query.limit(limit).offset(offset); - - // If its not an admin, get only the ones you mod - if !user.local_user.admin { - query = query.filter(community_actions::became_moderator.is_not_null()); - } - - query.load::(&mut conn).await - }; - - Queries::new(read, list) -} - -impl CommentReportView { - /// returns the CommentReportView for the provided report_id - /// - /// * `report_id` - the report id to obtain - pub async fn read( - pool: &mut DbPool<'_>, - report_id: CommentReportId, - my_person_id: PersonId, - ) -> Result { - queries().read(pool, (report_id, my_person_id)).await - } - - /// Returns the current unresolved comment report count for the communities you mod - pub async fn get_report_count( - pool: &mut DbPool<'_>, - my_person_id: PersonId, - admin: bool, - community_id: Option, - ) -> Result { - use diesel::dsl::count; - - let conn = &mut get_conn(pool).await?; - - let mut query = comment_report::table - .inner_join(comment::table) - .inner_join(post::table.on(comment::post_id.eq(post::id))) - .filter(comment_report::resolved.eq(false)) - .into_boxed(); - - if let Some(community_id) = community_id { - query = query.filter(post::community_id.eq(community_id)) - } - - // If its not an admin, get only the ones you mod - if !admin { - query - .inner_join( - community_actions::table.on( - community_actions::community_id - .eq(post::community_id) - .and(community_actions::person_id.eq(my_person_id)) - .and(community_actions::became_moderator.is_not_null()), - ), - ) - .select(count(comment_report::id)) - .first::(conn) - .await - } else { - query - .select(count(comment_report::id)) - .first::(conn) - .await - } - } -} - -#[derive(Default)] -pub struct CommentReportQuery { - pub community_id: Option, - pub comment_id: Option, - pub page: Option, - pub limit: Option, - pub unresolved_only: bool, -} - -impl CommentReportQuery { - pub async fn list( - self, - pool: &mut DbPool<'_>, - user: &LocalUserView, - ) -> Result, Error> { - queries().list(pool, (self, user)).await - } -} - -#[cfg(test)] -#[expect(clippy::indexing_slicing)] -mod tests { - - use crate::{ - comment_report_view::{CommentReportQuery, CommentReportView}, - structs::LocalUserView, - }; - use lemmy_db_schema::{ - aggregates::structs::CommentAggregates, - source::{ - comment::{Comment, CommentInsertForm}, - comment_report::{CommentReport, CommentReportForm}, - community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, - instance::Instance, - local_user::{LocalUser, LocalUserInsertForm}, - local_user_vote_display_mode::LocalUserVoteDisplayMode, - person::{Person, PersonInsertForm}, - post::{Post, PostInsertForm}, - }, - traits::{Crud, Joinable, Reportable}, - utils::{build_db_pool_for_tests, RANK_DEFAULT}, - CommunityVisibility, - SubscribedType, - }; - use lemmy_utils::error::LemmyResult; - use pretty_assertions::assert_eq; - use serial_test::serial; - - #[tokio::test] - #[serial] - async fn test_crud() -> LemmyResult<()> { - let pool = &build_db_pool_for_tests(); - let pool = &mut pool.into(); - - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; - - let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_crv"); - - let inserted_timmy = Person::create(pool, &new_person).await?; - - let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id); - let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?; - let timmy_view = LocalUserView { - local_user: timmy_local_user, - local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), - person: inserted_timmy.clone(), - counts: Default::default(), - }; - - let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_crv"); - - let inserted_sara = Person::create(pool, &new_person_2).await?; - - // Add a third person, since new ppl can only report something once. - let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_crv"); - - let inserted_jessica = Person::create(pool, &new_person_3).await?; - - let new_community = CommunityInsertForm::new( - inserted_instance.id, - "test community crv".to_string(), - "nada".to_owned(), - "pubkey".to_string(), - ); - let inserted_community = Community::create(pool, &new_community).await?; - - // Make timmy a mod - let timmy_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - person_id: inserted_timmy.id, - }; - - let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form).await?; - - let new_post = PostInsertForm::new( - "A test post crv".into(), - inserted_timmy.id, - inserted_community.id, - ); - - let inserted_post = Post::create(pool, &new_post).await?; - - let comment_form = CommentInsertForm::new( - inserted_timmy.id, - inserted_post.id, - "A test comment 32".into(), - ); - let inserted_comment = Comment::create(pool, &comment_form, None).await?; - - // sara reports - let sara_report_form = CommentReportForm { - creator_id: inserted_sara.id, - comment_id: inserted_comment.id, - original_comment_text: "this was it at time of creation".into(), - reason: "from sara".into(), - }; - - let inserted_sara_report = CommentReport::report(pool, &sara_report_form).await?; - - // jessica reports - let jessica_report_form = CommentReportForm { - creator_id: inserted_jessica.id, - comment_id: inserted_comment.id, - original_comment_text: "this was it at time of creation".into(), - reason: "from jessica".into(), - }; - - let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form).await?; - - let agg = CommentAggregates::read(pool, inserted_comment.id).await?; - - let read_jessica_report_view = - CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?; - let expected_jessica_report_view = CommentReportView { - comment_report: inserted_jessica_report.clone(), - comment: inserted_comment.clone(), - post: inserted_post, - creator_is_moderator: true, - creator_is_admin: false, - creator_blocked: false, - subscribed: SubscribedType::NotSubscribed, - saved: false, - community: Community { - id: inserted_community.id, - name: inserted_community.name, - icon: None, - removed: false, - deleted: false, - nsfw: false, - actor_id: inserted_community.actor_id.clone(), - local: true, - title: inserted_community.title, - sidebar: None, - description: None, - updated: None, - banner: None, - hidden: false, - posting_restricted_to_mods: false, - published: inserted_community.published, - private_key: inserted_community.private_key, - public_key: inserted_community.public_key, - last_refreshed_at: inserted_community.last_refreshed_at, - followers_url: inserted_community.followers_url, - inbox_url: inserted_community.inbox_url, - moderators_url: inserted_community.moderators_url, - featured_url: inserted_community.featured_url, - instance_id: inserted_instance.id, - visibility: CommunityVisibility::Public, - }, - creator: Person { - id: inserted_jessica.id, - name: inserted_jessica.name, - display_name: None, - published: inserted_jessica.published, - avatar: None, - actor_id: inserted_jessica.actor_id.clone(), - local: true, - banned: false, - deleted: false, - bot_account: false, - bio: None, - banner: None, - updated: None, - inbox_url: inserted_jessica.inbox_url.clone(), - matrix_user_id: None, - ban_expires: None, - instance_id: inserted_instance.id, - private_key: inserted_jessica.private_key, - public_key: inserted_jessica.public_key, - last_refreshed_at: inserted_jessica.last_refreshed_at, - }, - comment_creator: Person { - id: inserted_timmy.id, - name: inserted_timmy.name.clone(), - display_name: None, - published: inserted_timmy.published, - avatar: None, - actor_id: inserted_timmy.actor_id.clone(), - local: true, - banned: false, - deleted: false, - bot_account: false, - bio: None, - banner: None, - updated: None, - inbox_url: inserted_timmy.inbox_url.clone(), - matrix_user_id: None, - ban_expires: None, - instance_id: inserted_instance.id, - private_key: inserted_timmy.private_key.clone(), - public_key: inserted_timmy.public_key.clone(), - last_refreshed_at: inserted_timmy.last_refreshed_at, - }, - creator_banned_from_community: false, - counts: CommentAggregates { - comment_id: inserted_comment.id, - score: 0, - upvotes: 0, - downvotes: 0, - published: agg.published, - child_count: 0, - hot_rank: RANK_DEFAULT, - controversy_rank: 0.0, - report_count: 2, - unresolved_report_count: 2, - }, - my_vote: None, - resolver: None, - }; - - assert_eq!(read_jessica_report_view, expected_jessica_report_view); - - let mut expected_sara_report_view = expected_jessica_report_view.clone(); - expected_sara_report_view.comment_report = inserted_sara_report; - expected_sara_report_view.creator = Person { - id: inserted_sara.id, - name: inserted_sara.name, - display_name: None, - published: inserted_sara.published, - avatar: None, - actor_id: inserted_sara.actor_id.clone(), - local: true, - banned: false, - deleted: false, - bot_account: false, - bio: None, - banner: None, - updated: None, - inbox_url: inserted_sara.inbox_url.clone(), - matrix_user_id: None, - ban_expires: None, - instance_id: inserted_instance.id, - private_key: inserted_sara.private_key, - public_key: inserted_sara.public_key, - last_refreshed_at: inserted_sara.last_refreshed_at, - }; - - // Do a batch read of timmys reports - let reports = CommentReportQuery::default() - .list(pool, &timmy_view) - .await?; - - assert_eq!( - reports, - [ - expected_jessica_report_view.clone(), - expected_sara_report_view.clone(), - ] - ); - - // Make sure the counts are correct - let report_count = - CommentReportView::get_report_count(pool, inserted_timmy.id, false, None).await?; - assert_eq!(2, report_count); - - // Try to resolve the report - CommentReport::resolve(pool, inserted_jessica_report.id, inserted_timmy.id).await?; - let read_jessica_report_view_after_resolve = - CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?; - - let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view; - expected_jessica_report_view_after_resolve - .comment_report - .resolved = true; - expected_jessica_report_view_after_resolve - .comment_report - .resolver_id = Some(inserted_timmy.id); - expected_jessica_report_view_after_resolve - .comment_report - .updated = read_jessica_report_view_after_resolve - .comment_report - .updated; - expected_jessica_report_view_after_resolve - .counts - .unresolved_report_count = 1; - expected_sara_report_view.counts.unresolved_report_count = 1; - expected_jessica_report_view_after_resolve.resolver = Some(Person { - id: inserted_timmy.id, - name: inserted_timmy.name.clone(), - display_name: None, - published: inserted_timmy.published, - avatar: None, - actor_id: inserted_timmy.actor_id.clone(), - local: true, - banned: false, - deleted: false, - bot_account: false, - bio: None, - banner: None, - updated: None, - inbox_url: inserted_timmy.inbox_url.clone(), - private_key: inserted_timmy.private_key.clone(), - public_key: inserted_timmy.public_key.clone(), - last_refreshed_at: inserted_timmy.last_refreshed_at, - matrix_user_id: None, - ban_expires: None, - instance_id: inserted_instance.id, - }); - - assert_eq!( - read_jessica_report_view_after_resolve, - expected_jessica_report_view_after_resolve - ); - - // Do a batch read of timmys reports - // It should only show saras, which is unresolved - let reports_after_resolve = CommentReportQuery { - unresolved_only: (true), - ..Default::default() - } - .list(pool, &timmy_view) - .await?; - assert_eq!(reports_after_resolve[0], expected_sara_report_view); - assert_eq!(reports_after_resolve.len(), 1); - - // Make sure the counts are correct - let report_count_after_resolved = - CommentReportView::get_report_count(pool, inserted_timmy.id, false, None).await?; - assert_eq!(1, report_count_after_resolved); - - Person::delete(pool, inserted_timmy.id).await?; - Person::delete(pool, inserted_sara.id).await?; - Person::delete(pool, inserted_jessica.id).await?; - Community::delete(pool, inserted_community.id).await?; - Instance::delete(pool, inserted_instance.id).await?; - - Ok(()) + .first(conn) + .await } } diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index c530c9739..4c7fd676c 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -1,6 +1,5 @@ -use crate::structs::{LocalUserView, PostReportView}; +use crate::structs::PostReportView; use diesel::{ - pg::Pg, result::Error, BoolExpressionMethods, ExpressionMethods, @@ -11,7 +10,7 @@ use diesel::{ use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - newtypes::{CommunityId, PersonId, PostId, PostReportId}, + newtypes::{PersonId, PostReportId}, schema::{ community, community_actions, @@ -24,26 +23,22 @@ use lemmy_db_schema::{ post_report, }, source::community::CommunityFollower, - utils::{ - actions, - actions_alias, - functions::coalesce, - get_conn, - limit_and_offset, - DbConn, - DbPool, - ListFn, - Queries, - ReadFn, - }, + utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool}, }; -fn queries<'a>() -> Queries< - impl ReadFn<'a, PostReportView, (PostReportId, PersonId)>, - impl ListFn<'a, PostReportView, (PostReportQuery, &'a LocalUserView)>, -> { - let all_joins = |query: post_report::BoxedQuery<'a, Pg>, my_person_id: PersonId| { - query +impl PostReportView { + /// returns the PostReportView for the provided report_id + /// + /// * `report_id` - the report id to obtain + pub async fn read( + pool: &mut DbPool<'_>, + report_id: PostReportId, + my_person_id: PersonId, + ) -> Result { + let conn = &mut get_conn(pool).await?; + + post_report::table + .find(report_id) .inner_join(post::table) .inner_join(community::table.on(post::community_id.eq(community::id))) .inner_join(person::table.on(post_report::creator_id.eq(person::id))) @@ -104,322 +99,7 @@ fn queries<'a>() -> Queries< post_aggregates::all_columns, aliases::person2.fields(person::all_columns.nullable()), )) - }; - - let read = move |mut conn: DbConn<'a>, (report_id, my_person_id): (PostReportId, PersonId)| async move { - all_joins( - post_report::table.find(report_id).into_boxed(), - my_person_id, - ) - .first(&mut conn) - .await - }; - - let list = move |mut conn: DbConn<'a>, (options, user): (PostReportQuery, &'a LocalUserView)| async move { - let mut query = all_joins(post_report::table.into_boxed(), user.person.id); - - if let Some(community_id) = options.community_id { - query = query.filter(post::community_id.eq(community_id)); - } - - if let Some(post_id) = options.post_id { - query = query.filter(post::id.eq(post_id)); - } - - // If viewing all reports, order by newest, but if viewing unresolved only, show the oldest - // first (FIFO) - if options.unresolved_only { - query = query - .filter(post_report::resolved.eq(false)) - .order_by(post_report::published.asc()); - } else { - query = query.order_by(post_report::published.desc()); - } - - // If its not an admin, get only the ones you mod - if !user.local_user.admin { - query = query.filter(community_actions::became_moderator.is_not_null()); - } - - let (limit, offset) = limit_and_offset(options.page, options.limit)?; - - query = query.limit(limit).offset(offset); - - query.load::(&mut conn).await - }; - - Queries::new(read, list) -} - -impl PostReportView { - /// returns the PostReportView for the provided report_id - /// - /// * `report_id` - the report id to obtain - pub async fn read( - pool: &mut DbPool<'_>, - report_id: PostReportId, - my_person_id: PersonId, - ) -> Result { - queries().read(pool, (report_id, my_person_id)).await - } - - /// returns the current unresolved post report count for the communities you mod - pub async fn get_report_count( - pool: &mut DbPool<'_>, - my_person_id: PersonId, - admin: bool, - community_id: Option, - ) -> Result { - use diesel::dsl::count; - let conn = &mut get_conn(pool).await?; - let mut query = post_report::table - .inner_join(post::table) - .filter(post_report::resolved.eq(false)) - .into_boxed(); - - if let Some(community_id) = community_id { - query = query.filter(post::community_id.eq(community_id)) - } - - // If its not an admin, get only the ones you mod - if !admin { - query - .inner_join( - community_actions::table.on( - community_actions::community_id - .eq(post::community_id) - .and(community_actions::person_id.eq(my_person_id)) - .and(community_actions::became_moderator.is_not_null()), - ), - ) - .select(count(post_report::id)) - .first::(conn) - .await - } else { - query - .select(count(post_report::id)) - .first::(conn) - .await - } - } -} - -#[derive(Default)] -pub struct PostReportQuery { - pub community_id: Option, - pub post_id: Option, - pub page: Option, - pub limit: Option, - pub unresolved_only: bool, -} - -impl PostReportQuery { - pub async fn list( - self, - pool: &mut DbPool<'_>, - user: &LocalUserView, - ) -> Result, Error> { - queries().list(pool, (self, user)).await - } -} - -#[cfg(test)] -#[expect(clippy::indexing_slicing)] -mod tests { - - use crate::{ - post_report_view::{PostReportQuery, PostReportView}, - structs::LocalUserView, - }; - use lemmy_db_schema::{ - aggregates::structs::PostAggregates, - assert_length, - source::{ - community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, - instance::Instance, - local_user::{LocalUser, LocalUserInsertForm}, - local_user_vote_display_mode::LocalUserVoteDisplayMode, - person::{Person, PersonInsertForm}, - post::{Post, PostInsertForm}, - post_report::{PostReport, PostReportForm}, - }, - traits::{Crud, Joinable, Reportable}, - utils::build_db_pool_for_tests, - }; - use lemmy_utils::error::LemmyResult; - use pretty_assertions::assert_eq; - use serial_test::serial; - - #[tokio::test] - #[serial] - async fn test_crud() -> LemmyResult<()> { - let pool = &build_db_pool_for_tests(); - let pool = &mut pool.into(); - - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; - - let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_prv"); - - let inserted_timmy = Person::create(pool, &new_person).await?; - - let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id); - let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?; - let timmy_view = LocalUserView { - local_user: timmy_local_user, - local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), - person: inserted_timmy.clone(), - counts: Default::default(), - }; - - let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_prv"); - - let inserted_sara = Person::create(pool, &new_person_2).await?; - - // Add a third person, since new ppl can only report something once. - let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_prv"); - - let inserted_jessica = Person::create(pool, &new_person_3).await?; - - let new_community = CommunityInsertForm::new( - inserted_instance.id, - "test community prv".to_string(), - "nada".to_owned(), - "pubkey".to_string(), - ); - let inserted_community = Community::create(pool, &new_community).await?; - - // Make timmy a mod - let timmy_moderator_form = CommunityModeratorForm { - community_id: inserted_community.id, - person_id: inserted_timmy.id, - }; - - let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form).await?; - - let new_post = PostInsertForm::new( - "A test post crv".into(), - inserted_timmy.id, - inserted_community.id, - ); - let inserted_post = Post::create(pool, &new_post).await?; - - // sara reports - let sara_report_form = PostReportForm { - creator_id: inserted_sara.id, - post_id: inserted_post.id, - original_post_name: "Orig post".into(), - original_post_url: None, - original_post_body: None, - reason: "from sara".into(), - }; - - PostReport::report(pool, &sara_report_form).await?; - - let new_post_2 = PostInsertForm::new( - "A test post crv 2".into(), - inserted_timmy.id, - inserted_community.id, - ); - let inserted_post_2 = Post::create(pool, &new_post_2).await?; - - // jessica reports - let jessica_report_form = PostReportForm { - creator_id: inserted_jessica.id, - post_id: inserted_post_2.id, - original_post_name: "Orig post".into(), - original_post_url: None, - original_post_body: None, - reason: "from jessica".into(), - }; - - let inserted_jessica_report = PostReport::report(pool, &jessica_report_form).await?; - - let read_jessica_report_view = - PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?; - - // Make sure the triggers are reading the aggregates correctly. - let agg_1 = PostAggregates::read(pool, inserted_post.id).await?; - let agg_2 = PostAggregates::read(pool, inserted_post_2.id).await?; - - assert_eq!( - read_jessica_report_view.post_report, - inserted_jessica_report - ); - assert_eq!(read_jessica_report_view.post, inserted_post_2); - assert_eq!(read_jessica_report_view.community.id, inserted_community.id); - assert_eq!(read_jessica_report_view.creator.id, inserted_jessica.id); - assert_eq!(read_jessica_report_view.post_creator.id, inserted_timmy.id); - assert_eq!(read_jessica_report_view.my_vote, None); - assert_eq!(read_jessica_report_view.resolver, None); - assert_eq!(agg_1.report_count, 1); - assert_eq!(agg_1.unresolved_report_count, 1); - assert_eq!(agg_2.report_count, 1); - assert_eq!(agg_2.unresolved_report_count, 1); - - // Do a batch read of timmys reports - let reports = PostReportQuery::default().list(pool, &timmy_view).await?; - - assert_eq!(reports[1].creator.id, inserted_sara.id); - assert_eq!(reports[0].creator.id, inserted_jessica.id); - - // Make sure the counts are correct - let report_count = - PostReportView::get_report_count(pool, inserted_timmy.id, false, None).await?; - assert_eq!(2, report_count); - - // Pretend the post was removed, and resolve all reports for that object. - // This is called manually in the API for post removals - PostReport::resolve_all_for_object(pool, inserted_jessica_report.post_id, inserted_timmy.id) - .await?; - - let read_jessica_report_view_after_resolve = - PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?; - assert!(read_jessica_report_view_after_resolve.post_report.resolved); - assert_eq!( - read_jessica_report_view_after_resolve - .post_report - .resolver_id, - Some(inserted_timmy.id) - ); - assert_eq!( - read_jessica_report_view_after_resolve - .resolver - .map(|r| r.id), - Some(inserted_timmy.id) - ); - - // Make sure the unresolved_post report got decremented in the trigger - let agg_2 = PostAggregates::read(pool, inserted_post_2.id).await?; - assert_eq!(agg_2.report_count, 1); - assert_eq!(agg_2.unresolved_report_count, 0); - - // Make sure the other unresolved report isn't changed - let agg_1 = PostAggregates::read(pool, inserted_post.id).await?; - assert_eq!(agg_1.report_count, 1); - assert_eq!(agg_1.unresolved_report_count, 1); - - // Do a batch read of timmys reports - // It should only show saras, which is unresolved - let reports_after_resolve = PostReportQuery { - unresolved_only: true, - ..Default::default() - } - .list(pool, &timmy_view) - .await?; - assert_length!(1, reports_after_resolve); - assert_eq!(reports_after_resolve[0].creator.id, inserted_sara.id); - - // Make sure the counts are correct - let report_count_after_resolved = - PostReportView::get_report_count(pool, inserted_timmy.id, false, None).await?; - assert_eq!(1, report_count_after_resolved); - - Person::delete(pool, inserted_timmy.id).await?; - Person::delete(pool, inserted_sara.id).await?; - Person::delete(pool, inserted_jessica.id).await?; - Community::delete(pool, inserted_community.id).await?; - Instance::delete(pool, inserted_instance.id).await?; - - Ok(()) + .first(conn) + .await } } diff --git a/crates/db_views/src/private_message_report_view.rs b/crates/db_views/src/private_message_report_view.rs index e59d99608..956ccf0e1 100644 --- a/crates/db_views/src/private_message_report_view.rs +++ b/crates/db_views/src/private_message_report_view.rs @@ -1,76 +1,13 @@ use crate::structs::PrivateMessageReportView; -use diesel::{ - pg::Pg, - result::Error, - ExpressionMethods, - JoinOnDsl, - NullableExpressionMethods, - QueryDsl, -}; +use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use lemmy_db_schema::{ aliases, newtypes::PrivateMessageReportId, schema::{person, private_message, private_message_report}, - utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, + utils::{get_conn, DbPool}, }; -fn queries<'a>() -> Queries< - impl ReadFn<'a, PrivateMessageReportView, PrivateMessageReportId>, - impl ListFn<'a, PrivateMessageReportView, PrivateMessageReportQuery>, -> { - let all_joins = - |query: private_message_report::BoxedQuery<'a, Pg>| { - query - .inner_join(private_message::table) - .inner_join(person::table.on(private_message::creator_id.eq(person::id))) - .inner_join( - aliases::person1 - .on(private_message_report::creator_id.eq(aliases::person1.field(person::id))), - ) - .left_join(aliases::person2.on( - private_message_report::resolver_id.eq(aliases::person2.field(person::id).nullable()), - )) - .select(( - private_message_report::all_columns, - private_message::all_columns, - person::all_columns, - aliases::person1.fields(person::all_columns), - aliases::person2.fields(person::all_columns).nullable(), - )) - }; - - let read = move |mut conn: DbConn<'a>, report_id: PrivateMessageReportId| async move { - all_joins(private_message_report::table.find(report_id).into_boxed()) - .first(&mut conn) - .await - }; - - let list = move |mut conn: DbConn<'a>, options: PrivateMessageReportQuery| async move { - let mut query = all_joins(private_message_report::table.into_boxed()); - - // If viewing all reports, order by newest, but if viewing unresolved only, show the oldest - // first (FIFO) - if options.unresolved_only { - query = query - .filter(private_message_report::resolved.eq(false)) - .order_by(private_message_report::published.asc()); - } else { - query = query.order_by(private_message_report::published.desc()); - } - - let (limit, offset) = limit_and_offset(options.page, options.limit)?; - - query - .limit(limit) - .offset(offset) - .load::(&mut conn) - .await - }; - - Queries::new(read, list) -} - impl PrivateMessageReportView { /// returns the PrivateMessageReportView for the provided report_id /// @@ -79,118 +16,28 @@ impl PrivateMessageReportView { pool: &mut DbPool<'_>, report_id: PrivateMessageReportId, ) -> Result { - queries().read(pool, report_id).await - } - - /// Returns the current unresolved post report count for the communities you mod - pub async fn get_report_count(pool: &mut DbPool<'_>) -> Result { - use diesel::dsl::count; let conn = &mut get_conn(pool).await?; - private_message_report::table + .find(report_id) .inner_join(private_message::table) - .filter(private_message_report::resolved.eq(false)) - .into_boxed() - .select(count(private_message_report::id)) - .first::(conn) + .inner_join(person::table.on(private_message::creator_id.eq(person::id))) + .inner_join( + aliases::person1 + .on(private_message_report::creator_id.eq(aliases::person1.field(person::id))), + ) + .left_join( + aliases::person2.on( + private_message_report::resolver_id.eq(aliases::person2.field(person::id).nullable()), + ), + ) + .select(( + private_message_report::all_columns, + private_message::all_columns, + person::all_columns, + aliases::person1.fields(person::all_columns), + aliases::person2.fields(person::all_columns).nullable(), + )) + .first(conn) .await } } - -#[derive(Default)] -pub struct PrivateMessageReportQuery { - pub page: Option, - pub limit: Option, - pub unresolved_only: bool, -} - -impl PrivateMessageReportQuery { - pub async fn list(self, pool: &mut DbPool<'_>) -> Result, Error> { - queries().list(pool, self).await - } -} - -#[cfg(test)] -#[expect(clippy::indexing_slicing)] -mod tests { - - use crate::private_message_report_view::PrivateMessageReportQuery; - use lemmy_db_schema::{ - assert_length, - source::{ - instance::Instance, - person::{Person, PersonInsertForm}, - private_message::{PrivateMessage, PrivateMessageInsertForm}, - private_message_report::{PrivateMessageReport, PrivateMessageReportForm}, - }, - traits::{Crud, Reportable}, - utils::build_db_pool_for_tests, - }; - use lemmy_utils::error::LemmyResult; - use pretty_assertions::assert_eq; - use serial_test::serial; - - #[tokio::test] - #[serial] - async fn test_crud() -> LemmyResult<()> { - let pool = &build_db_pool_for_tests(); - let pool = &mut pool.into(); - - let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; - - let new_person_1 = PersonInsertForm::test_form(inserted_instance.id, "timmy_mrv"); - let inserted_timmy = Person::create(pool, &new_person_1).await?; - - let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "jessica_mrv"); - let inserted_jessica = Person::create(pool, &new_person_2).await?; - - // timmy sends private message to jessica - let pm_form = PrivateMessageInsertForm::new( - inserted_timmy.id, - inserted_jessica.id, - "something offensive".to_string(), - ); - let pm = PrivateMessage::create(pool, &pm_form).await?; - - // jessica reports private message - let pm_report_form = PrivateMessageReportForm { - creator_id: inserted_jessica.id, - original_pm_text: pm.content.clone(), - private_message_id: pm.id, - reason: "its offensive".to_string(), - }; - let pm_report = PrivateMessageReport::report(pool, &pm_report_form).await?; - - let reports = PrivateMessageReportQuery::default().list(pool).await?; - assert_length!(1, reports); - assert!(!reports[0].private_message_report.resolved); - assert_eq!(inserted_timmy.name, reports[0].private_message_creator.name); - assert_eq!(inserted_jessica.name, reports[0].creator.name); - assert_eq!(pm_report.reason, reports[0].private_message_report.reason); - assert_eq!(pm.content, reports[0].private_message.content); - - let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "admin_mrv"); - let inserted_admin = Person::create(pool, &new_person_3).await?; - - // admin resolves the report (after taking appropriate action) - PrivateMessageReport::resolve(pool, pm_report.id, inserted_admin.id).await?; - - let reports = PrivateMessageReportQuery { - unresolved_only: (false), - ..Default::default() - } - .list(pool) - .await?; - assert_length!(1, reports); - assert!(reports[0].private_message_report.resolved); - assert!(reports[0].resolver.is_some()); - assert_eq!( - Some(&inserted_admin.name), - reports[0].resolver.as_ref().map(|r| &r.name) - ); - - Instance::delete(pool, inserted_instance.id).await?; - - Ok(()) - } -} diff --git a/crates/db_views/src/report_combined_view.rs b/crates/db_views/src/report_combined_view.rs index 55bf533a9..b1232940f 100644 --- a/crates/db_views/src/report_combined_view.rs +++ b/crates/db_views/src/report_combined_view.rs @@ -151,6 +151,8 @@ impl ReportCombinedQuery { let options = self; let my_person_id = user.local_user.person_id; let item_creator = aliases::person1.field(person::id); + + let resolver = aliases::person2.field(person::id).nullable(); let conn = &mut get_conn(pool).await?; // Notes: since the post_report_id and comment_report_id are optional columns, @@ -225,7 +227,15 @@ impl ReportCombinedQuery { .left_join( comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)), ) - .left_join(aliases::person2.on(item_creator.eq(aliases::person2.field(person::id)))) + // The resolver + .left_join( + aliases::person2.on( + private_message_report::resolver_id + .eq(resolver) + .or(post_report::resolver_id.eq(resolver)) + .or(comment_report::resolver_id.eq(resolver)), + ), + ) .left_join(actions( comment_actions::table, Some(my_person_id), @@ -399,9 +409,16 @@ mod tests { use crate::{ report_combined_view::ReportCombinedQuery, - structs::{LocalUserView, ReportCombinedView, ReportCombinedViewInternal}, + structs::{ + CommentReportView, + LocalUserView, + PostReportView, + ReportCombinedView, + ReportCombinedViewInternal, + }, }; use lemmy_db_schema::{ + aggregates::structs::{CommentAggregates, PostAggregates}, assert_length, source::{ comment::{Comment, CommentInsertForm}, @@ -417,18 +434,26 @@ mod tests { private_message_report::{PrivateMessageReport, PrivateMessageReportForm}, }, traits::{Crud, Joinable, Reportable}, - utils::build_db_pool_for_tests, + utils::{build_db_pool_for_tests, DbPool}, }; use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; use serial_test::serial; - #[tokio::test] - #[serial] - async fn test_crud() -> LemmyResult<()> { - let pool = &build_db_pool_for_tests(); - let pool = &mut pool.into(); + struct Data { + instance: Instance, + timmy: Person, + sara: Person, + jessica: Person, + timmy_view: LocalUserView, + admin_view: LocalUserView, + community: Community, + post: Post, + post_2: Post, + comment: Comment, + } + async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult { let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?; let timmy_form = PersonInsertForm::test_form(inserted_instance.id, "timmy_rcv"); @@ -457,6 +482,9 @@ mod tests { let sara_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rcv"); let inserted_sara = Person::create(pool, &sara_form).await?; + let jessica_form = PersonInsertForm::test_form(inserted_instance.id, "jessica_mrv"); + let inserted_jessica = Person::create(pool, &jessica_form).await?; + let community_form = CommunityInsertForm::new( inserted_instance.id, "test community crv".to_string(), @@ -479,16 +507,12 @@ mod tests { ); let inserted_post = Post::create(pool, &post_form).await?; - // sara reports the post - let sara_report_post_form = PostReportForm { - creator_id: inserted_sara.id, - post_id: inserted_post.id, - original_post_name: "Orig post".into(), - original_post_url: None, - original_post_body: None, - reason: "from sara".into(), - }; - let inserted_post_report = PostReport::report(pool, &sara_report_post_form).await?; + let new_post_2 = PostInsertForm::new( + "A test post crv 2".into(), + inserted_timmy.id, + inserted_community.id, + ); + let inserted_post_2 = Post::create(pool, &new_post_2).await?; // Timmy creates a comment let comment_form = CommentInsertForm::new( @@ -498,10 +522,48 @@ mod tests { ); let inserted_comment = Comment::create(pool, &comment_form, None).await?; + Ok(Data { + instance: inserted_instance, + timmy: inserted_timmy, + sara: inserted_sara, + jessica: inserted_jessica, + admin_view, + timmy_view, + community: inserted_community, + post: inserted_post, + post_2: inserted_post_2, + comment: inserted_comment, + }) + } + + async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> { + Instance::delete(pool, data.instance.id).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn test_combined() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // sara reports the post + let sara_report_post_form = PostReportForm { + creator_id: data.sara.id, + post_id: data.post.id, + original_post_name: "Orig post".into(), + original_post_url: None, + original_post_body: None, + reason: "from sara".into(), + }; + let inserted_post_report = PostReport::report(pool, &sara_report_post_form).await?; + // Sara reports the comment let sara_report_comment_form = CommentReportForm { - creator_id: inserted_sara.id, - comment_id: inserted_comment.id, + creator_id: data.sara.id, + comment_id: data.comment.id, original_comment_text: "A test comment rv".into(), reason: "from sara".into(), }; @@ -509,15 +571,15 @@ mod tests { // Timmy creates a private message report let pm_form = PrivateMessageInsertForm::new( - inserted_timmy.id, - inserted_sara.id, + data.timmy.id, + data.sara.id, "something offensive crv".to_string(), ); let inserted_pm = PrivateMessage::create(pool, &pm_form).await?; // sara reports private message let pm_report_form = PrivateMessageReportForm { - creator_id: inserted_sara.id, + creator_id: data.sara.id, original_pm_text: inserted_pm.content.clone(), private_message_id: inserted_pm.id, reason: "its offensive".to_string(), @@ -526,22 +588,22 @@ mod tests { // Do a batch read of admins reports let reports = ReportCombinedQuery::default() - .list(pool, &admin_view) + .list(pool, &data.admin_view) .await?; assert_eq!(3, reports.len()); // Make sure the report types are correct if let ReportCombinedView::Post(v) = &reports[2] { - assert_eq!(inserted_post.id, v.post.id); - assert_eq!(inserted_sara.id, v.creator.id); - assert_eq!(inserted_timmy.id, v.post_creator.id); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.sara.id, v.creator.id); + assert_eq!(data.timmy.id, v.post_creator.id); } else { panic!("wrong type"); } if let ReportCombinedView::Comment(v) = &reports[1] { - assert_eq!(inserted_comment.id, v.comment.id); - assert_eq!(inserted_post.id, v.post.id); - assert_eq!(inserted_timmy.id, v.comment_creator.id); + assert_eq!(data.comment.id, v.comment.id); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.timmy.id, v.comment_creator.id); } else { panic!("wrong type"); } @@ -552,38 +614,38 @@ mod tests { } let report_count_admin = - ReportCombinedViewInternal::get_report_count(pool, &admin_view, None).await?; + ReportCombinedViewInternal::get_report_count(pool, &data.admin_view, None).await?; assert_eq!(3, report_count_admin); // Timmy should only see 2 reports, since they're not an admin, // but they do mod the community let reports = ReportCombinedQuery::default() - .list(pool, &timmy_view) + .list(pool, &data.timmy_view) .await?; assert_eq!(2, reports.len()); // Make sure the report types are correct if let ReportCombinedView::Post(v) = &reports[1] { - assert_eq!(inserted_post.id, v.post.id); - assert_eq!(inserted_sara.id, v.creator.id); - assert_eq!(inserted_timmy.id, v.post_creator.id); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.sara.id, v.creator.id); + assert_eq!(data.timmy.id, v.post_creator.id); } else { panic!("wrong type"); } if let ReportCombinedView::Comment(v) = &reports[0] { - assert_eq!(inserted_comment.id, v.comment.id); - assert_eq!(inserted_post.id, v.post.id); - assert_eq!(inserted_timmy.id, v.comment_creator.id); + assert_eq!(data.comment.id, v.comment.id); + assert_eq!(data.post.id, v.post.id); + assert_eq!(data.timmy.id, v.comment_creator.id); } else { panic!("wrong type"); } let report_count_timmy = - ReportCombinedViewInternal::get_report_count(pool, &timmy_view, None).await?; + ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; assert_eq!(2, report_count_timmy); // Resolve the post report - PostReport::resolve(pool, inserted_post_report.id, inserted_timmy.id).await?; + PostReport::resolve(pool, inserted_post_report.id, data.timmy.id).await?; // Do a batch read of timmys reports // It should only show saras, which is unresolved @@ -591,16 +653,312 @@ mod tests { unresolved_only: Some(true), ..Default::default() } - .list(pool, &timmy_view) + .list(pool, &data.timmy_view) .await?; assert_length!(1, reports_after_resolve); // Make sure the counts are correct let report_count_after_resolved = - ReportCombinedViewInternal::get_report_count(pool, &timmy_view, None).await?; + ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; assert_eq!(1, report_count_after_resolved); - Instance::delete(pool, inserted_instance.id).await?; + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn test_private_message_reports() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // timmy sends private message to jessica + let pm_form = PrivateMessageInsertForm::new( + data.timmy.id, + data.jessica.id, + "something offensive".to_string(), + ); + let pm = PrivateMessage::create(pool, &pm_form).await?; + + // jessica reports private message + let pm_report_form = PrivateMessageReportForm { + creator_id: data.jessica.id, + original_pm_text: pm.content.clone(), + private_message_id: pm.id, + reason: "its offensive".to_string(), + }; + let pm_report = PrivateMessageReport::report(pool, &pm_report_form).await?; + + let reports = ReportCombinedQuery::default() + .list(pool, &data.admin_view) + .await?; + assert_length!(1, reports); + if let ReportCombinedView::PrivateMessage(v) = &reports[0] { + assert!(!v.private_message_report.resolved); + assert_eq!(data.timmy.name, v.private_message_creator.name); + assert_eq!(data.jessica.name, v.creator.name); + assert_eq!(pm_report.reason, v.private_message_report.reason); + assert_eq!(pm.content, v.private_message.content); + } else { + panic!("wrong type"); + } + + // admin resolves the report (after taking appropriate action) + PrivateMessageReport::resolve(pool, pm_report.id, data.admin_view.person.id).await?; + + let reports = ReportCombinedQuery::default() + .list(pool, &data.admin_view) + .await?; + assert_length!(1, reports); + if let ReportCombinedView::PrivateMessage(v) = &reports[0] { + assert!(v.private_message_report.resolved); + assert!(v.resolver.is_some()); + assert_eq!( + Some(&data.admin_view.person.name), + v.resolver.as_ref().map(|r| &r.name) + ); + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn test_post_reports() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // sara reports + let sara_report_form = PostReportForm { + creator_id: data.sara.id, + post_id: data.post.id, + original_post_name: "Orig post".into(), + original_post_url: None, + original_post_body: None, + reason: "from sara".into(), + }; + + PostReport::report(pool, &sara_report_form).await?; + + // jessica reports + let jessica_report_form = PostReportForm { + creator_id: data.jessica.id, + post_id: data.post_2.id, + original_post_name: "Orig post".into(), + original_post_url: None, + original_post_body: None, + reason: "from jessica".into(), + }; + + let inserted_jessica_report = PostReport::report(pool, &jessica_report_form).await?; + + let read_jessica_report_view = + PostReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?; + + // Make sure the triggers are reading the aggregates correctly. + let agg_1 = PostAggregates::read(pool, data.post.id).await?; + let agg_2 = PostAggregates::read(pool, data.post_2.id).await?; + + assert_eq!( + read_jessica_report_view.post_report, + inserted_jessica_report + ); + assert_eq!(read_jessica_report_view.post, data.post_2); + assert_eq!(read_jessica_report_view.community.id, data.community.id); + assert_eq!(read_jessica_report_view.creator.id, data.jessica.id); + assert_eq!(read_jessica_report_view.post_creator.id, data.timmy.id); + assert_eq!(read_jessica_report_view.my_vote, None); + assert_eq!(read_jessica_report_view.resolver, None); + assert_eq!(agg_1.report_count, 1); + assert_eq!(agg_1.unresolved_report_count, 1); + assert_eq!(agg_2.report_count, 1); + assert_eq!(agg_2.unresolved_report_count, 1); + + // Do a batch read of timmys reports + let reports = ReportCombinedQuery::default() + .list(pool, &data.timmy_view) + .await?; + + if let ReportCombinedView::Post(v) = &reports[1] { + assert_eq!(v.creator.id, data.sara.id); + } else { + panic!("wrong type"); + } + if let ReportCombinedView::Post(v) = &reports[0] { + assert_eq!(v.creator.id, data.jessica.id); + } else { + panic!("wrong type"); + } + + // Make sure the counts are correct + let report_count = + ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; + assert_eq!(2, report_count); + + // Pretend the post was removed, and resolve all reports for that object. + // This is called manually in the API for post removals + PostReport::resolve_all_for_object(pool, inserted_jessica_report.post_id, data.timmy.id) + .await?; + + let read_jessica_report_view_after_resolve = + PostReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?; + assert!(read_jessica_report_view_after_resolve.post_report.resolved); + assert_eq!( + read_jessica_report_view_after_resolve + .post_report + .resolver_id, + Some(data.timmy.id) + ); + assert_eq!( + read_jessica_report_view_after_resolve + .resolver + .map(|r| r.id), + Some(data.timmy.id) + ); + + // Make sure the unresolved_post report got decremented in the trigger + let agg_2 = PostAggregates::read(pool, data.post_2.id).await?; + assert_eq!(agg_2.report_count, 1); + assert_eq!(agg_2.unresolved_report_count, 0); + + // Make sure the other unresolved report isn't changed + let agg_1 = PostAggregates::read(pool, data.post.id).await?; + assert_eq!(agg_1.report_count, 1); + assert_eq!(agg_1.unresolved_report_count, 1); + + // Do a batch read of timmys reports + // It should only show saras, which is unresolved + let reports_after_resolve = ReportCombinedQuery { + unresolved_only: Some(true), + ..Default::default() + } + .list(pool, &data.timmy_view) + .await?; + + if let ReportCombinedView::Post(v) = &reports_after_resolve[0] { + assert_length!(1, reports_after_resolve); + assert_eq!(v.creator.id, data.sara.id); + } else { + panic!("wrong type"); + } + + // Make sure the counts are correct + let report_count_after_resolved = + ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; + assert_eq!(1, report_count_after_resolved); + + cleanup(data, pool).await?; + + Ok(()) + } + + #[tokio::test] + #[serial] + async fn test_comment_reports() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // sara reports + let sara_report_form = CommentReportForm { + creator_id: data.sara.id, + comment_id: data.comment.id, + original_comment_text: "this was it at time of creation".into(), + reason: "from sara".into(), + }; + + CommentReport::report(pool, &sara_report_form).await?; + + // jessica reports + let jessica_report_form = CommentReportForm { + creator_id: data.jessica.id, + comment_id: data.comment.id, + original_comment_text: "this was it at time of creation".into(), + reason: "from jessica".into(), + }; + + let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form).await?; + + let agg = CommentAggregates::read(pool, data.comment.id).await?; + assert_eq!(agg.report_count, 2); + + let read_jessica_report_view = + CommentReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?; + assert_eq!(read_jessica_report_view.counts.unresolved_report_count, 2); + + // Do a batch read of timmys reports + let reports = ReportCombinedQuery::default() + .list(pool, &data.timmy_view) + .await?; + + if let ReportCombinedView::Comment(v) = &reports[0] { + assert_eq!(v.creator.id, data.jessica.id); + } else { + panic!("wrong type"); + } + if let ReportCombinedView::Comment(v) = &reports[1] { + assert_eq!(v.creator.id, data.sara.id); + } else { + panic!("wrong type"); + } + + // Make sure the counts are correct + let report_count = + ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; + assert_eq!(2, report_count); + + // Resolve the report + CommentReport::resolve(pool, inserted_jessica_report.id, data.timmy.id).await?; + let read_jessica_report_view_after_resolve = + CommentReportView::read(pool, inserted_jessica_report.id, data.timmy.id).await?; + + assert!( + read_jessica_report_view_after_resolve + .comment_report + .resolved + ); + assert_eq!( + read_jessica_report_view_after_resolve + .comment_report + .resolver_id, + Some(data.timmy.id) + ); + assert_eq!( + read_jessica_report_view_after_resolve + .resolver + .map(|r| r.id), + Some(data.timmy.id) + ); + + // Do a batch read of timmys reports + // It should only show saras, which is unresolved + let reports_after_resolve = ReportCombinedQuery { + unresolved_only: Some(true), + ..Default::default() + } + .list(pool, &data.timmy_view) + .await?; + + if let ReportCombinedView::Comment(v) = &reports_after_resolve[0] { + assert_length!(1, reports_after_resolve); + assert_eq!(v.creator.id, data.sara.id); + } else { + panic!("wrong type"); + } + + // Make sure the counts are correct + let report_count_after_resolved = + ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; + assert_eq!(1, report_count_after_resolved); + + cleanup(data, pool).await?; Ok(()) } diff --git a/src/api_routes_http.rs b/src/api_routes_http.rs index 452fb9870..3f6153435 100644 --- a/src/api_routes_http.rs +++ b/src/api_routes_http.rs @@ -61,21 +61,9 @@ use lemmy_api::{ }, private_message::mark_read::mark_pm_as_read, reports::{ - comment_report::{ - create::create_comment_report, - list::list_comment_reports, - resolve::resolve_comment_report, - }, - post_report::{ - create::create_post_report, - list::list_post_reports, - resolve::resolve_post_report, - }, - private_message_report::{ - create::create_pm_report, - list::list_pm_reports, - resolve::resolve_pm_report, - }, + comment_report::{create::create_comment_report, resolve::resolve_comment_report}, + post_report::{create::create_post_report, resolve::resolve_post_report}, + private_message_report::{create::create_pm_report, resolve::resolve_pm_report}, report_combined::list::list_reports, }, site::{ @@ -255,7 +243,6 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .route("/save", put().to(save_post)) .route("/report", post().to(create_post_report)) .route("/report/resolve", put().to(resolve_post_report)) - .route("/report/list", get().to(list_post_reports)) .route("/site_metadata", get().to(get_link_metadata)), ) // Comment @@ -280,8 +267,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .route("/save", put().to(save_comment)) .route("/list", get().to(list_comments)) .route("/report", post().to(create_comment_report)) - .route("/report/resolve", put().to(resolve_comment_report)) - .route("/report/list", get().to(list_comment_reports)), + .route("/report/resolve", put().to(resolve_comment_report)), ) .service( scope("report") @@ -298,8 +284,7 @@ pub fn config(cfg: &mut ServiceConfig, rate_limit: &RateLimitCell) { .route("/delete", post().to(delete_private_message)) .route("/mark_as_read", post().to(mark_pm_as_read)) .route("/report", post().to(create_pm_report)) - .route("/report/resolve", put().to(resolve_pm_report)) - .route("/report/list", get().to(list_pm_reports)), + .route("/report/resolve", put().to(resolve_pm_report)), ) // User .service(