summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2019-01-11 11:59:02 -0800
committerBryan Newbold <bnewbold@robocracy.org>2019-01-11 11:59:02 -0800
commit9bb1b10d10cd2b1863c9e5f136621a845b0cb3c1 (patch)
tree1c013fe09934ba2666be354ed7a139c065ebd52e
parent22c85fa299438b64e65c422b677c0c97fc2b5ef6 (diff)
downloadfatcat-9bb1b10d10cd2b1863c9e5f136621a845b0cb3c1.tar.gz
fatcat-9bb1b10d10cd2b1863c9e5f136621a845b0cb3c1.zip
WIP on annotations and editgroup accessors
-rw-r--r--rust/src/database_models.rs28
-rw-r--r--rust/src/editing.rs2
-rw-r--r--rust/src/editing_crud.rs301
-rw-r--r--rust/src/entity_crud.rs6
-rw-r--r--rust/src/lib.rs2
5 files changed, 335 insertions, 4 deletions
diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs
index 4e83afdb..ef6eb65d 100644
--- a/rust/src/database_models.rs
+++ b/rust/src/database_models.rs
@@ -4,7 +4,7 @@ use crate::database_schema::*;
use crate::errors::*;
use crate::identifiers::uuid2fcid;
use chrono;
-use fatcat_api_spec::models::{ChangelogEntry, Editgroup, Editor, EntityEdit};
+use fatcat_api_spec::models::{ChangelogEntry, Editgroup, EditgroupAnnotation, Editor, EntityEdit};
use serde_json;
use uuid::Uuid;
@@ -556,8 +556,8 @@ pub struct EditgroupRow {
pub created: chrono::NaiveDateTime,
pub submitted: Option<chrono::NaiveDateTime>,
pub is_accepted: bool,
- pub extra_json: Option<serde_json::Value>,
pub description: Option<String>,
+ pub extra_json: Option<serde_json::Value>,
}
impl EditgroupRow {
@@ -578,6 +578,30 @@ impl EditgroupRow {
}
}
+#[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)]
+#[table_name = "editgroup_annotation"]
+pub struct EditgroupAnnotationRow {
+ pub id: Uuid,
+ pub editgroup_id: Uuid,
+ pub editor_id: Uuid,
+ pub created: chrono::NaiveDateTime,
+ pub comment_markdown: Option<String>,
+ pub extra_json: Option<serde_json::Value>,
+}
+
+impl EditgroupAnnotationRow {
+ pub fn into_model(self) -> EditgroupAnnotation {
+ EditgroupAnnotation {
+ annotation_id: Some(self.id.to_string()),
+ editgroup_id: Some(uuid2fcid(&self.editgroup_id)),
+ editor_id: Some(uuid2fcid(&self.editor_id)),
+ created: Some(chrono::DateTime::from_utc(self.created, chrono::Utc)),
+ comment_markdown: self.comment_markdown,
+ extra: self.extra_json,
+ }
+ }
+}
+
#[derive(Debug, Clone, Queryable, Identifiable, Associations, AsChangeset)]
#[table_name = "editor"]
pub struct EditorRow {
diff --git a/rust/src/editing.rs b/rust/src/editing.rs
index 2feff837..c2061ae9 100644
--- a/rust/src/editing.rs
+++ b/rust/src/editing.rs
@@ -7,7 +7,7 @@ use crate::database_models::*;
use crate::database_schema::*;
use crate::entity_crud::EntityCrud;
use crate::errors::{FatcatError, Result};
-use crate::identifiers::{check_username, FatcatId};
+use crate::identifiers::FatcatId;
use crate::server::DbConn;
use diesel;
use diesel::prelude::*;
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)
+ }
+}
diff --git a/rust/src/entity_crud.rs b/rust/src/entity_crud.rs
index 3d9b5165..e54973da 100644
--- a/rust/src/entity_crud.rs
+++ b/rust/src/entity_crud.rs
@@ -104,6 +104,7 @@ pub struct ExpandFlags {
pub container: bool,
pub releases: bool,
pub creators: bool,
+ pub editors: bool,
}
impl FromStr for ExpandFlags {
@@ -123,6 +124,7 @@ impl ExpandFlags {
container: list.contains(&"container"),
releases: list.contains(&"releases"),
creators: list.contains(&"creators"),
+ editors: list.contains(&"editors"),
}
}
pub fn none() -> ExpandFlags {
@@ -133,6 +135,7 @@ impl ExpandFlags {
container: false,
releases: false,
creators: false,
+ editors: false,
}
}
}
@@ -150,6 +153,7 @@ fn test_expand_flags() {
"other_thing",
"releases",
"creators",
+ "editors",
]);
assert!(
all == ExpandFlags {
@@ -159,6 +163,7 @@ fn test_expand_flags() {
container: true,
releases: true,
creators: true
+ editors: true
}
);
assert!(ExpandFlags::from_str("").unwrap().files == false);
@@ -176,6 +181,7 @@ fn test_expand_flags() {
container: true,
releases: true,
creators: true
+ editors: true
}
);
}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index b4e5d17a..b7661334 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -14,7 +14,7 @@ pub mod auth;
pub mod database_models;
pub mod database_schema; // only public for tests
pub mod editing;
-//pub mod editing_crud;
+pub mod editing_crud;
mod endpoint_handlers;
mod endpoints;
pub mod entity_crud;