diff options
Diffstat (limited to 'rust/src/editing_crud.rs')
-rw-r--r-- | rust/src/editing_crud.rs | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/rust/src/editing_crud.rs b/rust/src/editing_crud.rs new file mode 100644 index 00000000..c409e368 --- /dev/null +++ b/rust/src/editing_crud.rs @@ -0,0 +1,301 @@ +use crate::database_models::*; +use crate::database_schema::*; +use crate::entity_crud::ExpandFlags; +use crate::errors::*; +use crate::identifiers::{self, FatcatId}; +use crate::server::DbConn; +use diesel::prelude::*; +use diesel::{self, insert_into}; +use fatcat_api_spec::models::*; +use std::str::FromStr; +use uuid::Uuid; + +/* + * The object types with accessors defined here: + * + * - editor + * - editgroup + * - editgroup_annotation + * + * Generic verbs/actions look like: + * + * - db_get (single) + * - db_get_range (group; by timestamp) + * - db_create (single) + * - db_update (single) + * - db_expand (single) + * + * Annotations can be fetch with a join on either editgroup or editor, with same range parameters. + */ + +pub trait EditorCrud { + fn db_get(conn: &DbConn, editor_id: FatcatId) -> Result<EditorRow>; + fn db_create(&self, conn: &DbConn) -> Result<EditorRow>; + fn db_update_username(&self, conn: &DbConn, editor_id: FatcatId) -> Result<EditorRow>; +} +impl EditorCrud for Editor { + fn db_get(conn: &DbConn, editor_id: FatcatId) -> Result<EditorRow> { + let editor: EditorRow = editor::table.find(editor_id.to_uuid()).get_result(conn)?; + Ok(editor) + } + + fn db_create(&self, conn: &DbConn) -> Result<EditorRow> { + identifiers::check_username(&self.username)?; + let is_admin = self.is_admin.unwrap_or(false); + let is_bot = self.is_bot.unwrap_or(false); + let ed: EditorRow = diesel::insert_into(editor::table) + .values(( + editor::username.eq(&self.username), + editor::is_admin.eq(is_admin), + editor::is_bot.eq(is_bot), + )) + .get_result(conn)?; + Ok(ed) + } + + fn db_update_username(&self, conn: &DbConn, editor_id: FatcatId) -> Result<EditorRow> { + identifiers::check_username(&self.username)?; + diesel::update(editor::table.find(editor_id.to_uuid())) + .set(editor::username.eq(&self.username)) + .execute(conn)?; + let editor: EditorRow = editor::table.find(editor_id.to_uuid()).get_result(conn)?; + Ok(editor) + } +} + +pub trait EditgroupCrud { + fn db_get(conn: &DbConn, editgroup_id: FatcatId) -> Result<EditgroupRow>; + fn db_expand(&mut self, conn: &DbConn, expand: ExpandFlags) -> Result<()>; + fn db_get_range_for_editor( + conn: &DbConn, + editor_id: FatcatId, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupRow>>; + fn db_get_range_reviewable( + conn: &DbConn, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupRow>>; + fn db_create(&self, conn: &DbConn, autoaccept: bool) -> Result<EditgroupRow>; + fn db_update( + &self, + conn: &DbConn, + editgroup_id: FatcatId, + submit: Option<bool>, + ) -> Result<EditgroupRow>; +} + +impl EditgroupCrud for Editgroup { + // XXX: this should probably *alwas* retun changelog status as well. If we do that, can we get rid + // of the is_accepted thing? no, still want it as denormalized speed-up in some queries/filters. + + /// This method does *not* expand the 'edits'; currently that's still done in the endpoint + /// handler, but it probably should be done in this trait with a db_expand() + fn db_get(conn: &DbConn, editgroup_id: FatcatId) -> Result<EditgroupRow> { + let row: EditgroupRow = editgroup::table + .find(editgroup_id.to_uuid()) + .get_result(conn)?; + + // Note: for now this is really conservative and verifies the is_accepted flag every time + let count: i64 = changelog::table + .filter(changelog::editgroup_id.eq(editgroup_id.to_uuid())) + .count() + .get_result(conn)?; + ensure!( + (count > 0) == row.is_accepted, + "internal database consistency error on editgroup: {}", + editgroup_id + ); + Ok(row) + } + + fn db_expand(&mut self, conn: &DbConn, expand: ExpandFlags) -> Result<()> { + // XXX: impl + Ok(()) + } + + fn db_get_range_for_editor( + conn: &DbConn, + editor_id: FatcatId, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupRow>> { + // TODO: since/before + let rows: Vec<EditgroupRow> = match (since, before) { + _ => { + editgroup::table + .filter(editgroup::editor_id.eq(editor_id.to_uuid())) + // XXX: .filter(editgroup::created + .order_by(editgroup::created.desc()) + .limit(limit as i64) + .get_results(conn)? + } + }; + Ok(rows) + } + + fn db_get_range_reviewable( + conn: &DbConn, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupRow>> { + // TODO: since/before + let rows: Vec<EditgroupRow> = match (since, before) { + _ => editgroup::table + .filter(editgroup::is_accepted.eq(false)) + .filter(editgroup::submitted.is_not_null()) + .order_by(editgroup::created.desc()) + .limit(limit as i64) + .get_results(conn)?, + }; + Ok(rows) + } + + fn db_create(&self, conn: &DbConn, autoaccept: bool) -> Result<EditgroupRow> { + let editor_id = self + .editor_id + .clone() + .ok_or_else(|| FatcatError::BadRequest("missing editor_id".to_string()))?; + let editor_id = FatcatId::from_str(&editor_id)?; + let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) + .values(( + editgroup::editor_id.eq(editor_id.to_uuid()), + editgroup::is_accepted.eq(autoaccept), + editgroup::description.eq(&self.description), + editgroup::extra_json.eq(&self.extra), + )) + .get_result(conn)?; + Ok(eg_row) + } + + fn db_update( + &self, + conn: &DbConn, + editgroup_id: FatcatId, + submit: Option<bool>, + ) -> Result<EditgroupRow> { + let row = Self::db_get(conn, editgroup_id)?; + if row.is_accepted { + // "can't update an accepted editgroup" + Err(FatcatError::EditgroupAlreadyAccepted( + editgroup_id.to_string(), + ))?; + } + match submit { + Some(true) => { + // just a submit + let row = diesel::update(editgroup::table.find(editgroup_id.to_uuid())) + .set(editgroup::submitted.eq(diesel::dsl::now)) + .get_result(conn)?; + Ok(row) + } + Some(false) => { + // just a retraction + let submitted: Option<chrono::NaiveDateTime> = None; + let row = diesel::update(editgroup::table.find(editgroup_id.to_uuid())) + .set(editgroup::submitted.eq(submitted)) + .get_result(conn)?; + Ok(row) + } + None => { + // full-on row update... though we only do extra and description + let row = diesel::update(editgroup::table.find(editgroup_id.to_uuid())) + .set(( + editgroup::description.eq(&self.description), + editgroup::extra_json.eq(&self.extra), + )) + .get_result(conn)?; + Ok(row) + } + } + } +} + +pub trait EditgroupAnnotationCrud { + fn db_get(conn: &DbConn, annotation_id: Uuid) -> Result<EditgroupAnnotationRow>; + fn db_get_range_for_editor( + conn: &DbConn, + editor_id: FatcatId, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupAnnotationRow>>; + fn db_get_range_for_editgroup( + conn: &DbConn, + editgroup_id: FatcatId, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupAnnotationRow>>; + fn db_create(&self, conn: &DbConn) -> Result<EditgroupAnnotationRow>; +} + +impl EditgroupAnnotationCrud for EditgroupAnnotation { + fn db_get(conn: &DbConn, annotation_id: Uuid) -> Result<EditgroupAnnotationRow> { + let row: EditgroupAnnotationRow = editgroup_annotation::table + .find(annotation_id) + .get_result(conn)?; + Ok(row) + } + + fn db_get_range_for_editor( + conn: &DbConn, + editor_id: FatcatId, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupAnnotationRow>> { + // TODO: since/before + let rows: Vec<EditgroupAnnotationRow> = match (since, before) { + _ => editgroup_annotation::table + .filter(editgroup_annotation::editor_id.eq(editor_id.to_uuid())) + .order_by(editgroup_annotation::created.desc()) + .limit(limit as i64) + .get_results(conn)?, + }; + Ok(rows) + } + + fn db_get_range_for_editgroup( + conn: &DbConn, + editgroup_id: FatcatId, + limit: u64, + since: Option<()>, + before: Option<()>, + ) -> Result<Vec<EditgroupAnnotationRow>> { + // TODO: since/before + let rows: Vec<EditgroupAnnotationRow> = match (since, before) { + _ => editgroup_annotation::table + .filter(editgroup_annotation::editgroup_id.eq(editgroup_id.to_uuid())) + .order_by(editgroup_annotation::created.desc()) + .limit(limit as i64) + .get_results(conn)?, + }; + Ok(rows) + } + + fn db_create(&self, conn: &DbConn) -> Result<EditgroupAnnotationRow> { + let editor_id = self + .editor_id + .clone() + .ok_or_else(|| FatcatError::BadRequest("missing editor_id".to_string()))?; + let editor_id = FatcatId::from_str(&editor_id)?; + let editgroup_id = self + .editgroup_id + .clone() + .ok_or_else(|| FatcatError::BadRequest("missing editgroup_id".to_string()))?; + let editgroup_id = FatcatId::from_str(&editgroup_id)?; + let ed: EditgroupAnnotationRow = diesel::insert_into(editgroup_annotation::table) + .values(( + editgroup_annotation::editor_id.eq(editor_id.to_uuid()), + editgroup_annotation::editgroup_id.eq(editgroup_id.to_uuid()), + )) + .get_result(conn)?; + Ok(ed) + } +} |