From bcb9d2c6793b39b165caf9e63c4803d2a28e9876 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Sun, 27 May 2018 15:45:03 -0700 Subject: batch POST methods --- rust/src/api_server.rs | 199 ++++++++++++++++++++++++++++++++++++------------- rust/src/lib.rs | 1 + 2 files changed, 150 insertions(+), 50 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index bde73da5..883b3ae4 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -13,13 +13,7 @@ use diesel::{self, insert_into}; use errors::*; use fatcat_api::models; use fatcat_api::models::*; -use fatcat_api::{Api, ApiError, ContainerIdGetResponse, ContainerLookupGetResponse, - ContainerPostResponse, Context, CreatorIdGetResponse, CreatorLookupGetResponse, - CreatorPostResponse, EditgroupIdAcceptPostResponse, EditgroupIdGetResponse, - EditgroupPostResponse, EditorUsernameChangelogGetResponse, - EditorUsernameGetResponse, FileIdGetResponse, FileLookupGetResponse, - FilePostResponse, ReleaseIdGetResponse, ReleaseLookupGetResponse, - ReleasePostResponse, WorkIdGetResponse, WorkPostResponse}; +use fatcat_api::*; use futures::{self, Future}; use uuid; @@ -34,7 +28,8 @@ macro_rules! wrap_entity_handlers { // in the context of defining new functions. // The only stable approach I know of would be: https://github.com/dtolnay/mashup ($get_fn:ident, $get_handler:ident, $get_resp:ident, $post_fn:ident, $post_handler:ident, - $post_resp:ident, $model:ident) => { + $post_resp:ident, $post_batch_fn:ident, $post_batch_handler:ident, + $post_batch_resp:ident, $model:ident) => { fn $get_fn( &self, id: String, @@ -57,7 +52,7 @@ macro_rules! wrap_entity_handlers { entity: models::$model, _context: &Context, ) -> Box + Send> { - let ret = match self.$post_handler(entity) { + let ret = match self.$post_handler(entity, None) { Ok(edit) => $post_resp::CreatedEntity(edit), Err(Error(ErrorKind::Diesel(e), _)) => @@ -66,8 +61,38 @@ macro_rules! wrap_entity_handlers { }; Box::new(futures::done(Ok(ret))) } + + fn $post_batch_fn( + &self, + entity_list: &Vec, + _context: &Context, + ) -> Box + Send> { + let ret = match self.$post_batch_handler(entity_list) { + Ok(edit) => + $post_batch_resp::CreatedEntities(edit), + Err(Error(ErrorKind::Diesel(e), _)) => + $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => $post_batch_resp::GenericError(ErrorResponse { message: e.to_string() }), + }; + Box::new(futures::done(Ok(ret))) + } + } +} + +macro_rules! entity_batch_post_handler { + ($post_handler:ident, $post_batch_handler:ident, $model:ident) => { + fn $post_batch_handler(&self, entity_list: &Vec) -> Result> { + let conn = self.db_pool.get().expect("db_pool error"); + // TODO: start a transaction + let mut ret: Vec = vec![]; + for entity in entity_list.into_iter() { + ret.push(self.$post_handler(entity.clone(), Some(&conn))?); + } + Ok(ret) + } } } + macro_rules! wrap_lookup_handler { ($get_fn:ident, $get_handler:ident, $get_resp:ident, $idname:ident, $idtype:ident) => { fn $get_fn( @@ -374,11 +399,20 @@ impl Server { work_row2entity(Some(ident), rev) } - fn container_post_handler(&self, entity: models::ContainerEntity) -> Result { - let conn = self.db_pool.get().expect("db_pool error"); + fn container_post_handler(&self, entity: models::ContainerEntity, conn: Option<&DbConn>) -> Result { + // TODO: still can't cast for some reason + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), + None => get_or_create_editgroup(editor_id, &conn)?, Some(param) => param as i64, }; @@ -399,13 +433,38 @@ impl Server { .bind::, _>(entity.coden) .bind::, _>(entity.extra) .bind::(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; edit.to_model() } - fn creator_post_handler(&self, entity: models::CreatorEntity) -> Result { + entity_batch_post_handler!(container_post_handler, container_batch_post_handler, ContainerEntity); + entity_batch_post_handler!(creator_post_handler, creator_batch_post_handler, CreatorEntity); + entity_batch_post_handler!(file_post_handler, file_batch_post_handler, FileEntity); + entity_batch_post_handler!(release_post_handler, release_batch_post_handler, ReleaseEntity); + entity_batch_post_handler!(work_post_handler, work_batch_post_handler, WorkEntity); +/* XXX: + fn container_batch_post_handler(&self, entity_list: &Vec) -> Result> { let conn = self.db_pool.get().expect("db_pool error"); + // TODO: start a transaction + let ret: Vec = vec![]; + for entity in entity_list.into_iter() { + ret.push(self.container_post_handler(*entity, Some(conn))?); + } + Ok(ret) + } +*/ + + fn creator_post_handler(&self, entity: models::CreatorEntity, conn: Option<&DbConn>) -> Result { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), @@ -426,13 +485,21 @@ impl Server { .bind::, _>(entity.orcid) .bind::, _>(entity.extra) .bind::(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; edit.to_model() } - fn file_post_handler(&self, entity: models::FileEntity) -> Result { - let conn = self.db_pool.get().expect("db_pool error"); + fn file_post_handler(&self, entity: models::FileEntity, conn: Option<&DbConn>) -> Result { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), @@ -456,7 +523,7 @@ impl Server { .bind::, _>(entity.url) .bind::, _>(entity.extra) .bind::(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; let _releases: Option> = match entity.releases { None => None, @@ -473,7 +540,7 @@ impl Server { .collect(); let release_rows: Vec = insert_into(file_release::table) .values(release_rows) - .get_results(&conn) + .get_results(conn) .expect("error inserting file_releases"); Some(release_rows) } @@ -483,35 +550,16 @@ impl Server { edit.to_model() } - fn work_post_handler(&self, entity: models::WorkEntity) -> Result { - let conn = self.db_pool.get().expect("db_pool error"); - let editor_id = 1; // TODO: auth - let editgroup_id = match entity.editgroup_id { - None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), - Some(param) => param as i64, + fn release_post_handler(&self, entity: models::ReleaseEntity, conn: Option<&DbConn>) -> Result { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), }; - - let edit: WorkEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (work_type, extra_json) - VALUES ($1, $2) - RETURNING id ), - ident AS ( INSERT INTO work_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO work_edit (editgroup_id, ident_id, rev_id) VALUES - ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::, _>(entity.work_type) - .bind::, _>(entity.extra) - .bind::(editgroup_id) - .get_result(&conn)?; - - edit.to_model() - } - - fn release_post_handler(&self, entity: models::ReleaseEntity) -> Result { - let conn = self.db_pool.get().expect("db_pool error"); let editor_id = 1; // TODO: auth let editgroup_id = match entity.editgroup_id { None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), @@ -548,7 +596,7 @@ impl Server { .bind::, _>(entity.publisher) .bind::, _>(entity.extra) .bind::(editgroup_id) - .get_result(&conn)?; + .get_result(conn)?; let _refs: Option> = match entity.refs { None => None, @@ -569,7 +617,7 @@ impl Server { .collect(); let ref_rows: Vec = insert_into(release_ref::table) .values(ref_rows) - .get_results(&conn) + .get_results(conn) .expect("error inserting release_refs"); Some(ref_rows) } @@ -596,7 +644,7 @@ impl Server { .collect(); let contrib_rows: Vec = insert_into(release_contrib::table) .values(contrib_rows) - .get_results(&conn) + .get_results(conn) .expect("error inserting release_contribs"); Some(contrib_rows) } @@ -606,6 +654,42 @@ impl Server { edit.to_model() } + fn work_post_handler(&self, entity: models::WorkEntity, conn: Option<&DbConn>) -> Result { + // There mut be a cleaner way to manage the lifetime here + let real_conn = match conn { + Some(_) => None, + None => { Some(self.db_pool.get().expect("database pool")) }, + }; + let conn = match real_conn { + Some(ref c) => c, + None => conn.unwrap(), + }; + + let editor_id = 1; // TODO: auth + let editgroup_id = match entity.editgroup_id { + None => get_or_create_editgroup(editor_id, &conn).expect("current editgroup"), + Some(param) => param as i64, + }; + + let edit: WorkEditRow = + diesel::sql_query( + "WITH rev AS ( INSERT INTO work_rev (work_type, extra_json) + VALUES ($1, $2) + RETURNING id ), + ident AS ( INSERT INTO work_ident (rev_id) + VALUES ((SELECT rev.id FROM rev)) + RETURNING id ) + INSERT INTO work_edit (editgroup_id, ident_id, rev_id) VALUES + ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + RETURNING *", + ).bind::, _>(entity.work_type) + .bind::, _>(entity.extra) + .bind::(editgroup_id) + .get_result(conn)?; + + edit.to_model() + } + fn editgroup_id_get_handler(&self, id: i64) -> Result> { let conn = self.db_pool.get().expect("db_pool error"); @@ -709,6 +793,9 @@ impl Api for Server { container_post, container_post_handler, ContainerPostResponse, + container_batch_post, + container_batch_post_handler, + ContainerBatchPostResponse, ContainerEntity ); wrap_entity_handlers!( @@ -718,6 +805,9 @@ impl Api for Server { creator_post, creator_post_handler, CreatorPostResponse, + creator_batch_post, + creator_batch_post_handler, + CreatorBatchPostResponse, CreatorEntity ); wrap_entity_handlers!( @@ -727,6 +817,9 @@ impl Api for Server { file_post, file_post_handler, FilePostResponse, + file_batch_post, + file_batch_post_handler, + FileBatchPostResponse, FileEntity ); wrap_entity_handlers!( @@ -736,6 +829,9 @@ impl Api for Server { release_post, release_post_handler, ReleasePostResponse, + release_batch_post, + release_batch_post_handler, + ReleaseBatchPostResponse, ReleaseEntity ); wrap_entity_handlers!( @@ -745,6 +841,9 @@ impl Api for Server { work_post, work_post_handler, WorkPostResponse, + work_batch_post, + work_batch_post_handler, + WorkBatchPostResponse, WorkEntity ); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0038f9db..8ec850c5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -24,6 +24,7 @@ mod errors { error_chain! { foreign_links { Fmt(::std::fmt::Error); Diesel(::diesel::result::Error); + R2d2(::diesel::r2d2::Error); Uuid(::uuid::ParseError); Io(::std::io::Error) #[cfg(unix)]; } -- cgit v1.2.3