diff options
-rw-r--r-- | rust/src/api_helpers.rs | 55 | ||||
-rw-r--r-- | rust/src/api_server.rs | 66 | ||||
-rw-r--r-- | rust/src/database_models.rs | 11 | ||||
-rw-r--r-- | rust/src/database_schema.rs | 2 | ||||
-rw-r--r-- | rust/tests/test_api_server.rs | 55 |
5 files changed, 142 insertions, 47 deletions
diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index ef07ee55..91c6200d 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -4,9 +4,8 @@ use database_schema::*; use diesel; use diesel::prelude::*; use errors::*; -use uuid::Uuid; use regex::Regex; - +use uuid::Uuid; pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result<Uuid> { // check for current active @@ -119,74 +118,80 @@ pub fn check_pmcid(raw: &str) -> Result<()> { if RE.is_match(raw) { Ok(()) } else { - Err(ErrorKind::MalformedExternalId( - format!("not a valid PubMed Central ID (PMCID): '{}' (expected, eg, 'PMC12345')", raw) - ).into()) + Err(ErrorKind::MalformedExternalId(format!( + "not a valid PubMed Central ID (PMCID): '{}' (expected, eg, 'PMC12345')", + raw + )).into()) } } pub fn check_pmid(raw: &str) -> Result<()> { lazy_static! { - static ref RE: Regex = Regex::new(r"^\d+$").unwrap(); + static ref RE: Regex = Regex::new(r"^\d+$").unwrap(); } if RE.is_match(raw) { Ok(()) } else { - Err(ErrorKind::MalformedExternalId( - format!("not a valid PubMed ID (PMID): '{}' (expected, eg, '1234')", raw) - ).into()) + Err(ErrorKind::MalformedExternalId(format!( + "not a valid PubMed ID (PMID): '{}' (expected, eg, '1234')", + raw + )).into()) } } pub fn check_wikidata_qid(raw: &str) -> Result<()> { lazy_static! { - static ref RE: Regex = Regex::new(r"^Q\d+$").unwrap(); + static ref RE: Regex = Regex::new(r"^Q\d+$").unwrap(); } if RE.is_match(raw) { Ok(()) } else { - Err(ErrorKind::MalformedExternalId( - format!("not a valid Wikidata QID: '{}' (expected, eg, 'Q1234')", raw) - ).into()) + Err(ErrorKind::MalformedExternalId(format!( + "not a valid Wikidata QID: '{}' (expected, eg, 'Q1234')", + raw + )).into()) } } pub fn check_doi(raw: &str) -> Result<()> { lazy_static! { - static ref RE: Regex = Regex::new(r"^10.\d{3,6}/.+$").unwrap(); + static ref RE: Regex = Regex::new(r"^10.\d{3,6}/.+$").unwrap(); } if RE.is_match(raw) { Ok(()) } else { - Err(ErrorKind::MalformedExternalId( - format!("not a valid DOI: '{}' (expected, eg, '10.1234/aksjdfh')", raw) - ).into()) + Err(ErrorKind::MalformedExternalId(format!( + "not a valid DOI: '{}' (expected, eg, '10.1234/aksjdfh')", + raw + )).into()) } } pub fn check_issn(raw: &str) -> Result<()> { lazy_static! { - static ref RE: Regex = Regex::new(r"^\d{4}-\d{3}[0-9X]$").unwrap(); + static ref RE: Regex = Regex::new(r"^\d{4}-\d{3}[0-9X]$").unwrap(); } if RE.is_match(raw) { Ok(()) } else { - Err(ErrorKind::MalformedExternalId( - format!("not a valid ISSN: '{}' (expected, eg, '1234-5678')", raw) - ).into()) + Err(ErrorKind::MalformedExternalId(format!( + "not a valid ISSN: '{}' (expected, eg, '1234-5678')", + raw + )).into()) } } pub fn check_orcid(raw: &str) -> Result<()> { lazy_static! { - static ref RE: Regex = Regex::new(r"^\d{4}-\d{4}-\d{4}-\d{4}$").unwrap(); + static ref RE: Regex = Regex::new(r"^\d{4}-\d{4}-\d{4}-\d{4}$").unwrap(); } if RE.is_match(raw) { Ok(()) } else { - Err(ErrorKind::MalformedExternalId( - format!("not a valid ORCID: '{}' (expected, eg, '0123-4567-3456-6789')", raw) - ).into()) + Err(ErrorKind::MalformedExternalId(format!( + "not a valid ORCID: '{}' (expected, eg, '0123-4567-3456-6789')", + raw + )).into()) } } diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 64c028be..d172cb16 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -4,16 +4,17 @@ use api_helpers::*; use chrono; use database_models::*; use database_schema::{ - changelog, container_edit, container_ident, container_rev, creator_edit, creator_ident, - creator_rev, editgroup, editor, file_edit, file_ident, file_release, file_rev, file_rev_url, - release_contrib, release_edit, release_ident, release_ref, release_rev, release_rev_abstract, - work_edit, work_ident, work_rev, + abstracts, changelog, container_edit, container_ident, container_rev, creator_edit, + creator_ident, creator_rev, editgroup, editor, file_edit, file_ident, file_release, file_rev, + file_rev_url, release_contrib, release_edit, release_ident, release_ref, release_rev, + release_rev_abstract, work_edit, work_ident, work_rev, }; use diesel::prelude::*; use diesel::{self, insert_into}; use errors::*; use fatcat_api::models; use fatcat_api::models::*; +use sha1::Sha1; use uuid::Uuid; use ConnectionPool; @@ -221,24 +222,26 @@ fn release_row2entity( .into_iter() .map(|c: ReleaseContribRow| ReleaseContrib { index: c.index, - raw: c.raw, + raw_name: c.raw_name, role: c.role, extra: c.extra_json, creator_id: c.creator_ident_id.map(|v| uuid2fcid(&v)), }) .collect(); - // XXX: join abstracts table 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| ReleaseEntityAbstracts { - sha1: Some(r.abstract_sha1), - mimetype: r.mimetype, - lang: r.lang, - content: None, - }) + .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 { @@ -767,6 +770,7 @@ impl Server { if contrib_list.is_empty() { Some(vec![]) } else { + println!("{:#?}", contrib_list); let contrib_rows: Vec<ReleaseContribNewRow> = contrib_list .iter() .map(|c| ReleaseContribNewRow { @@ -774,7 +778,7 @@ impl Server { creator_ident_id: c.creator_id .clone() .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), - raw: c.raw.clone(), + raw_name: c.raw_name.clone(), index: c.index, role: c.role.clone(), extra_json: c.extra.clone(), @@ -789,6 +793,42 @@ impl Server { } }; + 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)?; + } + edit.into_model() } diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index f875b492..50176f5f 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -225,7 +225,7 @@ pub struct ReleaseContribRow { pub id: i64, pub release_rev: Uuid, pub creator_ident_id: Option<Uuid>, - pub raw: Option<String>, + pub raw_name: Option<String>, pub role: Option<String>, pub index: Option<i64>, pub extra_json: Option<serde_json::Value>, @@ -236,7 +236,7 @@ pub struct ReleaseContribRow { pub struct ReleaseContribNewRow { pub release_rev: Uuid, pub creator_ident_id: Option<Uuid>, - pub raw: Option<String>, + pub raw_name: Option<String>, pub role: Option<String>, pub index: Option<i64>, pub extra_json: Option<serde_json::Value>, @@ -278,6 +278,13 @@ pub struct FileReleaseRow { pub target_release_ident_id: Uuid, } +#[derive(Debug, Queryable, Insertable, Associations, AsChangeset)] +#[table_name = "abstracts"] +pub struct AbstractsRow { + pub sha1: String, + pub content: String, +} + #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "editgroup"] pub struct EditgroupRow { diff --git a/rust/src/database_schema.rs b/rust/src/database_schema.rs index c23e3f83..f935302a 100644 --- a/rust/src/database_schema.rs +++ b/rust/src/database_schema.rs @@ -157,7 +157,7 @@ table! { id -> Int8, release_rev -> Uuid, creator_ident_id -> Nullable<Uuid>, - raw -> Nullable<Text>, + raw_name -> Nullable<Text>, role -> Nullable<Text>, index -> Nullable<Int8>, extra_json -> Nullable<Json>, diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs index b9880ba1..f43101c0 100644 --- a/rust/tests/test_api_server.rs +++ b/rust/tests/test_api_server.rs @@ -422,11 +422,11 @@ fn test_post_release() { }], "contribs": [{ "index": 1, - "raw": "textual description of contributor (aka, name)", + "raw_name": "textual description of contributor (aka, name)", "creator_id": "aaaaaaaaaaaaaircaaaaaaaaae", "contrib_type": "author" },{ - "raw": "shorter" + "raw_name": "shorter" }], "extra": { "source": "speculation" } }"#, @@ -620,11 +620,11 @@ fn test_400() { }], "contribs": [{ "index": 1, - "raw": "textual description of contributor (aka, name)", + "raw_name": "textual description of contributor (aka, name)", "creator_id": "aaaaaaaaaaaaaircaaaaaaaaae", "contrib_type": "author" },{ - "raw": "shorter" + "raw_name": "shorter" }], "extra": { "source": "speculation" } }"#, @@ -820,8 +820,8 @@ fn test_abstracts() { &router, ), status::Ok, - // SHA-1 of first abstract string - Some("4e30ded694c6a7775b9e7b019dfda6be0dd60944"), + // SHA-1 of first abstract string (with no trailing newline) + Some("65c171bd8c968e12ede25ad95f02cd4b2ce9db52"), ); check_response( request::get( @@ -842,3 +842,46 @@ fn test_abstracts() { Some("24iu3i25u2"), ); } + +#[test] +fn test_contribs() { + let (headers, router, conn) = setup(); + + check_response( + request::post( + "http://localhost:9411/v0/release", + headers.clone(), + r#"{"title": "some paper", + "doi": "10.1234/iiiiiii", + "contribs": [{ + "index": 1, + "raw_name": "textual description of contributor (aka, name)", + "creator_id": "aaaaaaaaaaaaaircaaaaaaaaae", + "contrib_type": "author", + "extra": {"key": "value 28328424942"} + },{ + "raw_name": "shorter" + }] + }"#, + &router, + ), + status::Created, + None, + ); + + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001").unwrap(); + let editgroup_id = get_or_create_editgroup(editor_id, &conn).unwrap(); + check_response( + request::post( + &format!( + "http://localhost:9411/v0/editgroup/{}/accept", + uuid2fcid(&editgroup_id) + ), + headers.clone(), + "", + &router, + ), + status::Ok, + None, + ); +} |