aboutsummaryrefslogtreecommitdiffstats
path: root/rust/src/api_server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/api_server.rs')
-rw-r--r--rust/src/api_server.rs498
1 files changed, 253 insertions, 245 deletions
diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs
index 488dfc9b..cb4424c4 100644
--- a/rust/src/api_server.rs
+++ b/rust/src/api_server.rs
@@ -25,21 +25,37 @@ use uuid;
// Helper for calling through to handlers
macro_rules! wrap_entity_handlers {
- ($get_fn:ident, $get_handler:ident, $get_resp:ident) => {
+ ($get_fn:ident, $get_handler:ident, $get_resp:ident, $post_fn:ident, $post_handler:ident,
+ $post_resp:ident, $model:ident) => {
fn $get_fn(
&self,
id: String,
_context: &Context,
) -> Box<Future<Item = $get_resp, Error = ApiError> + Send> {
- let ret = match self.$get_handler(id) {
+ let ret = match self.$get_handler(id.clone()) {
Ok(Some(entity)) =>
$get_resp::FoundEntity(entity),
Ok(None) =>
- $get_resp::NotFound(ErrorResponse { message: "No such entity".to_string() }),
+ $get_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }),
Err(e) => $get_resp::BadRequest(ErrorResponse { message: e.to_string() }),
};
Box::new(futures::done(Ok(ret)))
}
+
+ fn $post_fn(
+ &self,
+ body: models::$model,
+ _context: &Context,
+ ) -> Box<Future<Item = $post_resp, Error = ApiError> + Send> {
+ // TODO: look for diesel foreign key and other type errors, return as BadRequest; other
+ // errors are a 500.
+ let ret = match self.$post_handler(body) {
+ Ok(edit) =>
+ $post_resp::CreatedEntity(edit),
+ Err(e) => $post_resp::BadRequest(ErrorResponse { message: e.to_string() }),
+ };
+ Box::new(futures::done(Ok(ret)))
+ }
}
}
macro_rules! wrap_lookup_handler {
@@ -429,190 +445,7 @@ impl Server {
Ok(Some(entity))
}
- fn editgroup_id_get_handler(&self, id: i64) -> Result<Option<Editgroup>> {
- let conn = self.db_pool.get().expect("db_pool error");
-
- let row: EditgroupRow = editgroup::table.find(id as i64).first(&conn)?;
-
- let edits = EditgroupEdits {
- containers: Some(
- container_edit::table
- .filter(container_edit::editgroup_id.eq(id))
- .get_results(&conn)
- .unwrap()
- .iter()
- .map(|e: &ContainerEditRow| EntityEdit {
- edit_id: e.id,
- editgroup_id: e.editgroup_id,
- revision: e.rev_id,
- redirect_ident: e.redirect_id.map(|v| v.to_string()),
- ident: e.ident_id.to_string(),
- extra: e.extra_json.clone(),
- })
- .collect(),
- ),
- creators: Some(
- creator_edit::table
- .filter(creator_edit::editgroup_id.eq(id))
- .get_results(&conn)
- .unwrap()
- .iter()
- .map(|e: &CreatorEditRow| EntityEdit {
- edit_id: e.id,
- editgroup_id: e.editgroup_id,
- revision: e.rev_id,
- redirect_ident: e.redirect_id.map(|v| v.to_string()),
- ident: e.ident_id.to_string(),
- extra: e.extra_json.clone(),
- })
- .collect(),
- ),
- files: Some(
- file_edit::table
- .filter(file_edit::editgroup_id.eq(id))
- .get_results(&conn)
- .unwrap()
- .iter()
- .map(|e: &FileEditRow| EntityEdit {
- edit_id: e.id,
- editgroup_id: e.editgroup_id,
- revision: e.rev_id,
- redirect_ident: e.redirect_id.map(|v| v.to_string()),
- ident: e.ident_id.to_string(),
- extra: e.extra_json.clone(),
- })
- .collect(),
- ),
- releases: Some(
- release_edit::table
- .filter(release_edit::editgroup_id.eq(id))
- .get_results(&conn)
- .unwrap()
- .iter()
- .map(|e: &ReleaseEditRow| EntityEdit {
- edit_id: e.id,
- editgroup_id: e.editgroup_id,
- revision: e.rev_id,
- redirect_ident: e.redirect_id.map(|v| v.to_string()),
- ident: e.ident_id.to_string(),
- extra: e.extra_json.clone(),
- })
- .collect(),
- ),
- works: Some(
- work_edit::table
- .filter(work_edit::editgroup_id.eq(id))
- .get_results(&conn)
- .unwrap()
- .iter()
- .map(|e: &WorkEditRow| EntityEdit {
- edit_id: e.id,
- editgroup_id: e.editgroup_id,
- revision: e.rev_id,
- redirect_ident: e.redirect_id.map(|v| v.to_string()),
- ident: e.ident_id.to_string(),
- extra: e.extra_json.clone(),
- })
- .collect(),
- ),
- };
-
- let eg = Editgroup {
- id: Some(row.id),
- editor_id: row.editor_id,
- description: row.description,
- edits: Some(edits),
- extra: row.extra_json,
- };
- Ok(Some(eg))
- }
-
- fn editor_get_handler(&self, username: String) -> Result<Option<Editor>> {
- let conn = self.db_pool.get().expect("db_pool error");
-
- let row: EditorRow = editor::table
- .filter(editor::username.eq(&username))
- .first(&conn)?;
-
- let ed = Editor {
- username: row.username,
- };
- Ok(Some(ed))
- }
-
- fn editor_changelog_get_handler(&self, username: String) -> Result<Option<Changelogentries>> {
- let conn = self.db_pool.get().expect("db_pool error");
-
- // TODO: single query
- let editor: EditorRow = editor::table
- .filter(editor::username.eq(username))
- .first(&conn)?;
- let changes: Vec<(ChangelogRow, EditgroupRow)> = changelog::table
- .inner_join(editgroup::table)
- .filter(editgroup::editor_id.eq(editor.id))
- .load(&conn)?;
-
- let entries = changes
- .iter()
- .map(|(row, _)| ChangelogentriesInner {
- index: row.id,
- editgroup_id: row.editgroup_id,
- timestamp: chrono::DateTime::from_utc(row.timestamp, chrono::Utc),
- })
- .collect();
- Ok(Some(entries))
- }
-}
-
-impl Api for Server {
- wrap_entity_handlers!(
- container_id_get,
- container_id_get_handler,
- ContainerIdGetResponse
- );
- wrap_entity_handlers!(
- creator_id_get,
- creator_id_get_handler,
- CreatorIdGetResponse
- );
- wrap_entity_handlers!(file_id_get, file_id_get_handler, FileIdGetResponse);
- wrap_entity_handlers!(work_id_get, work_id_get_handler, WorkIdGetResponse);
- wrap_entity_handlers!(release_id_get, release_id_get_handler, ReleaseIdGetResponse);
-
- wrap_lookup_handler!(
- container_lookup_get,
- container_lookup_get_handler,
- ContainerLookupGetResponse,
- issnl,
- String
- );
- wrap_lookup_handler!(
- creator_lookup_get,
- creator_lookup_get_handler,
- CreatorLookupGetResponse,
- orcid,
- String
- );
- wrap_lookup_handler!(
- file_lookup_get,
- file_lookup_get_handler,
- FileLookupGetResponse,
- sha1,
- String
- );
- wrap_lookup_handler!(
- release_lookup_get,
- release_lookup_get_handler,
- ReleaseLookupGetResponse,
- doi,
- String
- );
-
- fn container_post(
- &self,
- body: models::ContainerEntity,
- _context: &Context,
- ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError> + Send> {
+ fn container_post_handler(&self, body: models::ContainerEntity) -> Result<EntityEdit> {
let conn = self.db_pool.get().expect("db_pool error");
let editor_id = 1; // TODO: auth
let editgroup_id = match body.editgroup_id {
@@ -637,28 +470,20 @@ impl Api for Server {
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.coden)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(body.extra)
.bind::<diesel::sql_types::BigInt, _>(editgroup_id)
- .get_result(&conn)
- .unwrap();
+ .get_result(&conn)?;
let edit = &edit;
- let entity_edit = EntityEdit {
+ Ok(EntityEdit {
editgroup_id: edit.editgroup_id,
revision: Some(edit.rev_id.unwrap()),
redirect_ident: None,
ident: edit.ident_id.to_string(),
edit_id: edit.id,
extra: edit.extra_json.clone(),
- };
- Box::new(futures::done(Ok(ContainerPostResponse::CreatedEntity(
- entity_edit,
- ))))
+ })
}
- fn creator_post(
- &self,
- body: models::CreatorEntity,
- _context: &Context,
- ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError> + Send> {
+ fn creator_post_handler(&self, body: models::CreatorEntity) -> Result<EntityEdit> {
let conn = self.db_pool.get().expect("db_pool error");
let editor_id = 1; // TODO: auth
let editgroup_id = match body.editgroup_id {
@@ -680,28 +505,20 @@ impl Api for Server {
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.orcid)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(body.extra)
.bind::<diesel::sql_types::BigInt, _>(editgroup_id)
- .get_result(&conn)
- .unwrap();
+ .get_result(&conn)?;
let edit = &edit;
- let entity_edit = EntityEdit {
+ Ok(EntityEdit {
editgroup_id: edit.editgroup_id,
revision: Some(edit.rev_id.unwrap()),
redirect_ident: None,
ident: edit.ident_id.to_string(),
edit_id: edit.id,
extra: edit.extra_json.clone(),
- };
- Box::new(futures::done(Ok(CreatorPostResponse::CreatedEntity(
- entity_edit,
- ))))
+ })
}
- fn file_post(
- &self,
- body: models::FileEntity,
- _context: &Context,
- ) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send> {
+ fn file_post_handler(&self, body: models::FileEntity) -> Result<EntityEdit> {
let conn = self.db_pool.get().expect("db_pool error");
let editor_id = 1; // TODO: auth
let editgroup_id = match body.editgroup_id {
@@ -726,8 +543,7 @@ impl Api for Server {
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.url)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(body.extra)
.bind::<diesel::sql_types::BigInt, _>(editgroup_id)
- .get_result(&conn)
- .unwrap();
+ .get_result(&conn)?;
let edit = &edit;
let _releases: Option<Vec<FileReleaseRow>> = match body.releases {
@@ -752,24 +568,17 @@ impl Api for Server {
}
};
- let entity_edit = EntityEdit {
+ Ok(EntityEdit {
editgroup_id: edit.editgroup_id,
revision: Some(edit.rev_id.unwrap()),
redirect_ident: None,
ident: edit.ident_id.to_string(),
edit_id: edit.id,
extra: edit.extra_json.clone(),
- };
- Box::new(futures::done(Ok(FilePostResponse::CreatedEntity(
- entity_edit,
- ))))
+ })
}
- fn work_post(
- &self,
- body: models::WorkEntity,
- _context: &Context,
- ) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send> {
+ fn work_post_handler(&self, body: models::WorkEntity) -> Result<EntityEdit> {
let conn = self.db_pool.get().expect("db_pool error");
let editor_id = 1; // TODO: auth
let editgroup_id = match body.editgroup_id {
@@ -791,28 +600,20 @@ impl Api for Server {
).bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.work_type)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(body.extra)
.bind::<diesel::sql_types::BigInt, _>(editgroup_id)
- .get_result(&conn)
- .unwrap();
+ .get_result(&conn)?;
let edit = &edit;
- let entity_edit = EntityEdit {
+ Ok(EntityEdit {
editgroup_id: edit.editgroup_id,
revision: Some(edit.rev_id.unwrap()),
redirect_ident: None,
ident: edit.ident_id.to_string(),
edit_id: edit.id,
extra: edit.extra_json.clone(),
- };
- Box::new(futures::done(Ok(WorkPostResponse::CreatedEntity(
- entity_edit,
- ))))
+ })
}
- fn release_post(
- &self,
- body: models::ReleaseEntity,
- _context: &Context,
- ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send> {
+ fn release_post_handler(&self, body: models::ReleaseEntity) -> Result<EntityEdit> {
let conn = self.db_pool.get().expect("db_pool error");
let editor_id = 1; // TODO: auth
let editgroup_id = match body.editgroup_id {
@@ -838,7 +639,8 @@ impl Api for Server {
RETURNING *",
).bind::<diesel::sql_types::Text, _>(body.title)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.release_type)
- .bind::<diesel::sql_types::Nullable<diesel::sql_types::Date>, _>(body.date.map(|v| v.naive_utc().date()))
+ .bind::<diesel::sql_types::Nullable<diesel::sql_types::Date>, _>(
+ body.date.map(|v| v.naive_utc().date()))
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.doi)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.isbn13)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.volume)
@@ -849,8 +651,7 @@ impl Api for Server {
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(body.publisher)
.bind::<diesel::sql_types::Nullable<diesel::sql_types::Json>, _>(body.extra)
.bind::<diesel::sql_types::BigInt, _>(editgroup_id)
- .get_result(&conn)
- .unwrap();
+ .get_result(&conn)?;
let edit = &edit;
let _refs: Option<Vec<ReleaseRefRow>> = match body.refs {
@@ -906,19 +707,222 @@ impl Api for Server {
}
};
- let entity_edit = EntityEdit {
+ Ok(EntityEdit {
editgroup_id: edit.editgroup_id,
revision: Some(edit.rev_id.unwrap()),
redirect_ident: None,
ident: edit.ident_id.to_string(),
edit_id: edit.id,
extra: edit.extra_json.clone(),
+ })
+ }
+
+ fn editgroup_id_get_handler(&self, id: i64) -> Result<Option<Editgroup>> {
+ let conn = self.db_pool.get().expect("db_pool error");
+
+ let row: EditgroupRow = editgroup::table.find(id as i64).first(&conn)?;
+
+ let edits = EditgroupEdits {
+ containers: Some(
+ container_edit::table
+ .filter(container_edit::editgroup_id.eq(id))
+ .get_results(&conn)?
+ .iter()
+ .map(|e: &ContainerEditRow| EntityEdit {
+ edit_id: e.id,
+ editgroup_id: e.editgroup_id,
+ revision: e.rev_id,
+ redirect_ident: e.redirect_id.map(|v| v.to_string()),
+ ident: e.ident_id.to_string(),
+ extra: e.extra_json.clone(),
+ })
+ .collect(),
+ ),
+ creators: Some(
+ creator_edit::table
+ .filter(creator_edit::editgroup_id.eq(id))
+ .get_results(&conn)?
+ .iter()
+ .map(|e: &CreatorEditRow| EntityEdit {
+ edit_id: e.id,
+ editgroup_id: e.editgroup_id,
+ revision: e.rev_id,
+ redirect_ident: e.redirect_id.map(|v| v.to_string()),
+ ident: e.ident_id.to_string(),
+ extra: e.extra_json.clone(),
+ })
+ .collect(),
+ ),
+ files: Some(
+ file_edit::table
+ .filter(file_edit::editgroup_id.eq(id))
+ .get_results(&conn)?
+ .iter()
+ .map(|e: &FileEditRow| EntityEdit {
+ edit_id: e.id,
+ editgroup_id: e.editgroup_id,
+ revision: e.rev_id,
+ redirect_ident: e.redirect_id.map(|v| v.to_string()),
+ ident: e.ident_id.to_string(),
+ extra: e.extra_json.clone(),
+ })
+ .collect(),
+ ),
+ releases: Some(
+ release_edit::table
+ .filter(release_edit::editgroup_id.eq(id))
+ .get_results(&conn)?
+ .iter()
+ .map(|e: &ReleaseEditRow| EntityEdit {
+ edit_id: e.id,
+ editgroup_id: e.editgroup_id,
+ revision: e.rev_id,
+ redirect_ident: e.redirect_id.map(|v| v.to_string()),
+ ident: e.ident_id.to_string(),
+ extra: e.extra_json.clone(),
+ })
+ .collect(),
+ ),
+ works: Some(
+ work_edit::table
+ .filter(work_edit::editgroup_id.eq(id))
+ .get_results(&conn)?
+ .iter()
+ .map(|e: &WorkEditRow| EntityEdit {
+ edit_id: e.id,
+ editgroup_id: e.editgroup_id,
+ revision: e.rev_id,
+ redirect_ident: e.redirect_id.map(|v| v.to_string()),
+ ident: e.ident_id.to_string(),
+ extra: e.extra_json.clone(),
+ })
+ .collect(),
+ ),
};
- Box::new(futures::done(Ok(ReleasePostResponse::CreatedEntity(
- entity_edit,
- ))))
+
+ let eg = Editgroup {
+ id: Some(row.id),
+ editor_id: row.editor_id,
+ description: row.description,
+ edits: Some(edits),
+ extra: row.extra_json,
+ };
+ Ok(Some(eg))
}
+ fn editor_get_handler(&self, username: String) -> Result<Option<Editor>> {
+ let conn = self.db_pool.get().expect("db_pool error");
+
+ let row: EditorRow = editor::table
+ .filter(editor::username.eq(&username))
+ .first(&conn)?;
+
+ let ed = Editor {
+ username: row.username,
+ };
+ Ok(Some(ed))
+ }
+
+ fn editor_changelog_get_handler(&self, username: String) -> Result<Option<Changelogentries>> {
+ let conn = self.db_pool.get().expect("db_pool error");
+
+ // TODO: single query
+ let editor: EditorRow = editor::table
+ .filter(editor::username.eq(username))
+ .first(&conn)?;
+ let changes: Vec<(ChangelogRow, EditgroupRow)> = changelog::table
+ .inner_join(editgroup::table)
+ .filter(editgroup::editor_id.eq(editor.id))
+ .load(&conn)?;
+
+ let entries = changes
+ .iter()
+ .map(|(row, _)| ChangelogentriesInner {
+ index: row.id,
+ editgroup_id: row.editgroup_id,
+ timestamp: chrono::DateTime::from_utc(row.timestamp, chrono::Utc),
+ })
+ .collect();
+ Ok(Some(entries))
+ }
+}
+
+impl Api for Server {
+ wrap_entity_handlers!(
+ container_id_get,
+ container_id_get_handler,
+ ContainerIdGetResponse,
+ container_post,
+ container_post_handler,
+ ContainerPostResponse,
+ ContainerEntity
+ );
+ wrap_entity_handlers!(
+ creator_id_get,
+ creator_id_get_handler,
+ CreatorIdGetResponse,
+ creator_post,
+ creator_post_handler,
+ CreatorPostResponse,
+ CreatorEntity
+ );
+ wrap_entity_handlers!(
+ file_id_get,
+ file_id_get_handler,
+ FileIdGetResponse,
+ file_post,
+ file_post_handler,
+ FilePostResponse,
+ FileEntity
+ );
+ wrap_entity_handlers!(
+ release_id_get,
+ release_id_get_handler,
+ ReleaseIdGetResponse,
+ release_post,
+ release_post_handler,
+ ReleasePostResponse,
+ ReleaseEntity
+ );
+ wrap_entity_handlers!(
+ work_id_get,
+ work_id_get_handler,
+ WorkIdGetResponse,
+ work_post,
+ work_post_handler,
+ WorkPostResponse,
+ WorkEntity
+ );
+
+ wrap_lookup_handler!(
+ container_lookup_get,
+ container_lookup_get_handler,
+ ContainerLookupGetResponse,
+ issnl,
+ String
+ );
+ wrap_lookup_handler!(
+ creator_lookup_get,
+ creator_lookup_get_handler,
+ CreatorLookupGetResponse,
+ orcid,
+ String
+ );
+ wrap_lookup_handler!(
+ file_lookup_get,
+ file_lookup_get_handler,
+ FileLookupGetResponse,
+ sha1,
+ String
+ );
+ wrap_lookup_handler!(
+ release_lookup_get,
+ release_lookup_get_handler,
+ ReleaseLookupGetResponse,
+ doi,
+ String
+ );
+
fn editgroup_id_accept_post(
&self,
id: i64,
@@ -928,7 +932,9 @@ impl Api for Server {
accept_editgroup(id as i64, &conn).expect("failed to accept editgroup");
- let ret = EditgroupIdAcceptPostResponse::MergedSuccessfully(Success { message: "horray!".to_string()});
+ let ret = EditgroupIdAcceptPostResponse::MergedSuccessfully(Success {
+ message: "horray!".to_string(),
+ });
Box::new(futures::done(Ok(ret)))
}
@@ -988,10 +994,12 @@ impl Api for Server {
Ok(Some(entries)) =>
EditorUsernameChangelogGetResponse::FoundMergedChanges(entries),
Ok(None) =>
- EditorUsernameChangelogGetResponse::NotFound(ErrorResponse { message: "No such entity".to_string() }),
+ EditorUsernameChangelogGetResponse::NotFound(
+ ErrorResponse { message: "No such entity".to_string() }),
Err(e) =>
// TODO: dig in to error type here
- EditorUsernameChangelogGetResponse::GenericError(ErrorResponse { message: e.to_string() }),
+ EditorUsernameChangelogGetResponse::GenericError(
+ ErrorResponse { message: e.to_string() }),
};
Box::new(futures::done(Ok(ret)))
}