diff options
-rw-r--r-- | rust/src/api_entity_crud.rs | 80 | ||||
-rw-r--r-- | rust/src/api_server.rs | 190 | ||||
-rw-r--r-- | rust/src/api_wrappers.rs | 282 | ||||
-rw-r--r-- | rust/src/lib.rs | 4 | ||||
-rw-r--r-- | rust/tests/test_old_python_tests.rs | 12 |
5 files changed, 493 insertions, 75 deletions
diff --git a/rust/src/api_entity_crud.rs b/rust/src/api_entity_crud.rs index 3a2760c2..814b42da 100644 --- a/rust/src/api_entity_crud.rs +++ b/rust/src/api_entity_crud.rs @@ -23,6 +23,9 @@ use uuid::Uuid; * db_update * db_delete * db_get_history + * db_get_edit + * db_delete_edit + * db_get_redirects * * For now, these will probably be macros, until we can level up our trait/generics foo. */ @@ -66,6 +69,9 @@ where ident: FatCatId, limit: Option<i64>, ) -> Result<Vec<EntityHistoryEntry>>; + fn db_get_edit(conn: &DbConn, edit_id: i64) -> Result<Self::EditRow>; + fn db_delete_edit(conn: &DbConn, edit_id: i64) -> Result<()>; + fn db_get_redirects(conn: &DbConn, ident: FatCatId) -> Result<Vec<FatCatId>>; fn db_accept_edits(conn: &DbConn, editgroup_id: FatCatId) -> Result<u64>; // Entity-specific Methods @@ -260,6 +266,47 @@ macro_rules! generic_db_get_history { }; } +macro_rules! generic_db_get_edit { + ($edit_table:ident) => { + fn db_get_edit(conn: &DbConn, edit_id: i64) -> Result<Self::EditRow> { + Ok($edit_table::table.find(edit_id).first(conn)?) + } + }; +} + +macro_rules! generic_db_delete_edit { + ($edit_table:ident) => { + /// This method assumes the connection is already in a transaction + fn db_delete_edit(conn: &DbConn, edit_id: i64) -> Result<()> { + // ensure that edit hasn't been accepted + let accepted_rows: Vec<(EditgroupRow, ChangelogRow, Self::EditRow)> = editgroup::table + .inner_join(changelog::table) + .inner_join($edit_table::table) + .filter($edit_table::id.eq(edit_id)) + .limit(1) + .get_results(conn)?; + if accepted_rows.len() != 0 { + // TODO: should be a 4xx, not a 5xx + bail!("attempted to delete an already accepted edit") + } + diesel::delete($edit_table::table.filter($edit_table::id.eq(edit_id))).execute(conn)?; + Ok(()) + } + }; +} + +macro_rules! generic_db_get_redirects { + ($ident_table:ident) => { + fn db_get_redirects(conn: &DbConn, ident: FatCatId) -> Result<Vec<FatCatId>> { + let res: Vec<Uuid> = $ident_table::table + .select($ident_table::id) + .filter($ident_table::redirect_id.eq(ident.to_uuid())) + .get_results(conn)?; + Ok(res.iter().map(|u| FatCatId::from_uuid(u)).collect()) + } + }; +} + /* // This would be the clean and efficient way, but see: // https://github.com/diesel-rs/diesel/issues/1478 @@ -382,6 +429,9 @@ impl EntityCrud for ContainerEntity { generic_db_update!(container_ident, container_edit); generic_db_delete!(container_ident, container_edit); generic_db_get_history!(container_edit); + generic_db_get_edit!(container_edit); + generic_db_delete_edit!(container_edit); + generic_db_get_redirects!(container_ident); generic_db_accept_edits_batch!("container"); generic_db_insert_rev!(); @@ -404,7 +454,7 @@ impl EntityCrud for ContainerEntity { issnl: rev_row.issnl, wikidata_qid: rev_row.wikidata_qid, publisher: rev_row.publisher, - name: rev_row.name, + name: Some(rev_row.name), abbrev: rev_row.abbrev, coden: rev_row.coden, state: state, @@ -432,7 +482,7 @@ impl EntityCrud for ContainerEntity { models .iter() .map(|model| ContainerRevNewRow { - name: model.name.clone(), + name: model.name.clone().unwrap(), // XXX: unwrap publisher: model.publisher.clone(), issnl: model.issnl.clone(), wikidata_qid: model.wikidata_qid.clone(), @@ -461,6 +511,9 @@ impl EntityCrud for CreatorEntity { generic_db_update!(creator_ident, creator_edit); generic_db_delete!(creator_ident, creator_edit); generic_db_get_history!(creator_edit); + generic_db_get_edit!(creator_edit); + generic_db_delete_edit!(creator_edit); + generic_db_get_redirects!(creator_ident); generic_db_accept_edits_batch!("creator"); generic_db_insert_rev!(); @@ -479,7 +532,7 @@ impl EntityCrud for CreatorEntity { None => (None, None, None), }; Ok(CreatorEntity { - display_name: rev_row.display_name, + display_name: Some(rev_row.display_name), given_name: rev_row.given_name, surname: rev_row.surname, orcid: rev_row.orcid, @@ -509,7 +562,7 @@ impl EntityCrud for CreatorEntity { models .iter() .map(|model| CreatorRevNewRow { - display_name: model.display_name.clone(), + display_name: model.display_name.clone().unwrap(), // XXX: unwrap given_name: model.given_name.clone(), surname: model.surname.clone(), orcid: model.orcid.clone(), @@ -537,6 +590,9 @@ impl EntityCrud for FileEntity { generic_db_update!(file_ident, file_edit); generic_db_delete!(file_ident, file_edit); generic_db_get_history!(file_edit); + generic_db_get_edit!(file_edit); + generic_db_delete_edit!(file_edit); + generic_db_get_redirects!(file_ident); generic_db_accept_edits_batch!("file"); generic_db_insert_rev!(); @@ -667,6 +723,9 @@ impl EntityCrud for ReleaseEntity { generic_db_update!(release_ident, release_edit); generic_db_delete!(release_ident, release_edit); generic_db_get_history!(release_edit); + generic_db_get_edit!(release_edit); + generic_db_delete_edit!(release_edit); + generic_db_get_redirects!(release_ident); generic_db_accept_edits_batch!("release"); generic_db_insert_rev!(); @@ -680,7 +739,11 @@ impl EntityCrud for ReleaseEntity { } if expand.container { if let Some(ref cid) = self.container_id { - self.container = Some(ContainerEntity::db_get(conn, FatCatId::from_str(&cid)?, HideFlags::none())?); + self.container = Some(ContainerEntity::db_get( + conn, + FatCatId::from_str(&cid)?, + HideFlags::none(), + )?); } } Ok(()) @@ -847,7 +910,7 @@ impl EntityCrud for ReleaseEntity { }; Ok(ReleaseEntity { - title: rev_row.title, + title: Some(rev_row.title), release_type: rev_row.release_type, release_status: rev_row.release_status, release_date: rev_row.release_date, @@ -913,7 +976,7 @@ impl EntityCrud for ReleaseEntity { .iter() .map(|model| { Ok(ReleaseRevNewRow { - title: model.title.clone(), + title: model.title.clone().unwrap(), // XXX: unwrap() release_type: model.release_type.clone(), release_status: model.release_status.clone(), release_date: model.release_date, @@ -1071,6 +1134,9 @@ impl EntityCrud for WorkEntity { generic_db_update!(work_ident, work_edit); generic_db_delete!(work_ident, work_edit); generic_db_get_history!(work_edit); + generic_db_get_edit!(work_edit); + generic_db_delete_edit!(work_edit); + generic_db_get_redirects!(work_ident); generic_db_accept_edits_batch!("work"); generic_db_insert_rev!(); diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 2bcd4a7f..0a415e91 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -63,32 +63,75 @@ pub fn get_release_files(id: FatCatId, hide: HideFlags, conn: &DbConn) -> Result .load(conn)?; rows.into_iter() - .map( - |(rev, ident, _)| FileEntity::db_from_row(conn, rev, Some(ident), hide), - ).collect() + .map(|(rev, ident, _)| FileEntity::db_from_row(conn, rev, Some(ident), hide)) + .collect() } impl Server { - pub fn lookup_container_handler(&self, issnl: &str, hide: HideFlags, conn: &DbConn) -> Result<ContainerEntity> { - check_issn(issnl)?; - let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table - .inner_join(container_rev::table) - .filter(container_rev::issnl.eq(issnl)) - .filter(container_ident::is_live.eq(true)) - .filter(container_ident::redirect_id.is_null()) - .first(conn)?; + pub fn lookup_container_handler( + &self, + issnl: &Option<String>, + wikidata_qid: &Option<String>, + hide: HideFlags, + conn: &DbConn, + ) -> Result<ContainerEntity> { + let (ident, rev): (ContainerIdentRow, ContainerRevRow) = match (issnl, wikidata_qid) { + (Some(issnl), None) => { + check_issn(issnl)?; + container_ident::table + .inner_join(container_rev::table) + .filter(container_rev::issnl.eq(&issnl)) + .filter(container_ident::is_live.eq(true)) + .filter(container_ident::redirect_id.is_null()) + .first(conn)? + } + (None, Some(wikidata_qid)) => { + check_issn(wikidata_qid)?; + container_ident::table + .inner_join(container_rev::table) + .filter(container_rev::wikidata_qid.eq(&wikidata_qid)) + .filter(container_ident::is_live.eq(true)) + .filter(container_ident::redirect_id.is_null()) + .first(conn)? + } + _ => { + return Err(ErrorKind::MissingOrMultipleExternalId("in lookup".to_string()).into()); + } + }; ContainerEntity::db_from_row(conn, rev, Some(ident), hide) } - pub fn lookup_creator_handler(&self, orcid: &str, hide: HideFlags, conn: &DbConn) -> Result<CreatorEntity> { - check_orcid(orcid)?; - let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table - .inner_join(creator_rev::table) - .filter(creator_rev::orcid.eq(orcid)) - .filter(creator_ident::is_live.eq(true)) - .filter(creator_ident::redirect_id.is_null()) - .first(conn)?; + pub fn lookup_creator_handler( + &self, + orcid: &Option<String>, + wikidata_qid: &Option<String>, + hide: HideFlags, + conn: &DbConn, + ) -> Result<CreatorEntity> { + let (ident, rev): (CreatorIdentRow, CreatorRevRow) = match (orcid, wikidata_qid) { + (Some(orcid), None) => { + check_orcid(orcid)?; + creator_ident::table + .inner_join(creator_rev::table) + .filter(creator_rev::orcid.eq(orcid)) + .filter(creator_ident::is_live.eq(true)) + .filter(creator_ident::redirect_id.is_null()) + .first(conn)? + } + (None, Some(wikidata_qid)) => { + check_wikidata_qid(wikidata_qid)?; + creator_ident::table + .inner_join(creator_rev::table) + .filter(creator_rev::wikidata_qid.eq(wikidata_qid)) + .filter(creator_ident::is_live.eq(true)) + .filter(creator_ident::redirect_id.is_null()) + .first(conn)? + } + _ => { + return Err(ErrorKind::MissingOrMultipleExternalId("in lookup".to_string()).into()); + } + }; CreatorEntity::db_from_row(conn, rev, Some(ident), hide) } @@ -114,30 +157,104 @@ impl Server { .collect() } - pub fn lookup_file_handler(&self, sha1: &str, hide: HideFlags, conn: &DbConn) -> Result<FileEntity> { - let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table - .inner_join(file_rev::table) - .filter(file_rev::sha1.eq(sha1)) - .filter(file_ident::is_live.eq(true)) - .filter(file_ident::redirect_id.is_null()) - .first(conn)?; + pub fn lookup_file_handler( + &self, + md5: &Option<String>, + sha1: &Option<String>, + sha256: &Option<String>, + hide: HideFlags, + conn: &DbConn, + ) -> Result<FileEntity> { + let (ident, rev): (FileIdentRow, FileRevRow) = match (md5, sha1, sha256) { + (Some(md5), None, None) => file_ident::table + .inner_join(file_rev::table) + .filter(file_rev::md5.eq(md5)) + .filter(file_ident::is_live.eq(true)) + .filter(file_ident::redirect_id.is_null()) + .first(conn)?, + (None, Some(sha1), None) => file_ident::table + .inner_join(file_rev::table) + .filter(file_rev::sha1.eq(sha1)) + .filter(file_ident::is_live.eq(true)) + .filter(file_ident::redirect_id.is_null()) + .first(conn)?, + (None, None, Some(sha256)) => file_ident::table + .inner_join(file_rev::table) + .filter(file_rev::sha256.eq(sha256)) + .filter(file_ident::is_live.eq(true)) + .filter(file_ident::redirect_id.is_null()) + .first(conn)?, + _ => { + return Err(ErrorKind::MissingOrMultipleExternalId("in lookup".to_string()).into()); + } + }; FileEntity::db_from_row(conn, rev, Some(ident), hide) } pub fn lookup_release_handler( &self, - doi: &str, + doi: &Option<String>, + wikidata_qid: &Option<String>, + isbn13: &Option<String>, + pmid: &Option<String>, + pmcid: &Option<String>, hide: HideFlags, conn: &DbConn, ) -> Result<ReleaseEntity> { - check_doi(doi)?; - let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table - .inner_join(release_rev::table) - .filter(release_rev::doi.eq(doi)) - .filter(release_ident::is_live.eq(true)) - .filter(release_ident::redirect_id.is_null()) - .first(conn)?; + let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = + match (doi, wikidata_qid, isbn13, pmid, pmcid) { + (Some(doi), None, None, None, None) => { + check_doi(doi)?; + release_ident::table + .inner_join(release_rev::table) + .filter(release_rev::doi.eq(doi)) + .filter(release_ident::is_live.eq(true)) + .filter(release_ident::redirect_id.is_null()) + .first(conn)? + } + (None, Some(wikidata_qid), None, None, None) => { + check_wikidata_qid(wikidata_qid)?; + release_ident::table + .inner_join(release_rev::table) + .filter(release_rev::wikidata_qid.eq(wikidata_qid)) + .filter(release_ident::is_live.eq(true)) + .filter(release_ident::redirect_id.is_null()) + .first(conn)? + } + (None, None, Some(isbn13), None, None) => { + // TODO: check_isbn13(isbn13)?; + release_ident::table + .inner_join(release_rev::table) + .filter(release_rev::isbn13.eq(isbn13)) + .filter(release_ident::is_live.eq(true)) + .filter(release_ident::redirect_id.is_null()) + .first(conn)? + } + (None, None, None, Some(pmid), None) => { + check_pmid(pmid)?; + release_ident::table + .inner_join(release_rev::table) + .filter(release_rev::pmid.eq(pmid)) + .filter(release_ident::is_live.eq(true)) + .filter(release_ident::redirect_id.is_null()) + .first(conn)? + } + (None, None, None, None, Some(pmcid)) => { + check_pmcid(pmcid)?; + release_ident::table + .inner_join(release_rev::table) + .filter(release_rev::pmcid.eq(pmcid)) + .filter(release_ident::is_live.eq(true)) + .filter(release_ident::redirect_id.is_null()) + .first(conn)? + } + _ => { + return Err( + ErrorKind::MissingOrMultipleExternalId("in lookup".to_string()).into(), + ); + } + }; ReleaseEntity::db_from_row(conn, rev, Some(ident), hide) } @@ -165,9 +282,8 @@ impl Server { .load(conn)?; rows.into_iter() - .map(|(rev, ident)| { - ReleaseEntity::db_from_row(conn, rev, Some(ident), hide) - }).collect() + .map(|(rev, ident)| ReleaseEntity::db_from_row(conn, rev, Some(ident), hide)) + .collect() } pub fn accept_editgroup_handler(&self, id: FatCatId, conn: &DbConn) -> Result<()> { diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index e5176ac6..8c274fb5 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -11,6 +11,7 @@ use fatcat_api_spec::models::*; use fatcat_api_spec::*; use futures::{self, Future}; use std::str::FromStr; +use uuid::Uuid; /// Helper for generating wrappers (which return "Box::new(futures::done(Ok(BLAH)))" like the /// codegen fatcat-api-spec code wants) that call through to actual helpers (which have simple @@ -20,11 +21,12 @@ macro_rules! wrap_entity_handlers { // stable doesn't have a mechanism to "concat" or generate new identifiers in macros, at least // in the context of defining new functions. // The only stable approach I know of would be: https://github.com/dtolnay/mashup - ($get_fn:ident, $get_resp:ident, $post_fn:ident, - $post_resp:ident, $post_batch_fn:ident, $post_batch_handler:ident, - $post_batch_resp:ident, $update_fn:ident, $update_resp:ident, - $delete_fn:ident, $delete_resp:ident, $get_history_fn:ident, - $get_history_resp:ident, $model:ident) => { + ($get_fn:ident, $get_resp:ident, $post_fn:ident, $post_resp:ident, $post_batch_fn:ident, + $post_batch_handler:ident, $post_batch_resp:ident, $update_fn:ident, $update_resp:ident, + $delete_fn:ident, $delete_resp:ident, $get_history_fn:ident, $get_history_resp:ident, + $get_edit_fn:ident, $get_edit_resp:ident, $delete_edit_fn:ident, $delete_edit_resp:ident, + $get_rev_fn:ident, $get_rev_resp:ident, $get_redirects_fn:ident, $get_redirects_resp:ident, + $model:ident) => { fn $get_fn( &self, @@ -244,14 +246,131 @@ macro_rules! wrap_entity_handlers { }; Box::new(futures::done(Ok(ret))) } + + fn $get_rev_fn( + &self, + id: String, + expand: Option<String>, + hide: Option<String>, + _context: &Context, + ) -> Box<Future<Item = $get_rev_resp, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + // No transaction for GET + let ret = match conn.transaction(|| { + let rev_id = Uuid::from_str(&id)?; + let hide_flags = match hide { + None => HideFlags::none(), + Some(param) => HideFlags::from_str(¶m)?, + }; + match expand { + None => $model::db_get_rev(&conn, rev_id, hide_flags), + Some(param) => { + let expand_flags = ExpandFlags::from_str(¶m)?; + let mut entity = $model::db_get_rev(&conn, rev_id, hide_flags)?; + entity.db_expand(&conn, expand_flags)?; + Ok(entity) + }, + } + }) { + Ok(entity) => + $get_rev_resp::FoundEntityRevision(entity), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $get_rev_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }), + Err(Error(ErrorKind::Uuid(e), _)) => + $get_rev_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $get_rev_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => { + error!("{}", e); + $get_rev_resp::GenericError(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + + fn $get_edit_fn( + &self, + edit_id: i64, + _context: &Context, + ) -> Box<Future<Item = $get_edit_resp, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + // No transaction for GET + let ret = match conn.transaction(|| { + $model::db_get_edit(&conn, edit_id)?.into_model() + }) { + Ok(edit) => + $get_edit_resp::FoundEdit(edit), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $get_edit_resp::NotFound(ErrorResponse { message: format!("No such {} entity edit: {}", stringify!($model), edit_id) }), + Err(e) => { + error!("{}", e); + $get_edit_resp::GenericError(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + + fn $delete_edit_fn( + &self, + edit_id: i64, + _context: &Context, + ) -> Box<Future<Item = $delete_edit_resp, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + let ret = match conn.transaction(|| { + $model::db_delete_edit(&conn, edit_id) + }) { + Ok(()) => + $delete_edit_resp::DeletedEdit(Success { message: format!("Successfully deleted work-in-progress {} edit: {}", stringify!($model), edit_id) } ), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $delete_edit_resp::NotFound(ErrorResponse { message: format!("No such {} edit: {}", stringify!($model), edit_id) }), + Err(Error(ErrorKind::Diesel(e), _)) => + $delete_edit_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => { + error!("{}", e); + $delete_edit_resp::GenericError(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + + fn $get_redirects_fn( + &self, + id: String, + _context: &Context, + ) -> Box<Future<Item = $get_redirects_resp, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + // No transaction for GET + let ret = match conn.transaction(|| { + let entity_id = FatCatId::from_str(&id)?; + let redirects: Vec<FatCatId> = $model::db_get_redirects(&conn, entity_id)?; + Ok(redirects.into_iter().map(|fcid| fcid.to_string()).collect()) + }) { + Ok(redirects) => + $get_redirects_resp::FoundEntityRedirects(redirects), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $get_redirects_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }), + Err(Error(ErrorKind::Uuid(e), _)) => + $get_redirects_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $get_redirects_resp::BadRequest(ErrorResponse { + message: ErrorKind::InvalidFatcatId(e).to_string() }), + Err(e) => { + error!("{}", e); + $get_redirects_resp::GenericError(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + } } macro_rules! wrap_lookup_handler { - ($get_fn:ident, $get_handler:ident, $get_resp:ident, $idname:ident, $idtype:ident) => { + ($get_fn:ident, $get_handler:ident, $get_resp:ident, $idname:ident) => { fn $get_fn( &self, - $idname: $idtype, + $idname: Option<String>, + wikidata_qid: Option<String>, hide: Option<String>, _context: &Context, ) -> Box<Future<Item = $get_resp, Error = ApiError> + Send> { @@ -261,11 +380,11 @@ macro_rules! wrap_lookup_handler { Some(param) => HideFlags::from_str(¶m).unwrap(), }; // No transaction for GET - let ret = match self.$get_handler(&$idname, hide_flags, &conn) { + let ret = match self.$get_handler(&$idname, &wikidata_qid, hide_flags, &conn) { Ok(entity) => $get_resp::FoundEntity(entity), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => - $get_resp::NotFound(ErrorResponse { message: format!("Not found: {}", $idname) }), + $get_resp::NotFound(ErrorResponse { message: format!("Not found: {:?} / {:?}", $idname, wikidata_qid) }), Err(Error(ErrorKind::MalformedExternalId(e), _)) => $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), Err(e) => { @@ -360,6 +479,14 @@ impl Api for Server { DeleteContainerResponse, get_container_history, GetContainerHistoryResponse, + get_container_edit, + GetContainerEditResponse, + delete_container_edit, + DeleteContainerEditResponse, + get_container_revision, + GetContainerRevisionResponse, + get_container_redirects, + GetContainerRedirectsResponse, ContainerEntity ); @@ -377,6 +504,14 @@ impl Api for Server { DeleteCreatorResponse, get_creator_history, GetCreatorHistoryResponse, + get_creator_edit, + GetCreatorEditResponse, + delete_creator_edit, + DeleteCreatorEditResponse, + get_creator_revision, + GetCreatorRevisionResponse, + get_creator_redirects, + GetCreatorRedirectsResponse, CreatorEntity ); wrap_entity_handlers!( @@ -393,6 +528,14 @@ impl Api for Server { DeleteFileResponse, get_file_history, GetFileHistoryResponse, + get_file_edit, + GetFileEditResponse, + delete_file_edit, + DeleteFileEditResponse, + get_file_revision, + GetFileRevisionResponse, + get_file_redirects, + GetFileRedirectsResponse, FileEntity ); wrap_entity_handlers!( @@ -409,6 +552,14 @@ impl Api for Server { DeleteReleaseResponse, get_release_history, GetReleaseHistoryResponse, + get_release_edit, + GetReleaseEditResponse, + delete_release_edit, + DeleteReleaseEditResponse, + get_release_revision, + GetReleaseRevisionResponse, + get_release_redirects, + GetReleaseRedirectsResponse, ReleaseEntity ); wrap_entity_handlers!( @@ -425,6 +576,14 @@ impl Api for Server { DeleteWorkResponse, get_work_history, GetWorkHistoryResponse, + get_work_edit, + GetWorkEditResponse, + delete_work_edit, + DeleteWorkEditResponse, + get_work_revision, + GetWorkRevisionResponse, + get_work_redirects, + GetWorkRedirectsResponse, WorkEntity ); @@ -432,29 +591,13 @@ impl Api for Server { lookup_container, lookup_container_handler, LookupContainerResponse, - issnl, - String + issnl ); wrap_lookup_handler!( lookup_creator, lookup_creator_handler, LookupCreatorResponse, - orcid, - String - ); - wrap_lookup_handler!( - lookup_file, - lookup_file_handler, - LookupFileResponse, - sha1, - String - ); - wrap_lookup_handler!( - lookup_release, - lookup_release_handler, - LookupReleaseResponse, - doi, - String + orcid ); wrap_fcid_hide_handler!( @@ -479,6 +622,91 @@ impl Api for Server { GetEditorChangelogResponse ); + fn lookup_file( + &self, + md5: Option<String>, + sha1: Option<String>, + sha256: Option<String>, + hide: Option<String>, + _context: &Context, + ) -> Box<Future<Item = LookupFileResponse, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + let hide_flags = match hide { + None => HideFlags::none(), + Some(param) => HideFlags::from_str(¶m).unwrap(), + }; + // No transaction for GET + let ret = match self.lookup_file_handler(&md5, &sha1, &sha256, hide_flags, &conn) { + Ok(entity) => LookupFileResponse::FoundEntity(entity), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => { + LookupFileResponse::NotFound(ErrorResponse { + message: format!("Not found: {:?} / {:?} / {:?}", md5, sha1, sha256), + }) + } + Err(Error(ErrorKind::MalformedExternalId(e), _)) => { + LookupFileResponse::BadRequest(ErrorResponse { + message: e.to_string(), + }) + } + Err(e) => { + error!("{}", e); + LookupFileResponse::BadRequest(ErrorResponse { + message: e.to_string(), + }) + } + }; + Box::new(futures::done(Ok(ret))) + } + + fn lookup_release( + &self, + doi: Option<String>, + wikidata_qid: Option<String>, + isbn13: Option<String>, + pmid: Option<String>, + pmcid: Option<String>, + hide: Option<String>, + _context: &Context, + ) -> Box<Future<Item = LookupReleaseResponse, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + let hide_flags = match hide { + None => HideFlags::none(), + Some(param) => HideFlags::from_str(¶m).unwrap(), + }; + // No transaction for GET + let ret = match self.lookup_release_handler( + &doi, + &wikidata_qid, + &isbn13, + &pmid, + &pmcid, + hide_flags, + &conn, + ) { + Ok(entity) => LookupReleaseResponse::FoundEntity(entity), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => { + LookupReleaseResponse::NotFound(ErrorResponse { + message: format!( + "Not found: {:?} / {:?} / {:?} / {:?} / {:?}", + doi, wikidata_qid, isbn13, pmid, pmcid + ), + }) + } + Err(Error(ErrorKind::MalformedExternalId(e), _)) => { + LookupReleaseResponse::BadRequest(ErrorResponse { + message: e.to_string(), + }) + } + Err(e) => { + error!("{}", e); + LookupReleaseResponse::BadRequest(ErrorResponse { + message: e.to_string(), + }) + } + }; + Box::new(futures::done(Ok(ret))) + } + fn accept_editgroup( &self, id: String, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 99035b85..6dbfc468 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -58,6 +58,10 @@ pub mod errors { description("editgroup was already accepted") display("attempted to accept an editgroup which was already accepted: {}", id) } + MissingOrMultipleExternalId(message: String) { + description("external identifiers missing or multiple specified") + display("external identifiers missing or multiple specified; please supply exactly one") + } } } } diff --git a/rust/tests/test_old_python_tests.rs b/rust/tests/test_old_python_tests.rs index dde8d66e..eae131a7 100644 --- a/rust/tests/test_old_python_tests.rs +++ b/rust/tests/test_old_python_tests.rs @@ -30,7 +30,8 @@ fn test_api_rich_create() { _ => unreachable!(), }; - let mut new_container = ContainerEntity::new("schmournal".to_string()); + let mut new_container = ContainerEntity::new(); + new_container.name = Some("schmournal".to_string()); new_container.publisher = Some("society of authors".to_string()); new_container.issnl = Some("2222-3333".to_string()); // extra=dict(a=2, i="zing"))), @@ -43,7 +44,8 @@ fn test_api_rich_create() { _ => unreachable!(), }; - let mut new_creator = CreatorEntity::new("anon y. mouse".to_string()); + let mut new_creator = CreatorEntity::new(); + new_creator.display_name = Some("anon y. mouse".to_string()); new_creator.orcid = Some("0000-0002-1825-0097".to_string()); // extra=dict(a=2, i="zing"))), let resp = client @@ -67,7 +69,8 @@ fn test_api_rich_create() { }; // this stub work will be referenced - let mut new_release = ReleaseEntity::new("derivative work".to_string()); + let mut new_release = ReleaseEntity::new(); + new_release.title = Some("derivative work".to_string()); new_release.release_type = Some("article-journal".to_string()); new_release.work_id = Some(work_id.clone()); let mut contrib = ReleaseContrib::new(); @@ -87,7 +90,8 @@ fn test_api_rich_create() { _ => unreachable!(), }; - let mut new_release = ReleaseEntity::new("dummy work".to_string()); + let mut new_release = ReleaseEntity::new(); + new_release.title = Some("dummy work".to_string()); new_release.release_type = Some("book".to_string()); new_release.work_id = Some(work_id.clone()); new_release.container_id = Some(container_id.clone()); |