diff options
Diffstat (limited to 'rust/src/api_server.rs')
-rw-r--r-- | rust/src/api_server.rs | 775 |
1 files changed, 141 insertions, 634 deletions
diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 88fd7063..602fbfb7 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -17,11 +17,11 @@ use fatcat_api::models::*; use sha1::Sha1; use uuid::Uuid; use ConnectionPool; - -type DbConn = diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>; +use database_entity_crud::{EntityCrud, EditContext}; +use std::str::FromStr; macro_rules! entity_batch_handler { - ($post_handler:ident, $post_batch_handler:ident, $model:ident) => { + ($post_batch_handler:ident, $model:ident) => { pub fn $post_batch_handler( &self, entity_list: &[models::$model], @@ -29,8 +29,6 @@ macro_rules! entity_batch_handler { editgroup: Option<String>, conn: &DbConn, ) -> Result<Vec<EntityEdit>> { - let mut ret: Vec<EntityEdit> = vec![]; - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth // editgroup override logic based on parameters let eg_id: Option<Uuid> = match (editgroup, autoaccept) { (Some(eg_string), _) => Some(fcid2uuid(&eg_string)?), @@ -51,43 +49,17 @@ macro_rules! entity_batch_handler { // actual wrapped function call here ret.push(self.$post_handler(e, autoaccept, conn)?); } + let mut edit_context = make_edit_context(conn, eg_id)?; + edit_context.autoaccept = autoaccept; + let model_list: Vec<&models::$model> = entity_list.iter().map(|e| e).collect(); + let edits = $model::db_create_batch(conn, &edit_context, model_list.as_slice())?; if autoaccept { // if autoaccept, eg_id is always Some let _clr: ChangelogRow = diesel::insert_into(changelog::table) .values((changelog::editgroup_id.eq(eg_id.unwrap()),)) .get_result(conn)?; } - Ok(ret) - } - } -} - -macro_rules! entity_history_handler { - ($history_handler:ident, $edit_row_type:ident, $edit_table:ident) => { - pub fn $history_handler( - &self, - id: &Uuid, - limit: Option<i64>, - conn: &DbConn, - ) -> Result<Vec<EntityHistoryEntry>> { - let limit = limit.unwrap_or(50); - - let rows: Vec<(EditgroupRow, ChangelogRow, $edit_row_type)> = editgroup::table - .inner_join(changelog::table) - .inner_join($edit_table::table) - .filter($edit_table::ident_id.eq(id)) - .order(changelog::id.desc()) - .limit(limit) - .get_results(conn)?; - - let history: Vec<EntityHistoryEntry> = rows.into_iter() - .map(|(eg_row, cl_row, e_row)| EntityHistoryEntry { - edit: e_row.into_model().expect("edit row to model"), - editgroup: eg_row.into_model_partial(), - changelog_entry: cl_row.into_model(), - }) - .collect(); - Ok(history) + edits.into_iter().map(|e| e.into_model()).collect() } } } @@ -103,227 +75,23 @@ macro_rules! count_entity { }}; } -#[derive(Clone)] -pub struct Server { - pub db_pool: ConnectionPool, -} - -fn container_row2entity( - ident: Option<ContainerIdentRow>, - rev: ContainerRevRow, -) -> Result<ContainerEntity> { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - Ok(ContainerEntity { - issnl: rev.issnl, - wikidata_qid: rev.wikidata_qid, - publisher: rev.publisher, - name: rev.name, - abbrev: rev.abbrev, - coden: rev.coden, - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - extra: rev.extra_json, - editgroup_id: None, - }) -} - -fn creator_row2entity(ident: Option<CreatorIdentRow>, rev: CreatorRevRow) -> Result<CreatorEntity> { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - Ok(CreatorEntity { - display_name: rev.display_name, - given_name: rev.given_name, - surname: rev.surname, - orcid: rev.orcid, - wikidata_qid: rev.wikidata_qid, - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) -} - -fn file_row2entity( - ident: Option<FileIdentRow>, - rev: FileRevRow, - conn: &DbConn, -) -> Result<FileEntity> { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), +fn make_edit_context(conn: &DbConn, editgroup_id: Option<FatCatId>) -> Result<EditContext> { + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth + let editgroup_id = match editgroup_id { + None => FatCatId::from_uuid(&get_or_create_editgroup(editor_id, conn)?), + Some(param) => param, }; - - let releases: Vec<String> = file_release::table - .filter(file_release::file_rev.eq(rev.id)) - .get_results(conn)? - .into_iter() - .map(|r: FileReleaseRow| uuid2fcid(&r.target_release_ident_id)) - .collect(); - - let urls: Vec<FileEntityUrls> = file_rev_url::table - .filter(file_rev_url::file_rev.eq(rev.id)) - .get_results(conn)? - .into_iter() - .map(|r: FileRevUrlRow| FileEntityUrls { - rel: r.rel, - url: r.url, - }) - .collect(); - - Ok(FileEntity { - sha1: rev.sha1, - sha256: rev.sha256, - md5: rev.md5, - size: rev.size.map(|v| v as i64), - urls: Some(urls), - mimetype: rev.mimetype, - releases: Some(releases), - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, + Ok(EditContext { + editor_id: FatCatId::from_uuid(&editor_id), + editgroup_id: editgroup_id, + extra_json: None, + autoapprove: false, }) } -fn release_row2entity( - ident: Option<ReleaseIdentRow>, - rev: ReleaseRevRow, - conn: &DbConn, -) -> Result<ReleaseEntity> { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - - let refs: Vec<ReleaseRef> = release_ref::table - .filter(release_ref::release_rev.eq(rev.id)) - .order(release_ref::index_val.asc()) - .get_results(conn) - .expect("fetch release refs") - .into_iter() - .map(|r: ReleaseRefRow| ReleaseRef { - index: r.index_val, - key: r.key, - extra: r.extra_json, - container_title: r.container_title, - year: r.year, - title: r.title, - locator: r.locator, - target_release_id: r.target_release_ident_id.map(|v| uuid2fcid(&v)), - }) - .collect(); - - let contribs: Vec<ReleaseContrib> = release_contrib::table - .filter(release_contrib::release_rev.eq(rev.id)) - .order(( - release_contrib::role.asc(), - release_contrib::index_val.asc(), - )) - .get_results(conn) - .expect("fetch release refs") - .into_iter() - .map(|c: ReleaseContribRow| ReleaseContrib { - index: c.index_val, - raw_name: c.raw_name, - role: c.role, - extra: c.extra_json, - creator_id: c.creator_ident_id.map(|v| uuid2fcid(&v)), - creator: None, - }) - .collect(); - - let abstracts: Vec<ReleaseEntityAbstracts> = release_rev_abstract::table - .inner_join(abstracts::table) - .filter(release_rev_abstract::release_rev.eq(rev.id)) - .get_results(conn)? - .into_iter() - .map( - |r: (ReleaseRevAbstractRow, AbstractsRow)| ReleaseEntityAbstracts { - sha1: Some(r.0.abstract_sha1), - mimetype: r.0.mimetype, - lang: r.0.lang, - content: Some(r.1.content), - }, - ) - .collect(); - - Ok(ReleaseEntity { - title: rev.title, - release_type: rev.release_type, - release_status: rev.release_status, - release_date: rev.release_date - .map(|v| chrono::DateTime::from_utc(v.and_hms(0, 0, 0), chrono::Utc)), - doi: rev.doi, - pmid: rev.pmid, - pmcid: rev.pmcid, - isbn13: rev.isbn13, - core_id: rev.core_id, - wikidata_qid: rev.wikidata_qid, - volume: rev.volume, - issue: rev.issue, - pages: rev.pages, - files: None, - container: None, - container_id: rev.container_ident_id.map(|u| uuid2fcid(&u)), - publisher: rev.publisher, - language: rev.language, - work_id: Some(uuid2fcid(&rev.work_ident_id)), - refs: Some(refs), - contribs: Some(contribs), - abstracts: Some(abstracts), - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) -} - -fn work_row2entity(ident: Option<WorkIdentRow>, rev: WorkRevRow) -> Result<WorkEntity> { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - Ok(WorkEntity { - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) +#[derive(Clone)] +pub struct Server { + pub db_pool: ConnectionPool, } impl Server { @@ -333,13 +101,7 @@ impl Server { _expand: Option<String>, conn: &DbConn, ) -> Result<ContainerEntity> { - // TODO: handle Deletions - let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table - .find(id) - .inner_join(container_rev::table) - .first(conn)?; - - container_row2entity(Some(ident), rev) + ContainerEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn lookup_container_handler(&self, issnl: &str, conn: &DbConn) -> Result<ContainerEntity> { @@ -354,7 +116,7 @@ impl Server { .filter(container_ident::redirect_id.is_null()) .first(conn)?; - container_row2entity(Some(ident), rev) + ContainerEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_creator_handler( @@ -363,12 +125,8 @@ impl Server { _expand: Option<String>, conn: &DbConn, ) -> Result<CreatorEntity> { - let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table - .find(id) - .inner_join(creator_rev::table) - .first(conn)?; - creator_row2entity(Some(ident), rev) + CreatorEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn lookup_creator_handler(&self, orcid: &str, conn: &DbConn) -> Result<CreatorEntity> { @@ -383,7 +141,7 @@ impl Server { .filter(creator_ident::redirect_id.is_null()) .first(conn)?; - creator_row2entity(Some(ident), rev) + CreatorEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_creator_releases_handler( @@ -402,8 +160,9 @@ impl Server { .filter(release_ident::redirect_id.is_null()) .load(conn)?; + // TODO: from_rows, not from_row? rows.into_iter() - .map(|(rev, ident, _)| release_row2entity(Some(ident), rev, conn)) + .map(|(rev, ident, _)| ReleaseEntity::db_from_row(conn, rev, Some(ident))) .collect() } @@ -413,12 +172,7 @@ impl Server { _expand: Option<String>, conn: &DbConn, ) -> Result<FileEntity> { - let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table - .find(id) - .inner_join(file_rev::table) - .first(conn)?; - - file_row2entity(Some(ident), rev, conn) + FileEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn lookup_file_handler(&self, sha1: &str, conn: &DbConn) -> Result<FileEntity> { @@ -432,7 +186,7 @@ impl Server { .filter(file_ident::redirect_id.is_null()) .first(conn)?; - file_row2entity(Some(ident), rev, conn) + FileEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_release_handler( @@ -441,12 +195,8 @@ impl Server { expand: Option<String>, conn: &DbConn, ) -> Result<ReleaseEntity> { - let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table - .find(id) - .inner_join(release_rev::table) - .first(conn)?; - let mut release = release_row2entity(Some(ident), rev, conn)?; + let mut release = ReleaseEntity::db_get(conn, FatCatId::from_uuid(id))?; // For now, if there is any expand param we do them all if expand.is_some() { @@ -457,7 +207,6 @@ impl Server { Some(self.get_container_handler(&fcid2uuid(&cid)?, None, conn)?); } } - Ok(release) } @@ -473,22 +222,23 @@ impl Server { .filter(release_ident::redirect_id.is_null()) .first(conn)?; - release_row2entity(Some(ident), rev, conn) + ReleaseEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_release_files_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<FileEntity>> { - let id = fcid2uuid(&id)?; + + let ident = FatCatId::from_str(id)?; let rows: Vec<(FileRevRow, FileIdentRow, FileReleaseRow)> = file_rev::table .inner_join(file_ident::table) .inner_join(file_release::table) - .filter(file_release::target_release_ident_id.eq(&id)) + .filter(file_release::target_release_ident_id.eq(&ident.to_uuid())) .filter(file_ident::is_live.eq(true)) .filter(file_ident::redirect_id.is_null()) .load(conn)?; rows.into_iter() - .map(|(rev, ident, _)| file_row2entity(Some(ident), rev, conn)) + .map(|(rev, ident, _)| FileEntity::db_from_row(conn, rev, Some(ident))) .collect() } @@ -498,12 +248,7 @@ impl Server { _expand: Option<String>, conn: &DbConn, ) -> Result<WorkEntity> { - let (ident, rev): (WorkIdentRow, WorkRevRow) = work_ident::table - .find(id) - .inner_join(work_rev::table) - .first(conn)?; - - work_row2entity(Some(ident), rev) + WorkEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn get_work_releases_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<ReleaseEntity>> { @@ -517,379 +262,139 @@ impl Server { .load(conn)?; rows.into_iter() - .map(|(rev, ident)| release_row2entity(Some(ident), rev, conn)) + .map(|(rev, ident)| ReleaseEntity::db_from_row(conn, rev, Some(ident))) .collect() } pub fn create_container_handler( &self, entity: models::ContainerEntity, - autoaccept: bool, conn: &DbConn, ) -> Result<EntityEdit> { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id: Uuid = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, conn)?, - Some(param) => fcid2uuid(¶m)?, - }; - if let Some(ref extid) = entity.wikidata_qid { - check_wikidata_qid(extid)?; - } - if let Some(ref extid) = entity.issnl { - check_issn(extid)?; - } - - let edit: ContainerEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO container_rev (name, publisher, issnl, wikidata_qid, abbrev, coden, extra_json) - VALUES ($1, $2, $3, $4, $5, $6, $7) - RETURNING id ), - ident AS ( INSERT INTO container_ident (is_live, rev_id) - VALUES ($8, (SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO container_edit (editgroup_id, ident_id, rev_id) VALUES - ($9, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::<diesel::sql_types::Text, _>(entity.name) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.publisher) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.issnl) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.wikidata_qid) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.abbrev) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.coden) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) - .bind::<diesel::sql_types::Bool, _>(autoaccept) - .bind::<diesel::sql_types::Uuid, _>(editgroup_id) - .get_result(conn)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; + edit.into_model() + } + pub fn update_container_handler( + &self, + id: &Uuid, + entity: models::ContainerEntity, + conn: &DbConn, + ) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() + } + pub fn delete_container_handler(&self, id: &Uuid, editgroup_id: Option<Uuid>, conn: &DbConn) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = ContainerEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } pub fn create_creator_handler( &self, entity: models::CreatorEntity, - autoaccept: bool, conn: &DbConn, ) -> Result<EntityEdit> { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, conn).expect("current editgroup"), - Some(param) => fcid2uuid(¶m)?, - }; - if let Some(ref extid) = entity.orcid { - check_orcid(extid)?; - } - if let Some(ref extid) = entity.wikidata_qid { - check_wikidata_qid(extid)?; - } + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; + edit.into_model() - let edit: CreatorEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO creator_rev (display_name, given_name, surname, orcid, wikidata_qid, extra_json) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id ), - ident AS ( INSERT INTO creator_ident (is_live, rev_id) - VALUES ($7, (SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO creator_edit (editgroup_id, ident_id, rev_id) VALUES - ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::<diesel::sql_types::Text, _>(entity.display_name) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.given_name) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.surname) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.orcid) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.wikidata_qid) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) - .bind::<diesel::sql_types::Bool, _>(autoaccept) - .bind::<diesel::sql_types::Uuid, _>(editgroup_id) - .get_result(conn)?; + } + pub fn update_creator_handler( + &self, + id: &Uuid, + entity: models::CreatorEntity, + conn: &DbConn, + ) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() + } + pub fn delete_creator_handler(&self, id: &Uuid, editgroup_id: Option<Uuid>, conn: &DbConn) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = CreatorEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } pub fn create_file_handler( &self, entity: models::FileEntity, - autoaccept: bool, conn: &DbConn, ) -> Result<EntityEdit> { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, conn).expect("current editgroup"), - Some(param) => fcid2uuid(¶m)?, - }; - - let edit: FileEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO file_rev (size, sha1, sha256, md5, mimetype, extra_json) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id ), - ident AS ( INSERT INTO file_ident (is_live, rev_id) - VALUES ($7, (SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO file_edit (editgroup_id, ident_id, rev_id) VALUES - ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::<diesel::sql_types::Nullable<diesel::sql_types::Int8>, _>(entity.size) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.sha1) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.sha256) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.md5) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.mimetype) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) - .bind::<diesel::sql_types::Bool, _>(autoaccept) - .bind::<diesel::sql_types::Uuid, _>(editgroup_id) - .get_result(conn)?; - - let _releases: Option<Vec<FileReleaseRow>> = match entity.releases { - None => None, - Some(release_list) => { - if release_list.is_empty() { - Some(vec![]) - } else { - let release_rows: Vec<FileReleaseRow> = release_list - .iter() - .map(|r| FileReleaseRow { - file_rev: edit.rev_id.unwrap(), - target_release_ident_id: fcid2uuid(r) - .expect("invalid fatcat identifier"), - }) - .collect(); - let release_rows: Vec<FileReleaseRow> = insert_into(file_release::table) - .values(release_rows) - .get_results(conn) - .expect("error inserting file_releases"); - Some(release_rows) - } - } - }; - - let _urls: Option<Vec<FileRevUrlRow>> = match entity.urls { - None => None, - Some(url_list) => { - if url_list.is_empty() { - Some(vec![]) - } else { - let url_rows: Vec<FileRevUrlNewRow> = url_list - .into_iter() - .map(|u| FileRevUrlNewRow { - file_rev: edit.rev_id.unwrap(), - rel: u.rel, - url: u.url, - }) - .collect(); - let url_rows: Vec<FileRevUrlRow> = insert_into(file_rev_url::table) - .values(url_rows) - .get_results(conn) - .expect("error inserting file_rev_url"); - Some(url_rows) - } - } - }; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; + edit.into_model() + } + pub fn update_file_handler( + &self, + id: &Uuid, + entity: models::FileEntity, + conn: &DbConn, + ) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() + } + pub fn delete_file_handler(&self, id: &Uuid, editgroup_id: Option<Uuid>, conn: &DbConn) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = FileEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } pub fn create_release_handler( &self, entity: models::ReleaseEntity, - autoaccept: bool, conn: &DbConn, ) -> Result<EntityEdit> { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, conn).expect("current editgroup"), - Some(param) => fcid2uuid(¶m)?, - }; - if let Some(ref extid) = entity.doi { - check_doi(extid)?; - } - if let Some(ref extid) = entity.pmid { - check_pmid(extid)?; - } - if let Some(ref extid) = entity.pmcid { - check_pmcid(extid)?; - } - if let Some(ref extid) = entity.wikidata_qid { - check_wikidata_qid(extid)?; - } - - let work_id = match entity.work_id { - Some(work_id) => fcid2uuid(&work_id)?, - None => { - // If a work_id wasn't passed, create a new work under the current editgroup - let work_model = models::WorkEntity { - ident: None, - revision: None, - redirect: None, - state: None, - editgroup_id: Some(uuid2fcid(&editgroup_id)), - extra: None, - }; - let new_entity = self.create_work_handler(work_model, autoaccept, conn)?; - fcid2uuid(&new_entity.ident)? - } - }; - - let container_id: Option<Uuid> = match entity.container_id { - Some(id) => Some(fcid2uuid(&id)?), - None => None, - }; - - let edit: ReleaseEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO release_rev (title, release_type, release_status, release_date, doi, pmid, pmcid, wikidata_qid, isbn13, core_id, volume, issue, pages, work_ident_id, container_ident_id, publisher, language, extra_json) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) - RETURNING id ), - ident AS ( INSERT INTO release_ident (is_live, rev_id) - VALUES ($19, (SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO release_edit (editgroup_id, ident_id, rev_id) VALUES - ($20, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::<diesel::sql_types::Text, _>(entity.title) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.release_type) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.release_status) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Date>, _>( - entity.release_date.map(|v| v.naive_utc().date())) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.doi) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.pmid) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.pmcid) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.wikidata_qid) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.isbn13) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.core_id) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.volume) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.issue) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.pages) - .bind::<diesel::sql_types::Uuid, _>(work_id) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Uuid>, _>(container_id) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.publisher) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(entity.language) - .bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) - .bind::<diesel::sql_types::Bool, _>(autoaccept) - .bind::<diesel::sql_types::Uuid, _>(editgroup_id) - .get_result(conn)?; - - let _refs: Option<Vec<ReleaseRefRow>> = match entity.refs { - None => None, - Some(ref_list) => { - if ref_list.is_empty() { - Some(vec![]) - } else { - let ref_rows: Vec<ReleaseRefNewRow> = ref_list - .iter() - .map(|r| ReleaseRefNewRow { - release_rev: edit.rev_id.unwrap(), - target_release_ident_id: r.target_release_id - .clone() - .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), - index_val: r.index, - key: r.key.clone(), - container_title: r.container_title.clone(), - year: r.year, - title: r.title.clone(), - locator: r.locator.clone(), - extra_json: r.extra.clone(), - }) - .collect(); - let ref_rows: Vec<ReleaseRefRow> = insert_into(release_ref::table) - .values(ref_rows) - .get_results(conn) - .expect("error inserting release_refs"); - Some(ref_rows) - } - } - }; - - let _contribs: Option<Vec<ReleaseContribRow>> = match entity.contribs { - None => None, - Some(contrib_list) => { - if contrib_list.is_empty() { - Some(vec![]) - } else { - let contrib_rows: Vec<ReleaseContribNewRow> = contrib_list - .iter() - .map(|c| ReleaseContribNewRow { - release_rev: edit.rev_id.unwrap(), - creator_ident_id: c.creator_id - .clone() - .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), - raw_name: c.raw_name.clone(), - index_val: c.index, - role: c.role.clone(), - extra_json: c.extra.clone(), - }) - .collect(); - let contrib_rows: Vec<ReleaseContribRow> = insert_into(release_contrib::table) - .values(contrib_rows) - .get_results(conn) - .expect("error inserting release_contribs"); - Some(contrib_rows) - } - } - }; - - if let Some(abstract_list) = entity.abstracts { - // For rows that specify content, we need to insert the abstract if it doesn't exist - // already - let new_abstracts: Vec<AbstractsRow> = abstract_list - .iter() - .filter(|ea| ea.content.is_some()) - .map(|c| AbstractsRow { - sha1: Sha1::from(c.content.clone().unwrap()).hexdigest(), - content: c.content.clone().unwrap(), - }) - .collect(); - if !new_abstracts.is_empty() { - // Sort of an "upsert"; only inserts new abstract rows if they don't already exist - insert_into(abstracts::table) - .values(&new_abstracts) - .on_conflict(abstracts::sha1) - .do_nothing() - .execute(conn)?; - } - let release_abstract_rows: Vec<ReleaseRevAbstractNewRow> = abstract_list - .into_iter() - .map(|c| ReleaseRevAbstractNewRow { - release_rev: edit.rev_id.unwrap(), - abstract_sha1: match c.content { - Some(ref content) => Sha1::from(content).hexdigest(), - None => c.sha1.expect("either abstract_sha1 or content is required"), - }, - lang: c.lang, - mimetype: c.mimetype, - }) - .collect(); - insert_into(release_rev_abstract::table) - .values(release_abstract_rows) - .execute(conn)?; - } + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; + edit.into_model() + } + pub fn update_release_handler( + &self, + id: &Uuid, + entity: models::ReleaseEntity, + conn: &DbConn, + ) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() + } + pub fn delete_release_handler(&self, id: &Uuid, editgroup_id: Option<Uuid>, conn: &DbConn) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = ReleaseEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } pub fn create_work_handler( &self, entity: models::WorkEntity, - autoaccept: bool, conn: &DbConn, ) -> Result<EntityEdit> { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, conn).expect("current editgroup"), - Some(param) => fcid2uuid(¶m)?, - }; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; + edit.into_model() + } + + pub fn update_work_handler( + &self, + id: &Uuid, + entity: models::WorkEntity, + conn: &DbConn, + ) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() + } - let edit: WorkEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (extra_json) - VALUES ($1) - RETURNING id ), - ident AS ( INSERT INTO work_ident (is_live, rev_id) - VALUES ($2, (SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO work_edit (editgroup_id, ident_id, rev_id) VALUES - ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(entity.extra) - .bind::<diesel::sql_types::Bool, _>(autoaccept) - .bind::<diesel::sql_types::Uuid, _>(editgroup_id) - .get_result(conn)?; + pub fn delete_work_handler(&self, id: &Uuid, editgroup_id: Option<Uuid>, conn: &DbConn) -> Result<EntityEdit> { + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = WorkEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -910,8 +415,7 @@ impl Server { editgroup::description.eq(entity.description), editgroup::extra_json.eq(entity.extra), )) - .get_result(conn) - .expect("error creating edit group"); + .get_result(conn)?; Ok(Editgroup { id: Some(uuid2fcid(&row.id)), @@ -1124,30 +628,33 @@ impl Server { } entity_batch_handler!( - create_container_handler, create_container_batch_handler, ContainerEntity ); entity_batch_handler!( - create_creator_handler, create_creator_batch_handler, CreatorEntity ); - entity_batch_handler!(create_file_handler, create_file_batch_handler, FileEntity); + entity_batch_handler!(create_file_batch_handler, FileEntity); entity_batch_handler!( - create_release_handler, create_release_batch_handler, ReleaseEntity ); - entity_batch_handler!(create_work_handler, create_work_batch_handler, WorkEntity); + entity_batch_handler!(create_work_batch_handler, WorkEntity); - entity_history_handler!( - get_container_history_handler, - ContainerEditRow, - container_edit - ); - entity_history_handler!(get_creator_history_handler, CreatorEditRow, creator_edit); - entity_history_handler!(get_file_history_handler, FileEditRow, file_edit); - entity_history_handler!(get_release_history_handler, ReleaseEditRow, release_edit); - entity_history_handler!(get_work_history_handler, WorkEditRow, work_edit); + pub fn get_container_history_handler(&self, id: &Uuid, limit: Option<i64>, conn: &DbConn,) -> Result<Vec<EntityHistoryEntry>> { + ContainerEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_creator_history_handler(&self, id: &Uuid, limit: Option<i64>, conn: &DbConn,) -> Result<Vec<EntityHistoryEntry>> { + CreatorEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_file_history_handler(&self, id: &Uuid, limit: Option<i64>, conn: &DbConn,) -> Result<Vec<EntityHistoryEntry>> { + FileEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_release_history_handler(&self, id: &Uuid, limit: Option<i64>, conn: &DbConn,) -> Result<Vec<EntityHistoryEntry>> { + ReleaseEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_work_history_handler(&self, id: &Uuid, limit: Option<i64>, conn: &DbConn,) -> Result<Vec<EntityHistoryEntry>> { + WorkEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } } |