From 8f1ff9e774e17601d3927493cf1cf6d0cf1eb51d Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Thu, 2 Mar 2023 12:54:55 +0000 Subject: [PATCH] feat(safety): add API route for editing report --- Cargo.lock | 8 +-- crates/bonfire/Cargo.toml | 2 +- crates/delta/Cargo.toml | 2 +- crates/delta/src/routes/safety/edit_report.rs | 56 +++++++++++++++++++ crates/delta/src/routes/safety/mod.rs | 2 + .../delta/src/routes/safety/report_content.rs | 3 +- crates/quark/Cargo.toml | 4 +- crates/quark/src/impl/dummy/safety/report.rs | 9 +++ crates/quark/src/impl/generic/mod.rs | 4 ++ .../quark/src/impl/generic/safety/report.rs | 9 +++ crates/quark/src/impl/mongo/mod.rs | 9 ++- crates/quark/src/impl/mongo/safety/report.rs | 11 ++++ crates/quark/src/models/safety/report.rs | 13 ++++- crates/quark/src/traits/safety/report.rs | 7 +++ crates/quark/src/util/ref.rs | 9 ++- 15 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 crates/delta/src/routes/safety/edit_report.rs create mode 100644 crates/quark/src/impl/generic/safety/report.rs diff --git a/Cargo.lock b/Cargo.lock index 4ecef152..98e3ca34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2170,7 +2170,7 @@ dependencies = [ [[package]] name = "optional_struct" version = "0.2.0" -source = "git+https://github.com/insertish/OptionalStruct?rev=e275d2726595474632485934aa0887fa52281f70#e275d2726595474632485934aa0887fa52281f70" +source = "git+https://github.com/insertish/OptionalStruct?rev=ee56427cee1f007839825d93d07fffd5a5e038c7#ee56427cee1f007839825d93d07fffd5a5e038c7" dependencies = [ "quote 0.3.15", "syn 0.11.11", @@ -2792,7 +2792,7 @@ dependencies = [ [[package]] name = "revolt-bonfire" -version = "0.5.10" +version = "0.5.11" dependencies = [ "async-std", "async-tungstenite", @@ -2808,7 +2808,7 @@ dependencies = [ [[package]] name = "revolt-delta" -version = "0.5.10" +version = "0.5.11" dependencies = [ "async-channel", "async-std", @@ -2848,7 +2848,7 @@ dependencies = [ [[package]] name = "revolt-quark" -version = "0.5.10" +version = "0.5.11" dependencies = [ "async-lock", "async-recursion", diff --git a/crates/bonfire/Cargo.toml b/crates/bonfire/Cargo.toml index a58fe638..3c576c61 100644 --- a/crates/bonfire/Cargo.toml +++ b/crates/bonfire/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-bonfire" -version = "0.5.10" +version = "0.5.11" license = "AGPL-3.0-or-later" edition = "2021" diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index 4b693339..654f2b5f 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-delta" -version = "0.5.10" +version = "0.5.11" license = "AGPL-3.0-or-later" authors = ["Paul Makles "] edition = "2018" diff --git a/crates/delta/src/routes/safety/edit_report.rs b/crates/delta/src/routes/safety/edit_report.rs new file mode 100644 index 00000000..eb7103d0 --- /dev/null +++ b/crates/delta/src/routes/safety/edit_report.rs @@ -0,0 +1,56 @@ +use revolt_quark::{ + models::{ + report::{PartialReport, ReportStatus}, + Report, User, + }, + Db, Error, Ref, Result, +}; +use rocket::serde::json::Json; +use serde::Deserialize; +use validator::Validate; + +/// # Report Data +#[derive(Validate, Deserialize, JsonSchema)] +pub struct DataEditReport { + /// New report status + status: Option, + /// Report notes + notes: Option, +} + +/// # Edit Report +/// +/// Edit a report. +#[openapi(tag = "User Safety")] +#[patch("/reports/", data = "")] +pub async fn edit_report( + db: &Db, + user: User, + report: Ref, + edit: Json, +) -> Result> { + // Must be privileged for this route + if !user.privileged { + return Err(Error::NotPrivileged); + } + + // Validate data + let edit = edit.into_inner(); + edit.validate() + .map_err(|error| Error::FailedValidation { error })?; + + // Create and apply update to report + let mut report = report.as_report(db).await?; + report + .update( + db, + PartialReport { + status: edit.status, + notes: edit.notes, + ..Default::default() + }, + ) + .await?; + + Ok(Json(report)) +} diff --git a/crates/delta/src/routes/safety/mod.rs b/crates/delta/src/routes/safety/mod.rs index 30214530..1ff4b516 100644 --- a/crates/delta/src/routes/safety/mod.rs +++ b/crates/delta/src/routes/safety/mod.rs @@ -1,6 +1,7 @@ use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; +mod edit_report; mod fetch_reports; mod report_content; @@ -11,6 +12,7 @@ pub fn routes() -> (Vec, OpenApi) { // Reports report_content::report_content, fetch_reports::fetch_reports, + edit_report::edit_report, // Snapshots fetch_snapshot::fetch_snapshot ] diff --git a/crates/delta/src/routes/safety/report_content.rs b/crates/delta/src/routes/safety/report_content.rs index 8b0da822..bdb03336 100644 --- a/crates/delta/src/routes/safety/report_content.rs +++ b/crates/delta/src/routes/safety/report_content.rs @@ -149,7 +149,8 @@ pub async fn report_content(db: &Db, user: User, data: Json) author_id: user.id, content: data.content, additional_context: data.additional_context, - status: ReportStatus::Created, + status: ReportStatus::Created {}, + notes: String::new(), }; db.insert_report(&report).await?; diff --git a/crates/quark/Cargo.toml b/crates/quark/Cargo.toml index 5772d436..b99c706e 100644 --- a/crates/quark/Cargo.toml +++ b/crates/quark/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "revolt-quark" -version = "0.5.10" +version = "0.5.11" license = "AGPL-3.0-or-later" edition = "2021" @@ -29,7 +29,7 @@ default = [ "test" ] serde = { version = "1", features = ["derive"] } validator = { version = "0.14", features = ["derive"] } iso8601-timestamp = { version = "0.1.8", features = ["schema", "bson"] } -optional_struct = { git = "https://github.com/insertish/OptionalStruct", rev = "e275d2726595474632485934aa0887fa52281f70" } +optional_struct = { git = "https://github.com/insertish/OptionalStruct", rev = "ee56427cee1f007839825d93d07fffd5a5e038c7" } # Formats bincode = "1.3.3" diff --git a/crates/quark/src/impl/dummy/safety/report.rs b/crates/quark/src/impl/dummy/safety/report.rs index c1f80886..3286f997 100644 --- a/crates/quark/src/impl/dummy/safety/report.rs +++ b/crates/quark/src/impl/dummy/safety/report.rs @@ -1,3 +1,4 @@ +use crate::models::report::PartialReport; use crate::models::Report; use crate::{AbstractReport, Result}; @@ -10,6 +11,14 @@ impl AbstractReport for DummyDb { Ok(()) } + async fn update_report(&self, _id: &str, _report: &PartialReport) -> Result<()> { + todo!() + } + + async fn fetch_report(&self, _report_id: &str) -> Result { + todo!() + } + async fn fetch_reports(&self) -> Result> { Ok(vec![]) } diff --git a/crates/quark/src/impl/generic/mod.rs b/crates/quark/src/impl/generic/mod.rs index 4c963b06..5dd5dff6 100644 --- a/crates/quark/src/impl/generic/mod.rs +++ b/crates/quark/src/impl/generic/mod.rs @@ -27,3 +27,7 @@ pub mod users { pub mod user; pub mod user_settings; } + +pub mod safety { + pub mod report; +} diff --git a/crates/quark/src/impl/generic/safety/report.rs b/crates/quark/src/impl/generic/safety/report.rs new file mode 100644 index 00000000..259d7701 --- /dev/null +++ b/crates/quark/src/impl/generic/safety/report.rs @@ -0,0 +1,9 @@ +use crate::{models::report::PartialReport, models::Report, Database, Result}; + +impl Report { + /// Update report data + pub async fn update(&mut self, db: &Database, partial: PartialReport) -> Result<()> { + self.apply_options(partial.clone()); + db.update_report(&self.id, &partial).await + } +} diff --git a/crates/quark/src/impl/mongo/mod.rs b/crates/quark/src/impl/mongo/mod.rs index d0639dbb..cc44bdcc 100644 --- a/crates/quark/src/impl/mongo/mod.rs +++ b/crates/quark/src/impl/mongo/mod.rs @@ -97,7 +97,14 @@ impl MongoDb { operation: "find", with: collection, })? - .filter_map(|s| async { s.ok() }) + .filter_map(|s| async { + if cfg!(debug_assertions) { + // Hard fail on invalid documents + Some(s.unwrap()) + } else { + s.ok() + } + }) .collect::>() .await) } diff --git a/crates/quark/src/impl/mongo/safety/report.rs b/crates/quark/src/impl/mongo/safety/report.rs index da0a84c4..69888e88 100644 --- a/crates/quark/src/impl/mongo/safety/report.rs +++ b/crates/quark/src/impl/mongo/safety/report.rs @@ -1,3 +1,4 @@ +use crate::models::report::PartialReport; use crate::models::Report; use crate::{AbstractReport, Result}; @@ -11,6 +12,16 @@ impl AbstractReport for MongoDb { self.insert_one(COL, report).await.map(|_| ()) } + async fn update_report(&self, id: &str, report: &PartialReport) -> Result<()> { + self.update_one_by_id(COL, id, report, vec![], None) + .await + .map(|_| ()) + } + + async fn fetch_report(&self, report_id: &str) -> Result { + self.find_one_by_id(COL, report_id).await + } + async fn fetch_reports(&self) -> Result> { self.find(COL, doc! {}).await } diff --git a/crates/quark/src/models/safety/report.rs b/crates/quark/src/models/safety/report.rs index 66098da0..f9ee2a02 100644 --- a/crates/quark/src/models/safety/report.rs +++ b/crates/quark/src/models/safety/report.rs @@ -76,17 +76,20 @@ pub enum ReportedContent { #[serde(tag = "status")] pub enum ReportStatus { /// Report is waiting for triage / action - Created, + Created {}, /// Report was rejected Rejected { rejection_reason: String }, /// Report was actioned and resolved - Resolved, + Resolved {}, } /// User-generated platform moderation report. -#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] +#[derive(Serialize, Deserialize, JsonSchema, Debug, OptionalStruct, Clone)] +#[optional_derive(Serialize, Deserialize, JsonSchema, Debug, Default, Clone)] +#[optional_name = "PartialReport"] +#[opt_skip_serializing_none] pub struct Report { /// Unique Id #[serde(rename = "_id")] @@ -98,6 +101,10 @@ pub struct Report { /// Additional report context pub additional_context: String, /// Status of the report + #[opt_passthrough] #[serde(flatten)] pub status: ReportStatus, + /// Additional notes included on the report + #[serde(default)] + pub notes: String, } diff --git a/crates/quark/src/traits/safety/report.rs b/crates/quark/src/traits/safety/report.rs index 0fa3766c..34752613 100644 --- a/crates/quark/src/traits/safety/report.rs +++ b/crates/quark/src/traits/safety/report.rs @@ -1,3 +1,4 @@ +use crate::models::report::PartialReport; use crate::models::Report; use crate::Result; @@ -6,6 +7,12 @@ pub trait AbstractReport: Sync + Send { /// Insert a new report into the database async fn insert_report(&self, report: &Report) -> Result<()>; + /// Update a given report with new information + async fn update_report(&self, id: &str, message: &PartialReport) -> Result<()>; + + /// Fetch report + async fn fetch_report(&self, report_id: &str) -> Result; + /// Fetch reports async fn fetch_reports(&self) -> Result>; } diff --git a/crates/quark/src/util/ref.rs b/crates/quark/src/util/ref.rs index d010b4c1..4d59bc06 100644 --- a/crates/quark/src/util/ref.rs +++ b/crates/quark/src/util/ref.rs @@ -4,7 +4,9 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::models::{Bot, Channel, Emoji, Invite, Member, Message, Server, ServerBan, User}; +use crate::models::{ + Bot, Channel, Emoji, Invite, Member, Message, Report, Server, ServerBan, User, +}; use crate::presence::presence_is_online; use crate::{Database, Error, Result}; @@ -78,6 +80,11 @@ impl Ref { pub async fn as_emoji(&self, db: &Database) -> Result { db.fetch_emoji(&self.id).await } + + /// Fetch report from Ref + pub async fn as_report(&self, db: &Database) -> Result { + db.fetch_report(&self.id).await + } } impl<'r> FromParam<'r> for Ref {