From 012e8c308544584247748b33c018901f8eb569eb Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 27 Jan 2025 04:13:45 -0500 Subject: [PATCH] Adding post_id and type_ filters to combined reports. (#5348) * Adding post_id and type_ filters to combined reports. - Added tests for these also. - Some additional cleanup of the joins in reports_combined. - Fixes #5265 * Adding period. --- .../api/src/reports/report_combined/list.rs | 9 +- crates/api_common/src/lib.rs | 2 +- crates/api_common/src/reports/combined.rs | 11 +- crates/db_schema/src/lib.rs | 14 +- crates/db_views/src/report_combined_view.rs | 135 ++++++++++++------ 5 files changed, 116 insertions(+), 55 deletions(-) diff --git a/crates/api/src/reports/report_combined/list.rs b/crates/api/src/reports/report_combined/list.rs index 12548d189..62df91502 100644 --- a/crates/api/src/reports/report_combined/list.rs +++ b/crates/api/src/reports/report_combined/list.rs @@ -15,9 +15,6 @@ pub async fn list_reports( context: Data, local_user_view: LocalUserView, ) -> LemmyResult> { - let community_id = data.community_id; - let unresolved_only = data.unresolved_only; - check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?; // parse pagination token @@ -29,8 +26,10 @@ pub async fn list_reports( let page_back = data.page_back; let reports = ReportCombinedQuery { - community_id, - unresolved_only, + community_id: data.community_id, + post_id: data.post_id, + type_: data.type_, + unresolved_only: data.unresolved_only, page_after, page_back, } diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index dd1c0a68a..fde2102d5 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -35,7 +35,7 @@ use std::{cmp::min, time::Duration}; #[derive(Debug, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(ts_rs::TS))] #[cfg_attr(feature = "full", ts(export))] -/// Saves settings for your user. +/// A response that completes successfully. pub struct SuccessResponse { pub success: bool, } diff --git a/crates/api_common/src/reports/combined.rs b/crates/api_common/src/reports/combined.rs index 69d928830..cd87c8a3d 100644 --- a/crates/api_common/src/reports/combined.rs +++ b/crates/api_common/src/reports/combined.rs @@ -1,4 +1,7 @@ -use lemmy_db_schema::newtypes::CommunityId; +use lemmy_db_schema::{ + newtypes::{CommunityId, PostId}, + ReportType, +}; use lemmy_db_views::structs::{ReportCombinedPaginationCursor, ReportCombinedView}; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -14,6 +17,12 @@ pub struct ListReports { /// Only shows the unresolved reports #[cfg_attr(feature = "full", ts(optional))] pub unresolved_only: Option, + /// Filter the type of report. + #[cfg_attr(feature = "full", ts(optional))] + pub type_: Option, + /// Filter by the post id. Can return either comment or post reports. + #[cfg_attr(feature = "full", ts(optional))] + pub post_id: Option, /// if no community is given, it returns reports for all communities moderated by the auth user #[cfg_attr(feature = "full", ts(optional))] pub community_id: Option, diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index 9c0f0ea0f..fd270e6d5 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -229,13 +229,25 @@ pub enum InboxDataType { #[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "full", derive(TS))] #[cfg_attr(feature = "full", ts(export))] -/// A list of possible types for the various modlog actions. +/// A list of possible types for a person's content. pub enum PersonContentType { All, Comments, Posts, } +#[derive(EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "full", derive(TS))] +#[cfg_attr(feature = "full", ts(export))] +/// A list of possible types for reports. +pub enum ReportType { + All, + Posts, + Comments, + PrivateMessages, + Communities, +} + #[derive( EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq, Eq, Hash, )] diff --git a/crates/db_views/src/report_combined_view.rs b/crates/db_views/src/report_combined_view.rs index 335fccb81..201e02728 100644 --- a/crates/db_views/src/report_combined_view.rs +++ b/crates/db_views/src/report_combined_view.rs @@ -22,7 +22,7 @@ use diesel_async::RunQueryDsl; use i_love_jesus::PaginatedQueryBuilder; use lemmy_db_schema::{ aliases::{self, creator_community_actions}, - newtypes::CommunityId, + newtypes::{CommunityId, PostId}, schema::{ comment, comment_actions, @@ -49,6 +49,7 @@ use lemmy_db_schema::{ }, traits::InternalToCombinedView, utils::{actions, actions_alias, functions::coalesce, get_conn, DbPool, ReverseTimestampKey}, + ReportType, }; use lemmy_utils::error::LemmyResult; @@ -148,6 +149,8 @@ pub struct PaginationCursorData(ReportCombined); #[derive(Default)] pub struct ReportCombinedQuery { + pub type_: Option, + pub post_id: Option, pub community_id: Option, pub unresolved_only: Option, pub page_after: Option, @@ -167,6 +170,33 @@ impl ReportCombinedQuery { let conn = &mut get_conn(pool).await?; + let report_creator_join = post_report::creator_id + .eq(report_creator) + .or(comment_report::creator_id.eq(report_creator)) + .or(private_message_report::creator_id.eq(report_creator)) + .or(community_report::creator_id.eq(report_creator)); + + let item_creator_join = post::creator_id + .eq(item_creator) + .or(comment::creator_id.eq(item_creator)) + .or(private_message::creator_id.eq(item_creator)); + + let resolver_join = private_message_report::resolver_id + .eq(resolver) + .or(post_report::resolver_id.eq(resolver)) + .or(comment_report::resolver_id.eq(resolver)) + .or(community_report::resolver_id.eq(resolver)); + + let post_join = post_report::post_id + .eq(post::id) + .or(comment::post_id.eq(post::id)); + + let community_join = community::table.on( + community_report::community_id + .eq(community::id) + .or(post::community_id.eq(community::id)), + ); + // Notes: since the post_report_id and comment_report_id are optional columns, // many joins must use an OR condition. // For example, the report creator must be the person table joined to either: @@ -178,15 +208,7 @@ impl ReportCombinedQuery { .left_join(private_message_report::table) .left_join(community_report::table) // The report creator - .inner_join( - person::table.on( - post_report::creator_id - .eq(report_creator) - .or(comment_report::creator_id.eq(report_creator)) - .or(private_message_report::creator_id.eq(report_creator)) - .or(community_report::creator_id.eq(report_creator)), - ), - ) + .inner_join(person::table.on(report_creator_join)) // The comment .left_join(comment::table.on(comment_report::comment_id.eq(comment::id))) // The private message @@ -195,30 +217,13 @@ impl ReportCombinedQuery { .on(private_message_report::private_message_id.eq(private_message::id)), ) // The post - .left_join( - post::table.on( - post_report::post_id - .eq(post::id) - .or(comment::post_id.eq(post::id)), - ), - ) + .left_join(post::table.on(post_join)) // The item creator (`item_creator` is the id of this person) - .left_join( - aliases::person1.on( - post::creator_id - .eq(item_creator) - .or(comment::creator_id.eq(item_creator)) - .or(private_message::creator_id.eq(item_creator)), - ), - ) + .left_join(aliases::person1.on(item_creator_join)) + // The resolver + .left_join(aliases::person2.on(resolver_join)) // The community - .left_join( - community::table.on( - post::community_id - .eq(community::id) - .or(community_report::community_id.eq(community::id)), - ), - ) + .left_join(community_join) .left_join(actions_alias( creator_community_actions, item_creator, @@ -250,16 +255,6 @@ impl ReportCombinedQuery { community_aggregates::table .on(community_report::community_id.eq(community_aggregates::community_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)) - .or(community_report::resolver_id.eq(resolver)), - ), - ) .left_join(actions( comment_actions::table, Some(my_person_id), @@ -318,6 +313,10 @@ impl ReportCombinedQuery { ); } + if let Some(post_id) = self.post_id { + query = query.filter(post::id.eq(post_id)); + } + // If its not an admin, get only the ones you mod if !user.local_user.admin { query = query.filter( @@ -337,6 +336,18 @@ impl ReportCombinedQuery { query = query.after(page_after); } + if let Some(type_) = self.type_ { + query = match type_ { + ReportType::All => query, + ReportType::Posts => query.filter(report_combined::post_report_id.is_not_null()), + ReportType::Comments => query.filter(report_combined::comment_report_id.is_not_null()), + ReportType::PrivateMessages => { + query.filter(report_combined::private_message_report_id.is_not_null()) + } + ReportType::Communities => query.filter(report_combined::community_report_id.is_not_null()), + } + } + // If viewing all reports, order by newest, but if viewing unresolved only, show the oldest // first (FIFO) if self.unresolved_only.unwrap_or_default() { @@ -508,6 +519,7 @@ mod tests { }, traits::{Crud, Joinable, Reportable}, utils::{build_db_pool_for_tests, DbPool}, + ReportType, }; use lemmy_utils::error::LemmyResult; use pretty_assertions::assert_eq; @@ -677,7 +689,7 @@ mod tests { let reports = ReportCombinedQuery::default() .list(pool, &data.admin_view) .await?; - assert_eq!(4, reports.len()); + assert_length!(4, reports); // Make sure the report types are correct if let ReportCombinedView::Community(v) = &reports[3] { @@ -709,22 +721,41 @@ mod tests { ReportCombinedViewInternal::get_report_count(pool, &data.admin_view, None).await?; assert_eq!(4, report_count_admin); + // Make sure the type_ filter is working + let reports_by_type = ReportCombinedQuery { + type_: Some(ReportType::Posts), + ..Default::default() + } + .list(pool, &data.admin_view) + .await?; + assert_length!(1, reports_by_type); + + // Filter by the post id + // Should be 2, for the post, and the comment on that post + let reports_by_post_id = ReportCombinedQuery { + post_id: Some(data.post.id), + ..Default::default() + } + .list(pool, &data.admin_view) + .await?; + assert_length!(2, reports_by_post_id); + // Timmy should only see 2 reports, since they're not an admin, // but they do mod the community - let reports = ReportCombinedQuery::default() + let timmys_reports = ReportCombinedQuery::default() .list(pool, &data.timmy_view) .await?; - assert_eq!(2, reports.len()); + assert_length!(2, timmys_reports); // Make sure the report types are correct - if let ReportCombinedView::Post(v) = &reports[1] { + if let ReportCombinedView::Post(v) = &timmys_reports[1] { 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] { + if let ReportCombinedView::Comment(v) = &timmys_reports[0] { 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); @@ -1050,6 +1081,16 @@ mod tests { ReportCombinedViewInternal::get_report_count(pool, &data.timmy_view, None).await?; assert_eq!(1, report_count_after_resolved); + // Filter by post id, which should still include the comments. + let reports_post_id_filter = ReportCombinedQuery { + post_id: Some(data.post.id), + ..Default::default() + } + .list(pool, &data.timmy_view) + .await?; + + assert_length!(2, reports_post_id_filter); + cleanup(data, pool).await?; Ok(())