aboutsummaryrefslogtreecommitdiffstats
path: root/rust/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/bin/fatcatd.rs1
-rw-r--r--rust/src/database_models.rs131
-rw-r--r--rust/src/database_schema.rs33
-rw-r--r--rust/src/editing.rs6
-rw-r--r--rust/src/endpoint_handlers.rs156
-rw-r--r--rust/src/endpoints.rs26
-rw-r--r--rust/src/entity_crud.rs175
-rw-r--r--rust/src/lib.rs2
8 files changed, 373 insertions, 157 deletions
diff --git a/rust/src/bin/fatcatd.rs b/rust/src/bin/fatcatd.rs
index 75a6f000..ccce6725 100644
--- a/rust/src/bin/fatcatd.rs
+++ b/rust/src/bin/fatcatd.rs
@@ -88,7 +88,6 @@ fn main() -> Result<()> {
server.metrics.incr("restart").unwrap();
}
};
- info!(logger, "{:#?}", server.metrics);
info!(
logger,
diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs
index 63fbcb29..adb38bda 100644
--- a/rust/src/database_models.rs
+++ b/rust/src/database_models.rs
@@ -3,8 +3,10 @@
use crate::database_schema::*;
use crate::errors::*;
use crate::identifiers::uuid2fcid;
-use chrono;
-use fatcat_api_spec::models::{ChangelogEntry, Editgroup, EditgroupAnnotation, Editor, EntityEdit};
+use chrono::Utc;
+use fatcat_api_spec::models::{
+ ChangelogEntry, Editgroup, EditgroupAnnotation, Editor, EntityEdit, ReleaseRef,
+};
use serde_json;
use uuid::Uuid;
@@ -127,11 +129,10 @@ pub struct ContainerRevRow {
pub id: Uuid,
pub extra_json: Option<serde_json::Value>,
pub name: String,
+ pub container_type: Option<String>,
pub publisher: Option<String>,
pub issnl: Option<String>,
pub wikidata_qid: Option<String>,
- pub abbrev: Option<String>,
- pub coden: Option<String>,
}
#[derive(Debug, Associations, AsChangeset, Insertable)]
@@ -139,11 +140,10 @@ pub struct ContainerRevRow {
pub struct ContainerRevNewRow {
pub extra_json: Option<serde_json::Value>,
pub name: String,
+ pub container_type: Option<String>,
pub publisher: Option<String>,
pub issnl: Option<String>,
pub wikidata_qid: Option<String>,
- pub abbrev: Option<String>,
- pub coden: Option<String>,
}
entity_structs!(
@@ -305,7 +305,7 @@ pub struct WebcaptureRevCdxRow {
pub id: i64,
pub webcapture_rev: Uuid,
pub surt: String,
- pub timestamp: String,
+ pub timestamp: chrono::DateTime<Utc>,
pub url: String,
pub mimetype: Option<String>,
pub status_code: Option<i64>,
@@ -318,7 +318,7 @@ pub struct WebcaptureRevCdxRow {
pub struct WebcaptureRevCdxNewRow {
pub webcapture_rev: Uuid,
pub surt: String,
- pub timestamp: String,
+ pub timestamp: chrono::DateTime<Utc>,
pub url: String,
pub mimetype: Option<String>,
pub status_code: Option<i64>,
@@ -376,7 +376,9 @@ pub struct ReleaseRevRow {
pub extra_json: Option<serde_json::Value>,
pub work_ident_id: Uuid,
pub container_ident_id: Option<Uuid>,
+ pub refs_blob_sha1: Option<String>,
pub title: String,
+ pub original_title: Option<String>,
pub release_type: Option<String>,
pub release_status: Option<String>,
pub release_date: Option<chrono::NaiveDate>,
@@ -387,11 +389,14 @@ pub struct ReleaseRevRow {
pub wikidata_qid: Option<String>,
pub isbn13: Option<String>,
pub core_id: Option<String>,
+ pub arxiv_id: Option<String>,
+ pub jstor_id: Option<String>,
pub volume: Option<String>,
pub issue: Option<String>,
pub pages: Option<String>,
pub publisher: Option<String>,
pub language: Option<String>,
+ pub license_slug: Option<String>,
}
#[derive(Debug, Associations, AsChangeset, Insertable)]
@@ -400,7 +405,9 @@ pub struct ReleaseRevNewRow {
pub extra_json: Option<serde_json::Value>,
pub work_ident_id: Uuid,
pub container_ident_id: Option<Uuid>,
+ pub refs_blob_sha1: Option<String>,
pub title: String,
+ pub original_title: Option<String>,
pub release_type: Option<String>,
pub release_status: Option<String>,
pub release_date: Option<chrono::NaiveDate>,
@@ -411,11 +418,14 @@ pub struct ReleaseRevNewRow {
pub wikidata_qid: Option<String>,
pub isbn13: Option<String>,
pub core_id: Option<String>,
+ pub arxiv_id: Option<String>,
+ pub jstor_id: Option<String>,
pub volume: Option<String>,
pub issue: Option<String>,
pub pages: Option<String>,
pub publisher: Option<String>,
pub language: Option<String>,
+ pub license_slug: Option<String>,
}
entity_structs!(
@@ -476,6 +486,7 @@ pub struct ReleaseContribRow {
pub creator_ident_id: Option<Uuid>,
pub raw_name: Option<String>,
pub role: Option<String>,
+ pub raw_affiliation: Option<String>,
pub index_val: Option<i32>,
pub extra_json: Option<serde_json::Value>,
}
@@ -487,39 +498,107 @@ pub struct ReleaseContribNewRow {
pub creator_ident_id: Option<Uuid>,
pub raw_name: Option<String>,
pub role: Option<String>,
+ pub raw_affiliation: Option<String>,
pub index_val: Option<i32>,
pub extra_json: Option<serde_json::Value>,
}
-#[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)]
+#[derive(Debug, Queryable, Insertable, Associations, AsChangeset)]
#[table_name = "release_ref"]
pub struct ReleaseRefRow {
- pub id: i64,
pub release_rev: Uuid,
- pub target_release_ident_id: Option<Uuid>,
- pub index_val: Option<i32>,
- pub key: Option<String>,
- pub extra_json: Option<serde_json::Value>,
- pub container_name: Option<String>,
- pub year: Option<i32>,
- pub title: Option<String>,
- pub locator: Option<String>,
+ pub index_val: i32,
+ pub target_release_ident_id: Uuid,
}
-#[derive(Debug, Insertable, AsChangeset)]
-#[table_name = "release_ref"]
-pub struct ReleaseRefNewRow {
- pub release_rev: Uuid,
- pub target_release_ident_id: Option<Uuid>,
- pub index_val: Option<i32>,
+#[derive(Debug, Queryable, Insertable, Associations, AsChangeset)]
+#[table_name = "refs_blob"]
+pub struct RefsBlobRow {
+ pub sha1: String,
+ pub refs_json: serde_json::Value,
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+/// This model is a stable representation of what goes in a RefsBlobRow `refs_json` field (an array
+/// of this model). We could rely on the `ReleaseRef` API spec model directly, but that would lock
+/// the database contents to the API spec rigidly; by defining this struct independently, we can
+/// migrate the schemas. To start, this is a direct copy of the `ReleaseRef` model.
+pub struct RefsBlobJson {
+ #[serde(rename = "index")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub index: Option<i64>,
+
+ /// base32-encoded unique identifier
+ #[serde(rename = "target_release_id")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub target_release_id: Option<String>,
+
+ #[serde(rename = "extra")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub extra: Option<serde_json::Value>,
+
+ #[serde(rename = "key")]
+ #[serde(skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
- pub extra_json: Option<serde_json::Value>,
+
+ #[serde(rename = "year")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub year: Option<i64>,
+
+ #[serde(rename = "container_name")]
+ #[serde(skip_serializing_if = "Option::is_none")]
pub container_name: Option<String>,
- pub year: Option<i32>,
+
+ #[serde(rename = "title")]
+ #[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
+
+ #[serde(rename = "locator")]
+ #[serde(skip_serializing_if = "Option::is_none")]
pub locator: Option<String>,
}
+impl RefsBlobJson {
+ pub fn into_model(self) -> ReleaseRef {
+ ReleaseRef {
+ index: self.index,
+ target_release_id: self.target_release_id,
+ extra: self.extra,
+ key: self.key,
+ year: self.year,
+ container_name: self.container_name,
+ title: self.title,
+ locator: self.locator,
+ }
+ }
+
+ pub fn to_model(&self) -> ReleaseRef {
+ ReleaseRef {
+ index: self.index,
+ target_release_id: self.target_release_id.clone(),
+ extra: self.extra.clone(),
+ key: self.key.clone(),
+ year: self.year,
+ container_name: self.container_name.clone(),
+ title: self.title.clone(),
+ locator: self.locator.clone(),
+ }
+ }
+
+ pub fn from_model(model: &ReleaseRef) -> RefsBlobJson {
+ RefsBlobJson {
+ index: model.index,
+ target_release_id: model.target_release_id.clone(),
+ extra: model.extra.clone(),
+ key: model.key.clone(),
+ year: model.year,
+ container_name: model.container_name.clone(),
+ title: model.title.clone(),
+ locator: model.locator.clone(),
+ }
+ }
+}
+
#[derive(Debug, Queryable, Insertable, Associations, AsChangeset)]
#[table_name = "file_rev_release"]
pub struct FileRevReleaseRow {
diff --git a/rust/src/database_schema.rs b/rust/src/database_schema.rs
index 3bc57d95..ea184226 100644
--- a/rust/src/database_schema.rs
+++ b/rust/src/database_schema.rs
@@ -51,11 +51,10 @@ table! {
id -> Uuid,
extra_json -> Nullable<Jsonb>,
name -> Text,
+ container_type -> Nullable<Text>,
publisher -> Nullable<Text>,
issnl -> Nullable<Text>,
wikidata_qid -> Nullable<Text>,
- abbrev -> Nullable<Text>,
- coden -> Nullable<Text>,
}
}
@@ -239,12 +238,20 @@ table! {
}
table! {
+ refs_blob (sha1) {
+ sha1 -> Text,
+ refs_json -> Jsonb,
+ }
+}
+
+table! {
release_contrib (id) {
id -> Int8,
release_rev -> Uuid,
creator_ident_id -> Nullable<Uuid>,
raw_name -> Nullable<Text>,
role -> Nullable<Text>,
+ raw_affiliation -> Nullable<Text>,
index_val -> Nullable<Int4>,
extra_json -> Nullable<Jsonb>,
}
@@ -273,17 +280,10 @@ table! {
}
table! {
- release_ref (id) {
- id -> Int8,
+ release_ref (release_rev, index_val) {
release_rev -> Uuid,
- target_release_ident_id -> Nullable<Uuid>,
- index_val -> Nullable<Int4>,
- key -> Nullable<Text>,
- extra_json -> Nullable<Jsonb>,
- container_name -> Nullable<Text>,
- year -> Nullable<Int4>,
- title -> Nullable<Text>,
- locator -> Nullable<Text>,
+ index_val -> Int4,
+ target_release_ident_id -> Uuid,
}
}
@@ -293,7 +293,9 @@ table! {
extra_json -> Nullable<Jsonb>,
work_ident_id -> Uuid,
container_ident_id -> Nullable<Uuid>,
+ refs_blob_sha1 -> Nullable<Text>,
title -> Text,
+ original_title -> Nullable<Text>,
release_type -> Nullable<Text>,
release_status -> Nullable<Text>,
release_date -> Nullable<Date>,
@@ -304,11 +306,14 @@ table! {
wikidata_qid -> Nullable<Text>,
isbn13 -> Nullable<Text>,
core_id -> Nullable<Text>,
+ arxiv_id -> Nullable<Text>,
+ jstor_id -> Nullable<Text>,
volume -> Nullable<Text>,
issue -> Nullable<Text>,
pages -> Nullable<Text>,
publisher -> Nullable<Text>,
language -> Nullable<Text>,
+ license_slug -> Nullable<Text>,
}
}
@@ -358,7 +363,7 @@ table! {
id -> Int8,
webcapture_rev -> Uuid,
surt -> Text,
- timestamp -> Text,
+ timestamp -> Timestamptz,
url -> Text,
mimetype -> Nullable<Text>,
status_code -> Nullable<Int8>,
@@ -439,6 +444,7 @@ joinable!(release_ident -> release_rev (rev_id));
joinable!(release_ref -> release_ident (target_release_ident_id));
joinable!(release_ref -> release_rev (release_rev));
joinable!(release_rev -> container_ident (container_ident_id));
+joinable!(release_rev -> refs_blob (refs_blob_sha1));
joinable!(release_rev -> work_ident (work_ident_id));
joinable!(release_rev_abstract -> abstracts (abstract_sha1));
joinable!(release_rev_abstract -> release_rev (release_rev));
@@ -475,6 +481,7 @@ allow_tables_to_appear_in_same_query!(
fileset_rev_file,
fileset_rev_release,
fileset_rev_url,
+ refs_blob,
release_contrib,
release_edit,
release_ident,
diff --git a/rust/src/editing.rs b/rust/src/editing.rs
index e181e8a7..c17e5964 100644
--- a/rust/src/editing.rs
+++ b/rust/src/editing.rs
@@ -42,6 +42,8 @@ pub fn make_edit_context(
editor_id: FatcatId,
editgroup_id: Option<FatcatId>,
autoaccept: bool,
+ description: Option<String>,
+ extra: Option<serde_json::Value>,
) -> Result<EditContext> {
// *either* autoaccept is false and editgroup_id is Some, *or* autoaccept is true and
// editgroup_id is None
@@ -54,8 +56,8 @@ pub fn make_edit_context(
editor: None,
changelog_index: None,
submitted: None,
- description: None,
- extra: None,
+ description: description,
+ extra: extra,
annotations: None,
edits: None,
};
diff --git a/rust/src/endpoint_handlers.rs b/rust/src/endpoint_handlers.rs
index bc606af9..d9bd3403 100644
--- a/rust/src/endpoint_handlers.rs
+++ b/rust/src/endpoint_handlers.rs
@@ -26,9 +26,11 @@ macro_rules! entity_batch_handler {
autoaccept: bool,
editor_id: FatcatId,
editgroup_id: Option<FatcatId>,
+ description: Option<String>,
+ extra: Option<serde_json::Value>,
) -> Result<Vec<EntityEdit>> {
- let edit_context = make_edit_context(conn, editor_id, editgroup_id, autoaccept)?;
+ let edit_context = make_edit_context(conn, editor_id, editgroup_id, autoaccept, description, extra)?;
edit_context.check(&conn)?;
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())?;
@@ -259,71 +261,99 @@ impl Server {
pmid: &Option<String>,
pmcid: &Option<String>,
core_id: &Option<String>,
+ arxiv_id: &Option<String>,
+ jstor_id: &Option<String>,
expand_flags: ExpandFlags,
hide_flags: HideFlags,
) -> Result<ReleaseEntity> {
- let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) =
- match (doi, wikidata_qid, isbn13, pmid, pmcid, core_id) {
- (Some(doi), None, 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, 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, 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, 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), None) => {
- 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)?
- }
- (None, None, None, None, None, Some(core_id)) => {
- // TODO: check_core_id(core_id)?;
- release_ident::table
- .inner_join(release_rev::table)
- .filter(release_rev::core_id.eq(core_id))
- .filter(release_ident::is_live.eq(true))
- .filter(release_ident::redirect_id.is_null())
- .first(conn)?
- }
- _ => {
- return Err(
- FatcatError::MissingOrMultipleExternalId("in lookup".to_string()).into(),
- );
- }
- };
+ let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = match (
+ doi,
+ wikidata_qid,
+ isbn13,
+ pmid,
+ pmcid,
+ core_id,
+ arxiv_id,
+ jstor_id,
+ ) {
+ (Some(doi), None, None, None, 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, 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, None, 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, None, None, 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), None, None, None) => {
+ 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)?
+ }
+ (None, None, None, None, None, Some(core_id), None, None) => {
+ // TODO: check_core_id(core_id)?;
+ release_ident::table
+ .inner_join(release_rev::table)
+ .filter(release_rev::core_id.eq(core_id))
+ .filter(release_ident::is_live.eq(true))
+ .filter(release_ident::redirect_id.is_null())
+ .first(conn)?
+ }
+ (None, None, None, None, None, None, Some(arxiv_id), None) => {
+ // TODO: check_arxiv_id(arxiv_id)?;
+ release_ident::table
+ .inner_join(release_rev::table)
+ .filter(release_rev::arxiv_id.eq(arxiv_id))
+ .filter(release_ident::is_live.eq(true))
+ .filter(release_ident::redirect_id.is_null())
+ .first(conn)?
+ }
+ (None, None, None, None, None, None, None, Some(jstor_id)) => {
+ // TODO: check_jstor_id(jstor_id)?;
+ release_ident::table
+ .inner_join(release_rev::table)
+ .filter(release_rev::jstor_id.eq(jstor_id))
+ .filter(release_ident::is_live.eq(true))
+ .filter(release_ident::redirect_id.is_null())
+ .first(conn)?
+ }
+ _ => {
+ return Err(
+ FatcatError::MissingOrMultipleExternalId("in lookup".to_string()).into(),
+ );
+ }
+ };
let mut entity = ReleaseEntity::db_from_row(conn, rev, Some(ident), hide_flags)?;
entity.db_expand(&conn, expand_flags)?;
diff --git a/rust/src/endpoints.rs b/rust/src/endpoints.rs
index f7e93448..2e467957 100644
--- a/rust/src/endpoints.rs
+++ b/rust/src/endpoints.rs
@@ -120,7 +120,7 @@ macro_rules! wrap_entity_handlers {
let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($post_fn)))?;
auth_context.require_role(FatcatRole::Editor)?;
auth_context.require_editgroup(&conn, editgroup_id)?;
- let edit_context = make_edit_context(&conn, auth_context.editor_id, Some(editgroup_id), false)?;
+ let edit_context = make_edit_context(&conn, auth_context.editor_id, Some(editgroup_id), false, None, None)?;
edit_context.check(&conn)?;
entity.db_create(&conn, &edit_context)?.into_model()
}).map_err(|e| FatcatError::from(e)) {
@@ -138,18 +138,30 @@ macro_rules! wrap_entity_handlers {
entity_list: &Vec<models::$model>,
autoaccept: Option<bool>,
editgroup_id: Option<String>,
+ description: Option<String>,
+ extra: Option<String>,
context: &Context,
) -> Box<Future<Item = $post_batch_resp, Error = ApiError> + Send> {
let conn = self.db_pool.get().expect("db_pool error");
let ret = match conn.transaction(|| {
let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($post_batch_fn)))?;
- auth_context.require_role(FatcatRole::Editor)?;
+ let autoaccept = autoaccept.unwrap_or(false);
+ if autoaccept {
+ auth_context.require_role(FatcatRole::Admin)?;
+ } else {
+ auth_context.require_role(FatcatRole::Editor)?;
+ };
let editgroup_id = if let Some(s) = editgroup_id {
+ // make_edit_context() checks for "both editgroup_id and autosubmit" error case
let eg_id = FatcatId::from_str(&s)?;
auth_context.require_editgroup(&conn, eg_id)?;
Some(eg_id)
} else { None };
- self.$post_batch_handler(&conn, entity_list, autoaccept.unwrap_or(false), auth_context.editor_id, editgroup_id)
+ let extra: Option<serde_json::Value> = match extra {
+ Some(v) => serde_json::from_str(&v)?,
+ None => None,
+ };
+ self.$post_batch_handler(&conn, entity_list, autoaccept, auth_context.editor_id, editgroup_id, description, extra)
}).map_err(|e| FatcatError::from(e)) {
Ok(edits) => {
self.metrics.count("entities.created", edits.len() as i64).ok();
@@ -178,7 +190,7 @@ macro_rules! wrap_entity_handlers {
auth_context.require_role(FatcatRole::Editor)?;
let entity_id = FatcatId::from_str(&ident)?;
auth_context.require_editgroup(&conn, editgroup_id)?;
- let edit_context = make_edit_context(&conn, auth_context.editor_id, Some(editgroup_id), false)?;
+ let edit_context = make_edit_context(&conn, auth_context.editor_id, Some(editgroup_id), false, None, None)?;
edit_context.check(&conn)?;
entity.db_update(&conn, &edit_context, entity_id)?.into_model()
}).map_err(|e| FatcatError::from(e)) {
@@ -204,7 +216,7 @@ macro_rules! wrap_entity_handlers {
auth_context.require_role(FatcatRole::Editor)?;
let entity_id = FatcatId::from_str(&ident)?;
auth_context.require_editgroup(&conn, editgroup_id)?;
- let edit_context = make_edit_context(&conn, auth_context.editor_id, Some(editgroup_id), false)?;
+ let edit_context = make_edit_context(&conn, auth_context.editor_id, Some(editgroup_id), false, None, None)?;
edit_context.check(&conn)?;
$model::db_delete(&conn, &edit_context, entity_id)?.into_model()
}).map_err(|e| FatcatError::from(e)) {
@@ -659,6 +671,8 @@ impl Api for Server {
pmid: Option<String>,
pmcid: Option<String>,
core_id: Option<String>,
+ arxiv_id: Option<String>,
+ jstor_id: Option<String>,
expand: Option<String>,
hide: Option<String>,
_context: &Context,
@@ -682,6 +696,8 @@ impl Api for Server {
&pmid,
&pmcid,
&core_id,
+ &arxiv_id,
+ &jstor_id,
expand_flags,
hide_flags,
)
diff --git a/rust/src/entity_crud.rs b/rust/src/entity_crud.rs
index ce1c1ed7..a92c45a6 100644
--- a/rust/src/entity_crud.rs
+++ b/rust/src/entity_crud.rs
@@ -8,7 +8,7 @@
use crate::database_models::*;
use crate::database_schema::*;
use crate::editing::EditContext;
-use crate::endpoint_handlers::get_release_files;
+use crate::endpoint_handlers::{get_release_files, get_release_filesets, get_release_webcaptures};
use crate::errors::*;
use crate::identifiers::*;
use crate::server::DbConn;
@@ -798,8 +798,7 @@ impl EntityCrud for ContainerEntity {
wikidata_qid: None,
publisher: None,
name: None,
- abbrev: None,
- coden: None,
+ container_type: None,
state: Some(ident_row.state().unwrap().shortname()),
ident: Some(FatcatId::from_uuid(&ident_row.id).to_string()),
revision: ident_row.rev_id.map(|u| u.to_string()),
@@ -831,8 +830,7 @@ impl EntityCrud for ContainerEntity {
wikidata_qid: rev_row.wikidata_qid,
publisher: rev_row.publisher,
name: Some(rev_row.name),
- abbrev: rev_row.abbrev,
- coden: rev_row.coden,
+ container_type: rev_row.container_type,
state,
ident: ident_id,
revision: Some(rev_row.id.to_string()),
@@ -869,8 +867,7 @@ impl EntityCrud for ContainerEntity {
publisher: model.publisher.clone(),
issnl: model.issnl.clone(),
wikidata_qid: model.wikidata_qid.clone(),
- abbrev: model.abbrev.clone(),
- coden: model.coden.clone(),
+ container_type: model.container_type.clone(),
extra_json: model.extra.clone(),
})
.collect::<Vec<ContainerRevNewRow>>(),
@@ -1619,6 +1616,7 @@ impl EntityCrud for ReleaseEntity {
Ok(ReleaseEntity {
title: None,
+ original_title: None,
release_type: None,
release_status: None,
release_date: None,
@@ -1627,8 +1625,10 @@ impl EntityCrud for ReleaseEntity {
pmid: None,
pmcid: None,
isbn13: None,
- core_id: None,
wikidata_qid: None,
+ core_id: None,
+ arxiv_id: None,
+ jstor_id: None,
volume: None,
issue: None,
pages: None,
@@ -1639,6 +1639,7 @@ impl EntityCrud for ReleaseEntity {
container_id: None,
publisher: None,
language: None,
+ license_slug: None,
work_id: None,
refs: None,
contribs: None,
@@ -1675,6 +1676,26 @@ impl EntityCrud for ReleaseEntity {
};
self.files = Some(get_release_files(conn, ident, HideFlags::none())?);
}
+ if expand.filesets && self.ident.is_some() {
+ let ident = match &self.ident {
+ None => bail!("Can't expand filesets on a non-concrete entity"), // redundant with above is_some()
+ Some(ident) => match &self.redirect {
+ None => FatcatId::from_str(&ident)?,
+ Some(redir) => FatcatId::from_str(&redir)?,
+ },
+ };
+ self.filesets = Some(get_release_filesets(conn, ident, HideFlags::none())?);
+ }
+ if expand.webcaptures && self.ident.is_some() {
+ let ident = match &self.ident {
+ None => bail!("Can't expand webcaptures on a non-concrete entity"), // redundant with above is_some()
+ Some(ident) => match &self.redirect {
+ None => FatcatId::from_str(&ident)?,
+ Some(redir) => FatcatId::from_str(&redir)?,
+ },
+ };
+ self.webcaptures = Some(get_release_webcaptures(conn, ident, HideFlags::none())?);
+ }
if expand.container {
if let Some(ref cid) = self.container_id {
self.container = Some(ContainerEntity::db_get(
@@ -1812,28 +1833,28 @@ impl EntityCrud for ReleaseEntity {
None => (None, None, None),
};
- let refs: Option<Vec<ReleaseRef>> = match hide.refs {
- true => None,
- false => Some(
- release_ref::table
+ let refs: Option<Vec<ReleaseRef>> = match (hide.refs, rev_row.refs_blob_sha1) {
+ (true, _) => None,
+ (false, None) => Some(vec![]),
+ (false, Some(sha1)) => Some({
+ let refs_blob: RefsBlobRow = refs_blob::table
+ .find(sha1) // checked in match
+ .get_result(conn)?;
+ let refs: Vec<RefsBlobJson> = serde_json::from_value(refs_blob.refs_json)?;
+ let mut refs: Vec<ReleaseRef> = refs.into_iter().map(|j| j.into_model()).collect();
+ let ref_rows: Vec<ReleaseRefRow> = release_ref::table
.filter(release_ref::release_rev.eq(rev_row.id))
.order(release_ref::index_val.asc())
- .get_results(conn)?
- .into_iter()
- .map(|r: ReleaseRefRow| ReleaseRef {
- index: r.index_val.map(|v| v as i64),
- key: r.key,
- extra: r.extra_json,
- container_name: r.container_name,
- year: r.year.map(|v| v as i64),
- title: r.title,
- locator: r.locator,
- target_release_id: r
- .target_release_ident_id
- .map(|v| FatcatId::from_uuid(&v).to_string()),
- })
- .collect(),
- ),
+ .get_results(conn)?;
+ for index in 0..refs.len() {
+ refs[index].index = Some(index as i64)
+ }
+ for row in ref_rows {
+ refs[row.index_val as usize].target_release_id =
+ Some(FatcatId::from_uuid(&row.target_release_ident_id).to_string());
+ }
+ refs
+ }),
};
let contribs: Option<Vec<ReleaseContrib>> = match hide.contribs {
@@ -1851,6 +1872,7 @@ impl EntityCrud for ReleaseEntity {
index: c.index_val.map(|v| v as i64),
raw_name: c.raw_name,
role: c.role,
+ raw_affiliation: c.raw_affiliation,
extra: c.extra_json,
creator_id: c
.creator_ident_id
@@ -1884,6 +1906,7 @@ impl EntityCrud for ReleaseEntity {
Ok(ReleaseEntity {
title: Some(rev_row.title),
+ original_title: rev_row.original_title,
release_type: rev_row.release_type,
release_status: rev_row.release_status,
release_date: rev_row.release_date,
@@ -1892,8 +1915,10 @@ impl EntityCrud for ReleaseEntity {
pmid: rev_row.pmid,
pmcid: rev_row.pmcid,
isbn13: rev_row.isbn13,
- core_id: rev_row.core_id,
wikidata_qid: rev_row.wikidata_qid,
+ core_id: rev_row.core_id,
+ arxiv_id: rev_row.arxiv_id,
+ jstor_id: rev_row.jstor_id,
volume: rev_row.volume,
issue: rev_row.issue,
pages: rev_row.pages,
@@ -1906,6 +1931,7 @@ impl EntityCrud for ReleaseEntity {
.map(|u| FatcatId::from_uuid(&u).to_string()),
publisher: rev_row.publisher,
language: rev_row.language,
+ license_slug: rev_row.license_slug,
work_id: Some(FatcatId::from_uuid(&rev_row.work_ident_id).to_string()),
refs,
contribs,
@@ -1934,6 +1960,7 @@ impl EntityCrud for ReleaseEntity {
if let Some(ref extid) = entity.wikidata_qid {
check_wikidata_qid(extid)?;
}
+ // TODO: JSTOR and arxiv IDs
if let Some(ref release_type) = entity.release_type {
check_release_type(release_type)?;
}
@@ -1953,13 +1980,65 @@ impl EntityCrud for ReleaseEntity {
.into());
}
+ // First, calculate and upsert any refs JSON blobs and record the SHA1 keys, so they can be
+ // included in the release_rev row itself
+ let mut refs_blob_rows: Vec<RefsBlobRow> = vec![];
+ let mut refs_blob_sha1: Vec<Option<String>> = vec![];
+ for model in models.iter() {
+ match &model.refs {
+ None => {
+ refs_blob_sha1.push(None);
+ }
+ Some(ref_list) => {
+ if ref_list.is_empty() {
+ refs_blob_sha1.push(None);
+ continue;
+ }
+ // Have to strip out target refs and indexes, or hashing won't work well when
+ // these change
+ let ref_list: Vec<RefsBlobJson> = ref_list
+ .iter()
+ .map(|r: &ReleaseRef| {
+ let mut r = RefsBlobJson::from_model(r);
+ r.target_release_id = None;
+ r.index = None;
+ r
+ })
+ .collect();
+ // TODO: maybe `canonical_json` crate?
+ let refs_json = serde_json::to_value(ref_list)?;
+ let refs_str = refs_json.to_string();
+ let sha1 = Sha1::from(refs_str).hexdigest();
+ let blob = RefsBlobRow {
+ sha1: sha1.clone(),
+ refs_json,
+ };
+ refs_blob_rows.push(blob);
+ refs_blob_sha1.push(Some(sha1));
+ }
+ };
+ }
+
+ if !refs_blob_rows.is_empty() {
+ // Sort of an "upsert"; only inserts new abstract rows if they don't already exist
+ insert_into(refs_blob::table)
+ .values(&refs_blob_rows)
+ .on_conflict(refs_blob::sha1)
+ .do_nothing()
+ .execute(conn)?;
+ }
+
+ // Then the main release_revs themselves
let rev_ids: Vec<Uuid> = insert_into(release_rev::table)
.values(
models
.iter()
- .map(|model| {
+ .zip(refs_blob_sha1.into_iter())
+ .map(|(model, refs_sha1)| {
Ok(ReleaseRevNewRow {
+ refs_blob_sha1: refs_sha1,
title: model.title.clone().unwrap(), // titles checked above
+ original_title: model.original_title.clone(),
release_type: model.release_type.clone(),
release_status: model.release_status.clone(),
release_date: model.release_date,
@@ -1970,6 +2049,8 @@ impl EntityCrud for ReleaseEntity {
wikidata_qid: model.wikidata_qid.clone(),
isbn13: model.isbn13.clone(),
core_id: model.core_id.clone(),
+ arxiv_id: model.arxiv_id.clone(),
+ jstor_id: model.jstor_id.clone(),
volume: model.volume.clone(),
issue: model.issue.clone(),
pages: model.pages.clone(),
@@ -1983,6 +2064,7 @@ impl EntityCrud for ReleaseEntity {
},
publisher: model.publisher.clone(),
language: model.language.clone(),
+ license_slug: model.license_slug.clone(),
extra_json: model.extra.clone()
})
})
@@ -1991,34 +2073,32 @@ impl EntityCrud for ReleaseEntity {
.returning(release_rev::id)
.get_results(conn)?;
- let mut release_ref_rows: Vec<ReleaseRefNewRow> = vec![];
+ let mut release_ref_rows: Vec<ReleaseRefRow> = vec![];
let mut release_contrib_rows: Vec<ReleaseContribNewRow> = vec![];
let mut abstract_rows: Vec<AbstractsRow> = vec![];
let mut release_abstract_rows: Vec<ReleaseRevAbstractNewRow> = vec![];
for (model, rev_id) in models.iter().zip(rev_ids.iter()) {
+ // We didn't know the release_rev id to insert here, so need to re-iterate over refs
match &model.refs {
None => (),
Some(ref_list) => {
- let these_ref_rows: Vec<ReleaseRefNewRow> = ref_list
+ let these_ref_rows: Vec<ReleaseRefRow> = ref_list
.iter()
- .map(|r| {
- Ok(ReleaseRefNewRow {
+ .enumerate()
+ .filter(|(_, r)| r.target_release_id.is_some())
+ .map(|(index, r)| {
+ Ok(ReleaseRefRow {
release_rev: *rev_id,
- target_release_ident_id: match r.target_release_id.clone() {
- None => None,
- Some(v) => Some(FatcatId::from_str(&v)?.to_uuid()),
- },
- index_val: r.index.map(|v| v as i32),
- key: r.key.clone(),
- container_name: r.container_name.clone(),
- year: r.year.map(|v| v as i32),
- title: r.title.clone(),
- locator: r.locator.clone(),
- extra_json: r.extra.clone(),
+ // unwrap() checked by is_some() filter
+ target_release_ident_id: FatcatId::from_str(
+ &r.target_release_id.clone().unwrap(),
+ )?
+ .to_uuid(),
+ index_val: index as i32,
})
})
- .collect::<Result<Vec<ReleaseRefNewRow>>>()?;
+ .collect::<Result<Vec<ReleaseRefRow>>>()?;
release_ref_rows.extend(these_ref_rows);
}
};
@@ -2038,6 +2118,7 @@ impl EntityCrud for ReleaseEntity {
raw_name: c.raw_name.clone(),
index_val: c.index.map(|v| v as i32),
role: c.role.clone(),
+ raw_affiliation: c.raw_affiliation.clone(),
extra_json: c.extra.clone(),
})
})
@@ -2053,7 +2134,7 @@ impl EntityCrud for ReleaseEntity {
.iter()
.filter(|ea| ea.content.is_some())
.map(|c| AbstractsRow {
- sha1: Sha1::from(c.content.clone().unwrap()).hexdigest(),
+ sha1: Sha1::from(c.content.as_ref().unwrap()).hexdigest(),
content: c.content.clone().unwrap(),
})
.collect();
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index b7661334..d089adf8 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -9,6 +9,8 @@ extern crate log;
extern crate lazy_static;
#[macro_use]
extern crate failure;
+#[macro_use]
+extern crate serde_derive;
pub mod auth;
pub mod database_models;