aboutsummaryrefslogtreecommitdiffstats
path: root/rust/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/bin/fatcatd.rs3
-rw-r--r--rust/src/endpoint_handlers.rs194
-rw-r--r--rust/src/endpoints.rs212
-rw-r--r--rust/src/entity_crud.rs38
-rw-r--r--rust/src/identifiers.rs87
5 files changed, 438 insertions, 96 deletions
diff --git a/rust/src/bin/fatcatd.rs b/rust/src/bin/fatcatd.rs
index 6f5610f0..b27ff911 100644
--- a/rust/src/bin/fatcatd.rs
+++ b/rust/src/bin/fatcatd.rs
@@ -35,8 +35,7 @@ impl AfterMiddleware for XClacksOverheadMiddleware {
/// Create custom server, wire it to the autogenerated router,
/// and pass it to the web server.
fn main() -> Result<()> {
- let _matches = App::new("server")
- .get_matches();
+ let _matches = App::new("server").get_matches();
dotenv::dotenv().ok();
diff --git a/rust/src/endpoint_handlers.rs b/rust/src/endpoint_handlers.rs
index 64b6ed62..91ea2393 100644
--- a/rust/src/endpoint_handlers.rs
+++ b/rust/src/endpoint_handlers.rs
@@ -26,7 +26,6 @@ macro_rules! entity_auto_batch_handler {
entity_list: &[models::$model],
editor_id: FatcatId,
) -> Result<Editgroup> {
-
let editgroup_row = editgroup.db_create(conn, true)?;
let editgroup_id = FatcatId::from_uuid(&editgroup_row.id);
let edit_context = make_edit_context(editor_id, editgroup_id, true)?;
@@ -39,7 +38,7 @@ macro_rules! entity_auto_batch_handler {
.get_result(conn)?;
self.get_editgroup_handler(conn, editgroup_id)
}
- }
+ };
}
pub fn get_release_files(
@@ -262,6 +261,9 @@ impl Server {
jstor: &Option<String>,
ark: &Option<String>,
mag: &Option<String>,
+ doaj: &Option<String>,
+ dblp: &Option<String>,
+ oai: &Option<String>,
expand_flags: ExpandFlags,
hide_flags: HideFlags,
) -> Result<ReleaseEntity> {
@@ -276,8 +278,11 @@ impl Server {
jstor,
ark,
mag,
+ doaj,
+ dblp,
+ oai,
) {
- (Some(doi), None, None, None, None, None, None, None, None, None) => {
+ (Some(doi), None, None, None, None, None, None, None, None, None, None, None, None) => {
// DOIs always stored lower-case; lookups are case-insensitive
let doi = doi.to_lowercase();
check_doi(&doi)?;
@@ -288,7 +293,21 @@ impl Server {
.filter(release_ident::redirect_id.is_null())
.first(conn)?
}
- (None, Some(wikidata_qid), None, None, None, None, None, None, None, None) => {
+ (
+ None,
+ Some(wikidata_qid),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
check_wikidata_qid(wikidata_qid)?;
release_ident::table
.inner_join(release_rev::table)
@@ -297,7 +316,21 @@ impl Server {
.filter(release_ident::redirect_id.is_null())
.first(conn)?
}
- (None, None, Some(isbn13), None, None, None, None, None, None, None) => {
+ (
+ None,
+ None,
+ Some(isbn13),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
check_isbn13(isbn13)?;
let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
release_rev::table
@@ -310,7 +343,21 @@ impl Server {
.first(conn)?;
(ident, rev)
}
- (None, None, None, Some(pmid), None, None, None, None, None, None) => {
+ (
+ None,
+ None,
+ None,
+ Some(pmid),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
check_pmid(pmid)?;
release_ident::table
.inner_join(release_rev::table)
@@ -319,7 +366,21 @@ impl Server {
.filter(release_ident::redirect_id.is_null())
.first(conn)?
}
- (None, None, None, None, Some(pmcid), None, None, None, None, None) => {
+ (
+ None,
+ None,
+ None,
+ None,
+ Some(pmcid),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
check_pmcid(pmcid)?;
release_ident::table
.inner_join(release_rev::table)
@@ -328,7 +389,21 @@ impl Server {
.filter(release_ident::redirect_id.is_null())
.first(conn)?
}
- (None, None, None, None, None, Some(core), None, None, None, None) => {
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(core),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
check_core_id(core)?;
release_ident::table
.inner_join(release_rev::table)
@@ -337,7 +412,21 @@ impl Server {
.filter(release_ident::redirect_id.is_null())
.first(conn)?
}
- (None, None, None, None, None, None, Some(arxiv), None, None, None) => {
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(arxiv),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
// TODO: this allows only lookup by full, versioned arxiv identifier. Probably also
// want to allow lookup by "work" style identifier?
check_arxiv_id(arxiv)?;
@@ -352,7 +441,21 @@ impl Server {
.first(conn)?;
(ident, rev)
}
- (None, None, None, None, None, None, None, Some(jstor), None, None) => {
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(jstor),
+ None,
+ None,
+ None,
+ None,
+ None,
+ ) => {
check_jstor_id(jstor)?;
let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
release_rev::table
@@ -365,7 +468,7 @@ impl Server {
.first(conn)?;
(ident, rev)
}
- (None, None, None, None, None, None, None, None, Some(ark), None) => {
+ (None, None, None, None, None, None, None, None, Some(ark), None, None, None, None) => {
check_ark_id(ark)?;
let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
release_rev::table
@@ -378,7 +481,7 @@ impl Server {
.first(conn)?;
(ident, rev)
}
- (None, None, None, None, None, None, None, None, None, Some(mag)) => {
+ (None, None, None, None, None, None, None, None, None, Some(mag), None, None, None) => {
check_mag_id(mag)?;
let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
release_rev::table
@@ -391,6 +494,73 @@ impl Server {
.first(conn)?;
(ident, rev)
}
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(doaj),
+ None,
+ None,
+ ) => {
+ check_doaj_id(doaj)?;
+ let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
+ release_rev::table
+ .inner_join(release_ident::table)
+ .inner_join(release_rev_extid::table)
+ .filter(release_rev_extid::extid_type.eq("doaj".to_string()))
+ .filter(release_rev_extid::value.eq(doaj))
+ .filter(release_ident::is_live.eq(true))
+ .filter(release_ident::redirect_id.is_null())
+ .first(conn)?;
+ (ident, rev)
+ }
+ (
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(dblp),
+ None,
+ ) => {
+ check_dblp_id(dblp)?;
+ let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
+ release_rev::table
+ .inner_join(release_ident::table)
+ .inner_join(release_rev_extid::table)
+ .filter(release_rev_extid::extid_type.eq("dblp".to_string()))
+ .filter(release_rev_extid::value.eq(dblp))
+ .filter(release_ident::is_live.eq(true))
+ .filter(release_ident::redirect_id.is_null())
+ .first(conn)?;
+ (ident, rev)
+ }
+ (None, None, None, None, None, None, None, None, None, None, None, None, Some(oai)) => {
+ check_oai_id(oai)?;
+ let (rev, ident, _extid): (ReleaseRevRow, ReleaseIdentRow, ReleaseExtidRow) =
+ release_rev::table
+ .inner_join(release_ident::table)
+ .inner_join(release_rev_extid::table)
+ .filter(release_rev_extid::extid_type.eq("oai".to_string()))
+ .filter(release_rev_extid::value.eq(oai))
+ .filter(release_ident::is_live.eq(true))
+ .filter(release_ident::redirect_id.is_null())
+ .first(conn)?;
+ (ident, rev)
+ }
_ => {
return Err(
FatcatError::MissingOrMultipleExternalId("in lookup".to_string()).into(),
diff --git a/rust/src/endpoints.rs b/rust/src/endpoints.rs
index 0dd69efd..0dd232c6 100644
--- a/rust/src/endpoints.rs
+++ b/rust/src/endpoints.rs
@@ -76,7 +76,6 @@ macro_rules! wrap_entity_handlers {
$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,
ident: String,
@@ -99,11 +98,12 @@ macro_rules! wrap_entity_handlers {
let mut entity = $model::db_get(&conn, entity_id, hide_flags)?;
entity.db_expand(&conn, expand_flags)?;
Ok(entity)
- },
+ }
}
- })().map_err(|e| FatcatError::from(e)) {
- Ok(entity) =>
- $get_resp::FoundEntity(entity),
+ })()
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(entity) => $get_resp::FoundEntity(entity),
Err(fe) => generic_err_responses!(fe, $get_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -116,19 +116,27 @@ macro_rules! wrap_entity_handlers {
context: &Context,
) -> Box<dyn Future<Item = $post_resp, Error = ApiError> + Send> {
let conn = self.db_pool.get().expect("db_pool error");
- let ret = match conn.transaction(|| {
- let editgroup_id = FatcatId::from_str(&editgroup_id)?;
- 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(auth_context.editor_id, editgroup_id, false)?;
- edit_context.check(&conn)?;
- entity.db_create(&conn, &edit_context)?.into_model()
- }).map_err(|e| FatcatError::from(e)) {
+ let ret = match conn
+ .transaction(|| {
+ let editgroup_id = FatcatId::from_str(&editgroup_id)?;
+ 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(auth_context.editor_id, editgroup_id, false)?;
+ edit_context.check(&conn)?;
+ entity.db_create(&conn, &edit_context)?.into_model()
+ })
+ .map_err(|e| FatcatError::from(e))
+ {
Ok(edit) => {
self.metrics.incr("entities.created").ok();
$post_resp::CreatedEntity(edit)
- },
+ }
Err(fe) => generic_auth_err_responses!(fe, $post_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -141,7 +149,11 @@ macro_rules! wrap_entity_handlers {
) -> Box<dyn Future<Item = $post_auto_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_auto_batch_fn)))?;
+ let auth_context = self.auth_confectionary.require_auth(
+ &conn,
+ &context.auth_data,
+ Some(stringify!($post_auto_batch_fn)),
+ )?;
auth_context.require_role(FatcatRole::Admin)?;
let mut editgroup = auto_batch.editgroup.clone();
// TODO: this is duplicated code with create_editgroup()
@@ -151,16 +163,21 @@ macro_rules! wrap_entity_handlers {
&& !auth_context.has_role(FatcatRole::Admin)
{
return Err(FatcatError::InsufficientPrivileges(
- "not authorized to create editgroups in others' names".to_string()
- ))
+ "not authorized to create editgroups in others' names".to_string(),
+ ));
}
}
None => {
editgroup.editor_id = Some(auth_context.editor_id.to_string());
}
};
- self.$post_auto_batch_handler(&conn, editgroup, &auto_batch.entity_list, auth_context.editor_id)
- .map_err(|e| FatcatError::from(e))
+ self.$post_auto_batch_handler(
+ &conn,
+ editgroup,
+ &auto_batch.entity_list,
+ auth_context.editor_id,
+ )
+ .map_err(|e| FatcatError::from(e))
}) {
Ok(editgroup) => {
// TODO: need a count helper on editgroup
@@ -168,7 +185,7 @@ macro_rules! wrap_entity_handlers {
self.metrics.incr("editgroup.created").ok();
self.metrics.incr("editgroup.accepted").ok();
$post_auto_batch_resp::CreatedEditgroup(editgroup)
- },
+ }
Err(fe) => generic_auth_err_responses!(fe, $post_auto_batch_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -182,20 +199,30 @@ macro_rules! wrap_entity_handlers {
context: &Context,
) -> Box<dyn Future<Item = $update_resp, Error = ApiError> + Send> {
let conn = self.db_pool.get().expect("db_pool error");
- let ret = match conn.transaction(|| {
- let editgroup_id = FatcatId::from_str(&editgroup_id)?;
- let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($update_fn)))?;
- 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(auth_context.editor_id, editgroup_id, false)?;
- edit_context.check(&conn)?;
- entity.db_update(&conn, &edit_context, entity_id)?.into_model()
- }).map_err(|e| FatcatError::from(e)) {
+ let ret = match conn
+ .transaction(|| {
+ let editgroup_id = FatcatId::from_str(&editgroup_id)?;
+ let auth_context = self.auth_confectionary.require_auth(
+ &conn,
+ &context.auth_data,
+ Some(stringify!($update_fn)),
+ )?;
+ 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(auth_context.editor_id, editgroup_id, false)?;
+ edit_context.check(&conn)?;
+ entity
+ .db_update(&conn, &edit_context, entity_id)?
+ .into_model()
+ })
+ .map_err(|e| FatcatError::from(e))
+ {
Ok(edit) => {
self.metrics.incr("entities.updated").ok();
$update_resp::UpdatedEntity(edit)
- },
+ }
Err(fe) => generic_auth_err_responses!(fe, $update_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -208,20 +235,28 @@ macro_rules! wrap_entity_handlers {
context: &Context,
) -> Box<dyn Future<Item = $delete_resp, Error = ApiError> + Send> {
let conn = self.db_pool.get().expect("db_pool error");
- let ret = match conn.transaction(|| {
- let editgroup_id = FatcatId::from_str(&editgroup_id)?;
- let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($delete_fn)))?;
- 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(auth_context.editor_id, editgroup_id, false)?;
- edit_context.check(&conn)?;
- $model::db_delete(&conn, &edit_context, entity_id)?.into_model()
- }).map_err(|e| FatcatError::from(e)) {
+ let ret = match conn
+ .transaction(|| {
+ let editgroup_id = FatcatId::from_str(&editgroup_id)?;
+ let auth_context = self.auth_confectionary.require_auth(
+ &conn,
+ &context.auth_data,
+ Some(stringify!($delete_fn)),
+ )?;
+ 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(auth_context.editor_id, editgroup_id, false)?;
+ edit_context.check(&conn)?;
+ $model::db_delete(&conn, &edit_context, entity_id)?.into_model()
+ })
+ .map_err(|e| FatcatError::from(e))
+ {
Ok(edit) => {
self.metrics.incr("entities.deleted").ok();
$delete_resp::DeletedEntity(edit)
- },
+ }
Err(fe) => generic_auth_err_responses!(fe, $delete_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -238,9 +273,10 @@ macro_rules! wrap_entity_handlers {
let ret = match (|| {
let entity_id = FatcatId::from_str(&ident)?;
$model::db_get_history(&conn, entity_id, limit)
- })().map_err(|e| FatcatError::from(e)) {
- Ok(history) =>
- $get_history_resp::FoundEntityHistory(history),
+ })()
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(history) => $get_history_resp::FoundEntityHistory(history),
Err(fe) => generic_err_responses!(fe, $get_history_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -268,11 +304,12 @@ macro_rules! wrap_entity_handlers {
let mut entity = $model::db_get_rev(&conn, rev_id, hide_flags)?;
entity.db_expand(&conn, expand_flags)?;
Ok(entity)
- },
+ }
}
- })().map_err(|e| FatcatError::from(e)) {
- Ok(entity) =>
- $get_rev_resp::FoundEntityRevision(entity),
+ })()
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(entity) => $get_rev_resp::FoundEntityRevision(entity),
Err(fe) => generic_err_responses!(fe, $get_rev_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -288,9 +325,10 @@ macro_rules! wrap_entity_handlers {
let ret = match (|| {
let edit_id = Uuid::from_str(&edit_id)?;
$model::db_get_edit(&conn, edit_id)?.into_model()
- })().map_err(|e| FatcatError::from(e)) {
- Ok(edit) =>
- $get_edit_resp::FoundEdit(edit),
+ })()
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(edit) => $get_edit_resp::FoundEdit(edit),
Err(fe) => generic_err_responses!(fe, $get_edit_resp),
};
Box::new(futures::done(Ok(ret)))
@@ -306,23 +344,29 @@ macro_rules! wrap_entity_handlers {
let ret = match conn.transaction(|| {
let editgroup_id = FatcatId::from_str(&editgroup_id)?;
let edit_id = Uuid::from_str(&edit_id)?;
- let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($delete_edit_fn)))?;
+ let auth_context = self.auth_confectionary.require_auth(
+ &conn,
+ &context.auth_data,
+ Some(stringify!($delete_edit_fn)),
+ )?;
auth_context.require_role(FatcatRole::Editor)?;
let edit = $model::db_get_edit(&conn, edit_id)?;
if !(edit.editgroup_id == editgroup_id.to_uuid()) {
return Err(FatcatError::BadRequest(
- "editgroup_id parameter didn't match that of the edit".to_string()
- ))
+ "editgroup_id parameter didn't match that of the edit".to_string(),
+ ));
}
auth_context.require_editgroup(&conn, editgroup_id)?;
// check for editgroup being deleted happens in db_delete_edit()
- $model::db_delete_edit(&conn, edit_id)
- .map_err(|e| FatcatError::from(e))
+ $model::db_delete_edit(&conn, edit_id).map_err(|e| FatcatError::from(e))
}) {
- Ok(()) =>
- $delete_edit_resp::DeletedEdit(Success {
- success: true,
- message: format!("Successfully deleted work-in-progress {} edit: {}", stringify!($model), edit_id)
+ Ok(()) => $delete_edit_resp::DeletedEdit(Success {
+ success: true,
+ message: format!(
+ "Successfully deleted work-in-progress {} edit: {}",
+ stringify!($model),
+ edit_id
+ ),
}),
Err(fe) => generic_auth_err_responses!(fe, $delete_edit_resp),
};
@@ -340,15 +384,15 @@ macro_rules! wrap_entity_handlers {
let entity_id = FatcatId::from_str(&ident)?;
let redirects: Vec<FatcatId> = $model::db_get_redirects(&conn, entity_id)?;
Ok(redirects.into_iter().map(|fcid| fcid.to_string()).collect())
- })().map_err(|e: Error| FatcatError::from(e)) {
- Ok(redirects) =>
- $get_redirects_resp::FoundEntityRedirects(redirects),
+ })()
+ .map_err(|e: Error| FatcatError::from(e))
+ {
+ Ok(redirects) => $get_redirects_resp::FoundEntityRedirects(redirects),
Err(fe) => generic_err_responses!(fe, $get_redirects_resp),
};
Box::new(futures::done(Ok(ret)))
}
-
- }
+ };
}
macro_rules! wrap_lookup_handler {
@@ -371,14 +415,16 @@ macro_rules! wrap_lookup_handler {
Some(param) => HideFlags::from_str(&param).unwrap(),
};
// No transaction for GET
- let ret = match self.$get_handler(&conn, &$idname, &wikidata_qid, expand_flags, hide_flags).map_err(|e| FatcatError::from(e)) {
- Ok(entity) =>
- $get_resp::FoundEntity(entity),
+ let ret = match self
+ .$get_handler(&conn, &$idname, &wikidata_qid, expand_flags, hide_flags)
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(entity) => $get_resp::FoundEntity(entity),
Err(fe) => generic_err_responses!(fe, $get_resp),
};
Box::new(futures::done(Ok(ret)))
}
- }
+ };
}
macro_rules! wrap_fcid_handler {
@@ -393,14 +439,15 @@ macro_rules! wrap_fcid_handler {
let ret = match (|| {
let fcid = FatcatId::from_str(&id)?;
self.$get_handler(&conn, fcid)
- })().map_err(|e| FatcatError::from(e)) {
- Ok(entity) =>
- $get_resp::Found(entity),
+ })()
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(entity) => $get_resp::Found(entity),
Err(fe) => generic_err_responses!(fe, $get_resp),
};
Box::new(futures::done(Ok(ret)))
}
- }
+ };
}
macro_rules! wrap_fcid_hide_handler {
@@ -420,14 +467,15 @@ macro_rules! wrap_fcid_hide_handler {
Some(param) => HideFlags::from_str(&param)?,
};
self.$get_handler(&conn, fcid, hide_flags)
- })().map_err(|e| FatcatError::from(e)) {
- Ok(entity) =>
- $get_resp::Found(entity),
+ })()
+ .map_err(|e| FatcatError::from(e))
+ {
+ Ok(entity) => $get_resp::Found(entity),
Err(fe) => generic_err_responses!(fe, $get_resp),
};
Box::new(futures::done(Ok(ret)))
}
- }
+ };
}
impl Api for Server {
@@ -689,6 +737,9 @@ impl Api for Server {
jstor: Option<String>,
ark: Option<String>,
mag: Option<String>,
+ doaj: Option<String>,
+ dblp: Option<String>,
+ oai: Option<String>,
expand: Option<String>,
hide: Option<String>,
_context: &Context,
@@ -716,6 +767,9 @@ impl Api for Server {
&jstor,
&ark,
&mag,
+ &doaj,
+ &dblp,
+ &oai,
expand_flags,
hide_flags,
)
diff --git a/rust/src/entity_crud.rs b/rust/src/entity_crud.rs
index 83dd26c9..0d72788d 100644
--- a/rust/src/entity_crud.rs
+++ b/rust/src/entity_crud.rs
@@ -334,7 +334,9 @@ macro_rules! generic_db_create {
fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result<Self::EditRow> {
if self.redirect.is_some() {
return Err(FatcatError::BadRequest(
- "can't create an entity that redirects from the start".to_string()).into());
+ "can't create an entity that redirects from the start".to_string(),
+ )
+ .into());
}
let rev_id = self.db_insert_rev(conn)?;
let ident: Uuid = insert_into($ident_table::table)
@@ -351,7 +353,7 @@ macro_rules! generic_db_create {
.get_result(conn)?;
Ok(edit)
}
- }
+ };
}
macro_rules! generic_db_create_batch {
@@ -764,7 +766,7 @@ macro_rules! generic_db_insert_rev {
fn db_insert_rev(&self, conn: &DbConn) -> Result<Uuid> {
Self::db_insert_revs(conn, &[self]).map(|id_list| id_list[0])
}
- }
+ };
}
impl EntityCrud for ContainerEntity {
@@ -1742,6 +1744,9 @@ impl EntityCrud for ReleaseEntity {
jstor: None,
ark: None,
mag: None,
+ doaj: None,
+ dblp: None,
+ oai: None,
},
refs: None,
contribs: None,
@@ -2018,6 +2023,9 @@ impl EntityCrud for ReleaseEntity {
jstor: None,
ark: None,
mag: None,
+ doaj: None,
+ dblp: None,
+ oai: None,
};
let extid_rows: Vec<ReleaseExtidRow> = release_rev_extid::table
@@ -2030,6 +2038,9 @@ impl EntityCrud for ReleaseEntity {
"jstor" => ext_ids.jstor = Some(extid_row.value),
"ark" => ext_ids.ark = Some(extid_row.value),
"mag" => ext_ids.mag = Some(extid_row.value),
+ "doaj" => ext_ids.doaj = Some(extid_row.value),
+ "dblp" => ext_ids.dblp = Some(extid_row.value),
+ "oai" => ext_ids.oai = Some(extid_row.value),
_ => (),
}
}
@@ -2290,6 +2301,27 @@ impl EntityCrud for ReleaseEntity {
value: extid.clone(),
});
};
+ if let Some(extid) = &model.ext_ids.doaj {
+ release_extid_rows.push(ReleaseExtidRow {
+ release_rev: *rev_id,
+ extid_type: "doaj".to_string(),
+ value: extid.clone(),
+ });
+ };
+ if let Some(extid) = &model.ext_ids.dblp {
+ release_extid_rows.push(ReleaseExtidRow {
+ release_rev: *rev_id,
+ extid_type: "dblp".to_string(),
+ value: extid.clone(),
+ });
+ };
+ if let Some(extid) = &model.ext_ids.oai {
+ release_extid_rows.push(ReleaseExtidRow {
+ release_rev: *rev_id,
+ extid_type: "oai".to_string(),
+ value: extid.clone(),
+ });
+ };
}
for (model, rev_id) in models.iter().zip(rev_ids.iter()) {
diff --git a/rust/src/identifiers.rs b/rust/src/identifiers.rs
index 180dc43b..76f978f9 100644
--- a/rust/src/identifiers.rs
+++ b/rust/src/identifiers.rs
@@ -362,6 +362,93 @@ fn test_check_isbn13() {
assert!(check_isbn13("9781566199094").is_err());
}
+pub fn check_doaj_id(raw: &str) -> Result<()> {
+ lazy_static! {
+ static ref RE: Regex = Regex::new(r"^[a-f0-9]{32}$").unwrap();
+ }
+ if raw.is_ascii() && RE.is_match(raw) {
+ Ok(())
+ } else {
+ Err(FatcatError::MalformedChecksum(
+ "DOAJ Article Identifier (expected, eg, 'e58f08a11ecb495ead55a44ad4f89808')"
+ .to_string(),
+ raw.to_string(),
+ ))?
+ }
+}
+
+#[test]
+fn test_check_doaj_id() {
+ assert!(check_doaj_id("e58f08a11ecb495ead55a44ad4f89808").is_ok());
+ assert!(check_doaj_id("1b39813549077b2347c0f370c3864b40").is_ok());
+ assert!(check_doaj_id("1b39813549077b2347c0f370c3864b40 ").is_err());
+ assert!(check_doaj_id("1g39813549077b2347c0f370c3864b40").is_err());
+ assert!(check_doaj_id("1B39813549077B2347C0F370c3864b40").is_err());
+ assert!(check_doaj_id("1b39813549077b2347c0f370c3864b4").is_err());
+ assert!(check_doaj_id("1b39813549077b2347c0f370c3864b411").is_err());
+}
+
+pub fn check_dblp_id(raw: &str) -> Result<()> {
+ lazy_static! {
+ // TODO: what should this actually be? more or less restrictive?
+ static ref RE: Regex = Regex::new(r"^[a-z]+/[a-zA-Z0-9]+/[a-zA-Z0-9/]+$").unwrap();
+ }
+ if raw.is_ascii() && RE.is_match(raw) {
+ Ok(())
+ } else {
+ Err(FatcatError::MalformedChecksum(
+ "dblp Article Key (expected, eg, 'journals/entcs/GoubaultM12')".to_string(),
+ raw.to_string(),
+ ))?
+ }
+}
+
+#[test]
+fn test_check_dblp_id() {
+ assert!(check_dblp_id("journals/entcs/GoubaultM12").is_ok());
+ assert!(check_dblp_id("journals/entcs/GoubaultM12").is_ok());
+ assert!(check_dblp_id("10.123*").is_err());
+ assert!(check_dblp_id("").is_err());
+}
+
+pub fn check_oai_id(raw: &str) -> Result<()> {
+ lazy_static! {
+ // http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm
+ static ref RE: Regex = Regex::new(r"^oai:[a-zA-Z][a-zA-Z0-9\-]*(\.[a-zA-Z][a-zA-Z0-9\-]*)+:[a-zA-Z0-9\-_\.!~\*'\(\);/\?:@&=\+$,%]+$").unwrap();
+ }
+ if raw.is_ascii() && RE.is_match(raw) {
+ Ok(())
+ } else {
+ Err(FatcatError::MalformedChecksum(
+ "OAI-PMH identifier (expected, eg, 'oai:foo.org:some-local-id-54')".to_string(),
+ raw.to_string(),
+ ))?
+ }
+}
+
+#[test]
+fn test_check_oai_id() {
+ assert!(check_oai_id("journals/entcs/GoubaultM12").is_err());
+ assert!(check_oai_id("10.123*").is_err());
+ assert!(check_oai_id("").is_err());
+ assert!(check_oai_id("something:arXiv.org:hep-th/9901001").is_err()); // bad schema
+ assert!(check_oai_id("oai:999:abc123").is_err()); // namespace-identifier must not start with digit
+ assert!(check_oai_id("oai:wibble:abc123").is_err()); // namespace-identifier must be domain name
+ assert!(check_oai_id("oai:wibble.org:ab cd").is_err()); // space not permitted (must be escaped as %20)
+ assert!(check_oai_id("oai:wibble.org:ab#cd").is_err()); // # not permitted
+ assert!(check_oai_id("oai:wibble.org:ab<cd").is_err()); // < not permitted
+ // the "official" regex used above allows this case
+ //assert!(check_oai_id("oai:wibble.org:ab%3ccd").is_err()); // < must be escaped at %3C not %3c
+
+ assert!(check_oai_id("oai:arXiv.org:hep-th/9901001").is_ok());
+ assert!(check_oai_id("oai:foo.org:some-local-id-53").is_ok());
+ assert!(check_oai_id("oai:FOO.ORG:some-local-id-53").is_ok());
+ assert!(check_oai_id("oai:foo.org:some-local-id-54").is_ok());
+ assert!(check_oai_id("oai:foo.org:Some-Local-Id-54").is_ok());
+ assert!(check_oai_id("oai:wibble.org:ab%20cd").is_ok());
+ assert!(check_oai_id("oai:wibble.org:ab?cd").is_ok());
+}
+
pub fn check_issn(raw: &str) -> Result<()> {
lazy_static! {
static ref RE: Regex = Regex::new(r"^\d{4}-\d{3}[0-9X]$").unwrap();