diff options
| -rw-r--r-- | rust/src/api_helpers.rs | 140 | ||||
| -rw-r--r-- | rust/src/api_server.rs | 237 | ||||
| -rw-r--r-- | rust/src/api_wrappers.rs | 48 | 
3 files changed, 189 insertions, 236 deletions
| diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 91c6200d..489631b3 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -7,6 +7,7 @@ use errors::*;  use regex::Regex;  use uuid::Uuid; +/// This function should always be run within a transaction  pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result<Uuid> {      // check for current active      let ed_row: EditorRow = editor::table.find(editor_id).first(conn)?; @@ -15,81 +16,78 @@ pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result<U      }      // need to insert and update -    conn.build_transaction().run(|| { -        let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) -            .values((editgroup::editor_id.eq(ed_row.id),)) -            .get_result(conn)?; -        diesel::update(editor::table.find(ed_row.id)) -            .set(editor::active_editgroup_id.eq(eg_row.id)) -            .execute(conn)?; -        Ok(eg_row.id) -    }) +    let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) +        .values((editgroup::editor_id.eq(ed_row.id),)) +        .get_result(conn)?; +    diesel::update(editor::table.find(ed_row.id)) +        .set(editor::active_editgroup_id.eq(eg_row.id)) +        .execute(conn)?; +    Ok(eg_row.id)  } +/// This function should always be run within a transaction  pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result<ChangelogRow> { -    conn.build_transaction().run(|| { -        // check that we haven't accepted already (in changelog) -        // NB: could leave this to a UNIQUE constraint -        let count: i64 = changelog::table -            .filter(changelog::editgroup_id.eq(editgroup_id)) -            .count() -            .get_result(conn)?; -        if count > 0 { -            bail!( -                "editgroup {} has already been accepted", -                editgroup_id.to_string() -            ); -        } - -        // for each entity type... -        //for entity in (container_edit, creator_edit, file_edit, release_edit, work_edit) { -            /* -            // This would be the clean and efficient way, but see: -            // https://github.com/diesel-rs/diesel/issues/1478 -            diesel::update(container_ident::table) -                .inner_join(container_edit::table.on( -                    container_ident::id.eq(container_edit::ident_id) -                )) -                .filter(container_edit::editgroup_id.eq(editgroup_id)) -                .values(( -                    container_ident::is_live.eq(true), -                    container_ident::rev_id.eq(container_edit::rev_id), -                    container_ident::redirect_id.eq(container_edit::redirect_id), -                )) -                .execute()?; -            */ - -        // Sketchy... but fast? Only a few queries per accept. -        for entity in &["container", "creator", "file", "work", "release"] { -            diesel::sql_query(format!( -                " -                    UPDATE {entity}_ident -                    SET -                        is_live = true, -                        rev_id = {entity}_edit.rev_id, -                        redirect_id = {entity}_edit.redirect_id -                    FROM {entity}_edit -                    WHERE -                        {entity}_ident.id = {entity}_edit.ident_id -                        AND {entity}_edit.editgroup_id = $1", -                entity = entity -            )).bind::<diesel::sql_types::Uuid, _>(editgroup_id) -                .execute(conn)?; -        } - -        // append log/changelog row -        let entry: ChangelogRow = diesel::insert_into(changelog::table) -            .values((changelog::editgroup_id.eq(editgroup_id),)) -            .get_result(conn)?; - -        // update any editor's active editgroup -        let no_active: Option<Uuid> = None; -        diesel::update(editor::table) -            .filter(editor::active_editgroup_id.eq(editgroup_id)) -            .set(editor::active_editgroup_id.eq(no_active)) +    // check that we haven't accepted already (in changelog) +    // NB: could leave this to a UNIQUE constraint +    let count: i64 = changelog::table +        .filter(changelog::editgroup_id.eq(editgroup_id)) +        .count() +        .get_result(conn)?; +    if count > 0 { +        bail!( +            "editgroup {} has already been accepted", +            editgroup_id.to_string() +        ); +    } + +    // for each entity type... +    //for entity in (container_edit, creator_edit, file_edit, release_edit, work_edit) { +        /* +        // This would be the clean and efficient way, but see: +        // https://github.com/diesel-rs/diesel/issues/1478 +        diesel::update(container_ident::table) +            .inner_join(container_edit::table.on( +                container_ident::id.eq(container_edit::ident_id) +            )) +            .filter(container_edit::editgroup_id.eq(editgroup_id)) +            .values(( +                container_ident::is_live.eq(true), +                container_ident::rev_id.eq(container_edit::rev_id), +                container_ident::redirect_id.eq(container_edit::redirect_id), +            )) +            .execute()?; +        */ + +    // Sketchy... but fast? Only a few queries per accept. +    for entity in &["container", "creator", "file", "work", "release"] { +        diesel::sql_query(format!( +            " +                UPDATE {entity}_ident +                SET +                    is_live = true, +                    rev_id = {entity}_edit.rev_id, +                    redirect_id = {entity}_edit.redirect_id +                FROM {entity}_edit +                WHERE +                    {entity}_ident.id = {entity}_edit.ident_id +                    AND {entity}_edit.editgroup_id = $1", +            entity = entity +        )).bind::<diesel::sql_types::Uuid, _>(editgroup_id)              .execute(conn)?; -        Ok(entry) -    }) +    } + +    // append log/changelog row +    let entry: ChangelogRow = diesel::insert_into(changelog::table) +        .values((changelog::editgroup_id.eq(editgroup_id),)) +        .get_result(conn)?; + +    // update any editor's active editgroup +    let no_active: Option<Uuid> = None; +    diesel::update(editor::table) +        .filter(editor::active_editgroup_id.eq(editgroup_id)) +        .set(editor::active_editgroup_id.eq(no_active)) +        .execute(conn)?; +    Ok(entry)  }  /// Convert fatcat IDs (base32 strings) to UUID diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 902b0934..bd13ebb6 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -22,13 +22,14 @@ type DbConn = diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<die  macro_rules! entity_batch_handler {      ($post_handler:ident, $post_batch_handler:ident, $model:ident) => { -        pub fn $post_batch_handler(&self, entity_list: &[models::$model]) -> -                Result<Vec<EntityEdit>> { -            let conn = self.db_pool.get().expect("db_pool error"); -            // TODO: start a transaction +        pub fn $post_batch_handler( +            &self, +            entity_list: &[models::$model], +            conn: &DbConn, +        ) -> Result<Vec<EntityEdit>> {              let mut ret: Vec<EntityEdit> = vec![];              for entity in entity_list { -                ret.push(self.$post_handler(entity.clone(), Some(&conn))?); +                ret.push(self.$post_handler(entity.clone(), conn)?);              }              Ok(ret)          } @@ -41,8 +42,8 @@ macro_rules! entity_history_handler {              &self,              id: &str,              limit: Option<i64>, +            conn: &DbConn,          ) -> Result<Vec<EntityHistoryEntry>> { -            let conn = self.db_pool.get().expect("db_pool error");              let id = fcid2uuid(id)?;              let limit = limit.unwrap_or(50); @@ -52,7 +53,7 @@ macro_rules! entity_history_handler {                  .filter($edit_table::ident_id.eq(id))                  .order(changelog::id.desc())                  .limit(limit) -                .get_results(&conn)?; +                .get_results(conn)?;              let history: Vec<EntityHistoryEntry> = rows.into_iter()                  .map(|(eg_row, cl_row, e_row)| EntityHistoryEntry { @@ -294,21 +295,19 @@ fn work_row2entity(ident: Option<WorkIdentRow>, rev: WorkRevRow) -> Result<WorkE  }  impl Server { -    pub fn get_container_handler(&self, id: &str) -> Result<ContainerEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_container_handler(&self, id: &str, conn: &DbConn) -> Result<ContainerEntity> {          let id = fcid2uuid(id)?;          // TODO: handle Deletions          let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table              .find(id)              .inner_join(container_rev::table) -            .first(&conn)?; +            .first(conn)?;          container_row2entity(Some(ident), rev)      } -    pub fn lookup_container_handler(&self, issnl: &str) -> Result<ContainerEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn lookup_container_handler(&self, issnl: &str, conn: &DbConn) -> Result<ContainerEntity> {          check_issn(issnl)?;          let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table @@ -316,25 +315,23 @@ impl Server {              .filter(container_rev::issnl.eq(issnl))              .filter(container_ident::is_live.eq(true))              .filter(container_ident::redirect_id.is_null()) -            .first(&conn)?; +            .first(conn)?;          container_row2entity(Some(ident), rev)      } -    pub fn get_creator_handler(&self, id: &str) -> Result<CreatorEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_creator_handler(&self, id: &str, conn: &DbConn) -> Result<CreatorEntity> {          let id = fcid2uuid(id)?;          let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table              .find(id)              .inner_join(creator_rev::table) -            .first(&conn)?; +            .first(conn)?;          creator_row2entity(Some(ident), rev)      } -    pub fn lookup_creator_handler(&self, orcid: &str) -> Result<CreatorEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn lookup_creator_handler(&self, orcid: &str, conn: &DbConn) -> Result<CreatorEntity> {          check_orcid(orcid)?;          let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table @@ -342,13 +339,13 @@ impl Server {              .filter(creator_rev::orcid.eq(orcid))              .filter(creator_ident::is_live.eq(true))              .filter(creator_ident::redirect_id.is_null()) -            .first(&conn)?; +            .first(conn)?;          creator_row2entity(Some(ident), rev)      } -    pub fn get_creator_releases_handler(&self, id: &str) -> Result<Vec<ReleaseEntity>> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_creator_releases_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<ReleaseEntity>> { +          let id = fcid2uuid(id)?;          // TODO: some kind of unique or group-by? @@ -358,52 +355,51 @@ impl Server {              .filter(release_contrib::creator_ident_id.eq(&id))              .filter(release_ident::is_live.eq(true))              .filter(release_ident::redirect_id.is_null()) -            .load(&conn)?; +            .load(conn)?;          rows.into_iter() -            .map(|(rev, ident, _)| release_row2entity(Some(ident), rev, &conn)) +            .map(|(rev, ident, _)| release_row2entity(Some(ident), rev, conn))              .collect()      } -    pub fn get_file_handler(&self, id: &str) -> Result<FileEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_file_handler(&self, id: &str, conn: &DbConn) -> Result<FileEntity> { +          let id = fcid2uuid(id)?;          let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table              .find(id)              .inner_join(file_rev::table) -            .first(&conn)?; +            .first(conn)?; -        file_row2entity(Some(ident), rev, &conn) +        file_row2entity(Some(ident), rev, conn)      } -    pub fn lookup_file_handler(&self, sha1: &str) -> Result<FileEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn lookup_file_handler(&self, sha1: &str, conn: &DbConn) -> Result<FileEntity> { +          let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table              .inner_join(file_rev::table)              .filter(file_rev::sha1.eq(sha1))              .filter(file_ident::is_live.eq(true))              .filter(file_ident::redirect_id.is_null()) -            .first(&conn)?; +            .first(conn)?; -        file_row2entity(Some(ident), rev, &conn) +        file_row2entity(Some(ident), rev, conn)      } -    pub fn get_release_handler(&self, id: &str) -> Result<ReleaseEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_release_handler(&self, id: &str, conn: &DbConn) -> Result<ReleaseEntity> { +          let id = fcid2uuid(id)?;          let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table              .find(id)              .inner_join(release_rev::table) -            .first(&conn)?; +            .first(conn)?; -        release_row2entity(Some(ident), rev, &conn) +        release_row2entity(Some(ident), rev, conn)      } -    pub fn lookup_release_handler(&self, doi: &str) -> Result<ReleaseEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn lookup_release_handler(&self, doi: &str, conn: &DbConn) -> Result<ReleaseEntity> {          check_doi(doi)?;          let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table @@ -411,13 +407,13 @@ impl Server {              .filter(release_rev::doi.eq(doi))              .filter(release_ident::is_live.eq(true))              .filter(release_ident::redirect_id.is_null()) -            .first(&conn)?; +            .first(conn)?; -        release_row2entity(Some(ident), rev, &conn) +        release_row2entity(Some(ident), rev, conn)      } -    pub fn get_release_files_handler(&self, id: &str) -> Result<Vec<FileEntity>> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_release_files_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<FileEntity>> { +          let id = fcid2uuid(id)?;          let rows: Vec<(FileRevRow, FileIdentRow, FileReleaseRow)> = file_rev::table @@ -426,27 +422,27 @@ impl Server {              .filter(file_release::target_release_ident_id.eq(&id))              .filter(file_ident::is_live.eq(true))              .filter(file_ident::redirect_id.is_null()) -            .load(&conn)?; +            .load(conn)?;          rows.into_iter() -            .map(|(rev, ident, _)| file_row2entity(Some(ident), rev, &conn)) +            .map(|(rev, ident, _)| file_row2entity(Some(ident), rev, conn))              .collect()      } -    pub fn get_work_handler(&self, id: &str) -> Result<WorkEntity> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_work_handler(&self, id: &str, conn: &DbConn) -> Result<WorkEntity> { +          let id = fcid2uuid(id)?;          let (ident, rev): (WorkIdentRow, WorkRevRow) = work_ident::table              .find(id)              .inner_join(work_rev::table) -            .first(&conn)?; +            .first(conn)?;          work_row2entity(Some(ident), rev)      } -    pub fn get_work_releases_handler(&self, id: &str) -> Result<Vec<ReleaseEntity>> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_work_releases_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<ReleaseEntity>> { +          let id = fcid2uuid(id)?;          let rows: Vec<(ReleaseRevRow, ReleaseIdentRow)> = release_rev::table @@ -454,31 +450,21 @@ impl Server {              .filter(release_rev::work_ident_id.eq(&id))              .filter(release_ident::is_live.eq(true))              .filter(release_ident::redirect_id.is_null()) -            .load(&conn)?; +            .load(conn)?;          rows.into_iter() -            .map(|(rev, ident)| release_row2entity(Some(ident), rev, &conn)) +            .map(|(rev, ident)| release_row2entity(Some(ident), rev, conn))              .collect()      }      pub fn create_container_handler(          &self,          entity: models::ContainerEntity, -        conn: Option<&DbConn>, +        conn: &DbConn,      ) -> Result<EntityEdit> { -        // 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 = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth          let editgroup_id: Uuid = match entity.editgroup_id { -            None => get_or_create_editgroup(editor_id, &conn)?, +            None => get_or_create_editgroup(editor_id, conn)?,              Some(param) => fcid2uuid(¶m)?,          };          if let Some(ref extid) = entity.wikidata_qid { @@ -514,20 +500,11 @@ impl Server {      pub fn create_creator_handler(          &self,          entity: models::CreatorEntity, -        conn: Option<&DbConn>, +        conn: &DbConn,      ) -> Result<EntityEdit> { -        // 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 = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // 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).expect("current editgroup"),              Some(param) => fcid2uuid(¶m)?,          };          if let Some(ref extid) = entity.orcid { @@ -562,20 +539,11 @@ impl Server {      pub fn create_file_handler(          &self,          entity: models::FileEntity, -        conn: Option<&DbConn>, +        conn: &DbConn,      ) -> Result<EntityEdit> { -        // 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 = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // 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).expect("current editgroup"),              Some(param) => fcid2uuid(¶m)?,          }; @@ -651,20 +619,11 @@ impl Server {      pub fn create_release_handler(          &self,          entity: models::ReleaseEntity, -        conn: Option<&DbConn>, +        conn: &DbConn,      ) -> Result<EntityEdit> { -        // 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 = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // 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).expect("current editgroup"),              Some(param) => fcid2uuid(¶m)?,          };          if let Some(ref extid) = entity.doi { @@ -692,7 +651,7 @@ impl Server {                      editgroup_id: Some(uuid2fcid(&editgroup_id)),                      extra: None,                  }; -                let new_entity = self.create_work_handler(work_model, Some(&conn))?; +                let new_entity = self.create_work_handler(work_model, conn)?;                  fcid2uuid(&new_entity.ident)?              }          }; @@ -835,21 +794,12 @@ impl Server {      pub fn create_work_handler(          &self,          entity: models::WorkEntity, -        conn: Option<&DbConn>, +        conn: &DbConn,      ) -> Result<EntityEdit> { -        // 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 = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // 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).expect("current editgroup"),              Some(param) => fcid2uuid(¶m)?,          }; @@ -871,22 +821,19 @@ impl Server {          edit.into_model()      } -    pub fn accept_editgroup_handler(&self, id: &str) -> Result<()> { -        let conn = self.db_pool.get().expect("db_pool error"); -        accept_editgroup(fcid2uuid(id)?, &conn)?; +    pub fn accept_editgroup_handler(&self, id: &str, conn: &DbConn) -> Result<()> { +        accept_editgroup(fcid2uuid(id)?, conn)?;          Ok(())      } -    pub fn create_editgroup_handler(&self, entity: models::Editgroup) -> Result<Editgroup> { -        let conn = self.db_pool.get().expect("db_pool error"); - +    pub fn create_editgroup_handler(&self, entity: models::Editgroup, conn: &DbConn) -> Result<Editgroup> {          let row: EditgroupRow = insert_into(editgroup::table)              .values((                  editgroup::editor_id.eq(fcid2uuid(&entity.editor_id)?),                  editgroup::description.eq(entity.description),                  editgroup::extra_json.eq(entity.extra),              )) -            .get_result(&conn) +            .get_result(conn)              .expect("error creating edit group");          Ok(Editgroup { @@ -898,17 +845,7 @@ impl Server {          })      } -    pub fn get_editgroup_handler(&self, id: &str, conn: Option<&DbConn>) -> Result<Editgroup> { -        // 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(), -        }; +    pub fn get_editgroup_handler(&self, id: &str, conn: &DbConn) -> Result<Editgroup> {          let id = fcid2uuid(id)?;          let row: EditgroupRow = editgroup::table.find(id).first(conn)?; @@ -966,11 +903,10 @@ impl Server {          Ok(eg)      } -    pub fn get_editor_handler(&self, id: &str) -> Result<Editor> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_editor_handler(&self, id: &str, conn: &DbConn) -> Result<Editor> {          let id = fcid2uuid(id)?; -        let row: EditorRow = editor::table.find(id).first(&conn)?; +        let row: EditorRow = editor::table.find(id).first(conn)?;          let ed = Editor {              id: Some(uuid2fcid(&row.id)), @@ -979,16 +915,15 @@ impl Server {          Ok(ed)      } -    pub fn editor_changelog_get_handler(&self, id: &str) -> Result<Vec<ChangelogEntry>> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn editor_changelog_get_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<ChangelogEntry>> {          let id = fcid2uuid(id)?;          // TODO: single query -        let editor: EditorRow = editor::table.find(id).first(&conn)?; +        let editor: EditorRow = editor::table.find(id).first(conn)?;          let changes: Vec<(ChangelogRow, EditgroupRow)> = changelog::table              .inner_join(editgroup::table)              .filter(editgroup::editor_id.eq(editor.id)) -            .load(&conn)?; +            .load(conn)?;          let entries = changes              .into_iter() @@ -1002,15 +937,15 @@ impl Server {          Ok(entries)      } -    pub fn get_changelog_handler(&self, limit: Option<i64>) -> Result<Vec<ChangelogEntry>> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_changelog_handler(&self, limit: Option<i64>, conn: &DbConn) -> Result<Vec<ChangelogEntry>> { +          let limit = limit.unwrap_or(50);          let changes: Vec<(ChangelogRow, EditgroupRow)> = changelog::table              .inner_join(editgroup::table)              .order(changelog::id.desc())              .limit(limit) -            .load(&conn)?; +            .load(conn)?;          let entries = changes              .into_iter() @@ -1024,44 +959,42 @@ impl Server {          Ok(entries)      } -    pub fn get_changelog_entry_handler(&self, id: i64) -> Result<ChangelogEntry> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_changelog_entry_handler(&self, id: i64, conn: &DbConn) -> Result<ChangelogEntry> { -        let cl_row: ChangelogRow = changelog::table.find(id).first(&conn)?; -        let editgroup = self.get_editgroup_handler(&uuid2fcid(&cl_row.editgroup_id), Some(&conn))?; +        let cl_row: ChangelogRow = changelog::table.find(id).first(conn)?; +        let editgroup = self.get_editgroup_handler(&uuid2fcid(&cl_row.editgroup_id), conn)?;          let mut entry = cl_row.into_model();          entry.editgroup = Some(editgroup);          Ok(entry)      } -    pub fn get_stats_handler(&self, more: &Option<String>) -> Result<StatsResponse> { -        let conn = self.db_pool.get().expect("db_pool error"); +    pub fn get_stats_handler(&self, more: &Option<String>, conn: &DbConn) -> Result<StatsResponse> {          let merged_editgroups: i64 = changelog::table              .select(diesel::dsl::count_star()) -            .first(&conn)?; +            .first(conn)?;          let releases_with_dois: i64 = release_rev::table              .inner_join(release_ident::table)              .filter(release_rev::doi.is_not_null())              .filter(release_ident::is_live.eq(true))              .filter(release_ident::redirect_id.is_null())              .select(diesel::dsl::count_star()) -            .first(&conn)?; +            .first(conn)?;          let creators_with_orcids: i64 = creator_rev::table              .inner_join(creator_ident::table)              .filter(creator_rev::orcid.is_not_null())              .filter(creator_ident::is_live.eq(true))              .filter(creator_ident::redirect_id.is_null())              .select(diesel::dsl::count_star()) -            .first(&conn)?; +            .first(conn)?;          let containers_with_issnls: i64 = container_rev::table              .inner_join(container_ident::table)              .filter(container_rev::issnl.is_not_null())              .filter(container_ident::is_live.eq(true))              .filter(container_ident::redirect_id.is_null())              .count() -            .first(&conn)?; +            .first(conn)?;          let files_with_releases: Option<i64> = if more.is_some() {              // this query is slightly inaccurate and over-counts: it includes files that have release @@ -1074,7 +1007,7 @@ impl Server {                  .select(file_ident::id)                  .distinct()                  .count() -                .first(&conn)?) +                .first(conn)?)          } else {              None          }; @@ -1088,18 +1021,18 @@ impl Server {                  .select(file_release::target_release_ident_id)                  .distinct()                  .count() -                .first(&conn)?) +                .first(conn)?)          } else {              None          };          let val = json!({              "entity_counts": { -                "container": count_entity!(container_ident, &conn), -                "creator": count_entity!(creator_ident, &conn), -                "file": count_entity!(file_ident, &conn), -                "release": count_entity!(release_ident, &conn), -                "work": count_entity!(work_ident, &conn), +                "container": count_entity!(container_ident, conn), +                "creator": count_entity!(creator_ident, conn), +                "file": count_entity!(file_ident, conn), +                "release": count_entity!(release_ident, conn), +                "work": count_entity!(work_ident, conn),              },              "merged_editgroups": merged_editgroups,              "releases_with_dois": releases_with_dois, diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index df066694..e0112149 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -6,6 +6,7 @@ use fatcat_api::models;  use fatcat_api::models::*;  use fatcat_api::*;  use futures::{self, Future}; +use diesel::Connection;  /// Helper for generating wrappers (which return "Box::new(futures::done(Ok(BLAH)))" like the  /// codegen fatcat-api code wants) that call through to actual helpers (which have simple Result<> @@ -25,7 +26,9 @@ macro_rules! wrap_entity_handlers {              id: String,              _context: &Context,          ) -> Box<Future<Item = $get_resp, Error = ApiError> + Send> { -            let ret = match self.$get_handler(&id) { +            let conn = self.db_pool.get().expect("db_pool error"); +            // No transaction for GET +            let ret = match self.$get_handler(&id, &conn) {                  Ok(entity) =>                      $get_resp::FoundEntity(entity),                  Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -50,7 +53,8 @@ macro_rules! wrap_entity_handlers {              entity: models::$model,              _context: &Context,          ) -> Box<Future<Item = $post_resp, Error = ApiError> + Send> { -            let ret = match self.$post_handler(entity, None) { +            let conn = self.db_pool.get().expect("db_pool error"); +            let ret = match conn.transaction(|| self.$post_handler(entity, &conn)) {                  Ok(edit) =>                      $post_resp::CreatedEntity(edit),                  Err(Error(ErrorKind::Diesel(e), _)) => @@ -75,7 +79,8 @@ macro_rules! wrap_entity_handlers {              entity_list: &Vec<models::$model>,              _context: &Context,          ) -> Box<Future<Item = $post_batch_resp, Error = ApiError> + Send> { -            let ret = match self.$post_batch_handler(entity_list) { +            let conn = self.db_pool.get().expect("db_pool error"); +            let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, &conn)) {                  Ok(edit) =>                      $post_batch_resp::CreatedEntities(edit),                  Err(Error(ErrorKind::Diesel(e), _)) => @@ -101,7 +106,9 @@ macro_rules! wrap_entity_handlers {              limit: Option<i64>,              _context: &Context,          ) -> Box<Future<Item = $get_history_resp, Error = ApiError> + Send> { -            let ret = match self.$get_history_handler(&id, limit) { +            let conn = self.db_pool.get().expect("db_pool error"); +            // No transaction for GET +            let ret = match self.$get_history_handler(&id, limit, &conn) {                  Ok(history) =>                      $get_history_resp::FoundEntityHistory(history),                  Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -128,7 +135,9 @@ macro_rules! wrap_lookup_handler {              $idname: $idtype,              _context: &Context,          ) -> Box<Future<Item = $get_resp, Error = ApiError> + Send> { -            let ret = match self.$get_handler(&$idname) { +            let conn = self.db_pool.get().expect("db_pool error"); +            // No transaction for GET +            let ret = match self.$get_handler(&$idname, &conn) {                  Ok(entity) =>                      $get_resp::FoundEntity(entity),                  Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -279,7 +288,8 @@ impl Api for Server {          id: String,          _context: &Context,      ) -> Box<Future<Item = AcceptEditgroupResponse, Error = ApiError> + Send> { -        let ret = match self.accept_editgroup_handler(&id) { +        let conn = self.db_pool.get().expect("db_pool error"); +        let ret = match conn.transaction(|| self.accept_editgroup_handler(&id, &conn)) {              Ok(()) => AcceptEditgroupResponse::MergedSuccessfully(Success {                  message: "horray!".to_string(),              }), @@ -300,7 +310,8 @@ impl Api for Server {          id: String,          _context: &Context,      ) -> Box<Future<Item = GetEditgroupResponse, Error = ApiError> + Send> { -        let ret = match self.get_editgroup_handler(&id, None) { +        let conn = self.db_pool.get().expect("db_pool error"); +        let ret = match conn.transaction(|| self.get_editgroup_handler(&id, &conn)) {              Ok(entity) =>                  GetEditgroupResponse::FoundEntity(entity),              Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -319,7 +330,8 @@ impl Api for Server {          entity: models::Editgroup,          _context: &Context,      ) -> Box<Future<Item = CreateEditgroupResponse, Error = ApiError> + Send> { -        let ret = match self.create_editgroup_handler(entity) { +        let conn = self.db_pool.get().expect("db_pool error"); +        let ret = match conn.transaction(|| self.create_editgroup_handler(entity, &conn)) {              Ok(eg) =>                  CreateEditgroupResponse::SuccessfullyCreated(eg),              Err(e) => @@ -335,7 +347,9 @@ impl Api for Server {          username: String,          _context: &Context,      ) -> Box<Future<Item = GetEditorChangelogResponse, Error = ApiError> + Send> { -        let ret = match self.editor_changelog_get_handler(&username) { +        let conn = self.db_pool.get().expect("db_pool error"); +        // No transaction for GET +        let ret = match self.editor_changelog_get_handler(&username, &conn) {              Ok(entries) => GetEditorChangelogResponse::FoundMergedChanges(entries),              Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => {                  GetEditorChangelogResponse::NotFound(ErrorResponse { @@ -358,7 +372,9 @@ impl Api for Server {          username: String,          _context: &Context,      ) -> Box<Future<Item = GetEditorResponse, Error = ApiError> + Send> { -        let ret = match self.get_editor_handler(&username) { +        let conn = self.db_pool.get().expect("db_pool error"); +        // No transaction for GET +        let ret = match self.get_editor_handler(&username, &conn) {              Ok(entity) => GetEditorResponse::FoundEditor(entity),              Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => {                  GetEditorResponse::NotFound(ErrorResponse { @@ -381,7 +397,9 @@ impl Api for Server {          limit: Option<i64>,          _context: &Context,      ) -> Box<Future<Item = GetChangelogResponse, Error = ApiError> + Send> { -        let ret = match self.get_changelog_handler(limit) { +        let conn = self.db_pool.get().expect("db_pool error"); +        // No transaction for GET +        let ret = match self.get_changelog_handler(limit, &conn) {              Ok(changelog) => GetChangelogResponse::Success(changelog),              Err(e) => {                  error!("{}", e); @@ -398,7 +416,9 @@ impl Api for Server {          id: i64,          _context: &Context,      ) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send> { -        let ret = match self.get_changelog_entry_handler(id) { +        let conn = self.db_pool.get().expect("db_pool error"); +        // No transaction for GET +        let ret = match self.get_changelog_entry_handler(id, &conn) {              Ok(entry) => GetChangelogEntryResponse::FoundChangelogEntry(entry),              Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => {                  GetChangelogEntryResponse::NotFound(ErrorResponse { @@ -420,7 +440,9 @@ impl Api for Server {          more: Option<String>,          _context: &Context,      ) -> Box<Future<Item = GetStatsResponse, Error = ApiError> + Send> { -        let ret = match self.get_stats_handler(&more) { +        let conn = self.db_pool.get().expect("db_pool error"); +        // No transaction for GET +        let ret = match self.get_stats_handler(&more, &conn) {              Ok(stats) => GetStatsResponse::Success(stats),              Err(e) => {                  error!("{}", e); | 
