diff options
-rw-r--r-- | rust/src/api_server.rs | 498 |
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))) } |