diff options
| author | Bryan Newbold <bnewbold@robocracy.org> | 2018-12-13 19:36:57 +0800 | 
|---|---|---|
| committer | Bryan Newbold <bnewbold@robocracy.org> | 2018-12-13 19:36:57 +0800 | 
| commit | 54abbfcd57fa82624504a1f2f359aa668e75f3d8 (patch) | |
| tree | ef84f9de9f734386678c27c98685c4da27311d51 | |
| parent | 061e66c4c41063e6b7321f1f421b5152e9e5a84d (diff) | |
| download | fatcat-54abbfcd57fa82624504a1f2f359aa668e75f3d8.tar.gz fatcat-54abbfcd57fa82624504a1f2f359aa668e75f3d8.zip | |
skeleton out new schema features
| -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()); | 
