From b83347388d17dfd58c9f3d123d3d96d050ca6e9b Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 16 Aug 2018 09:42:55 -0700 Subject: ORCIDs can end in X character --- rust/src/api_helpers.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'rust/src') diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 489631b3..020aad76 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -181,7 +181,7 @@ pub fn check_issn(raw: &str) -> Result<()> { pub fn check_orcid(raw: &str) -> Result<()> { lazy_static! { - static ref RE: Regex = Regex::new(r"^\d{4}-\d{4}-\d{4}-\d{4}$").unwrap(); + static ref RE: Regex = Regex::new(r"^\d{4}-\d{4}-\d{4}-\d{3}[\dX]$").unwrap(); } if RE.is_match(raw) { Ok(()) @@ -193,6 +193,14 @@ pub fn check_orcid(raw: &str) -> Result<()> { } } +#[test] +fn test_check_orcid() { + assert!(check_orcid("0123-4567-3456-6789").is_ok()); + assert!(check_orcid("0123-4567-3456-678X").is_ok()); + assert!(check_orcid("01234567-3456-6780").is_err()); + assert!(check_orcid("0x23-4567-3456-6780").is_err()); +} + // TODO: make the above checks "more correct" // TODO: check ISBN-13 // TODO: check hashes (SHA-1, etc) -- cgit v1.2.3 From 582d2397004b8a55847a3fe933cc29e8b99ab8f6 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Sun, 19 Aug 2018 18:31:09 -0700 Subject: add hints for postgres query planner --- rust/src/api_server.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index f7866b68..9239d63b 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -318,6 +318,9 @@ impl Server { let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table .inner_join(container_rev::table) .filter(container_rev::issnl.eq(issnl)) + // This NOT NULL is here to ensure the postgresql query planner that it can use an + // index + .filter(container_rev::issnl.is_not_null()) .filter(container_ident::is_live.eq(true)) .filter(container_ident::redirect_id.is_null()) .first(conn)?; @@ -344,6 +347,9 @@ impl Server { let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table .inner_join(creator_rev::table) .filter(creator_rev::orcid.eq(orcid)) + // This NOT NULL is here to ensure the postgresql query planner that it can use an + // index + .filter(creator_rev::orcid.is_not_null()) .filter(creator_ident::is_live.eq(true)) .filter(creator_ident::redirect_id.is_null()) .first(conn)?; -- cgit v1.2.3 From 657c1e7d4d6f07762bc1f6a7b24e9512e0089987 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Sun, 19 Aug 2018 18:42:21 -0700 Subject: add NOT NULL to other lookups --- rust/src/api_server.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 9239d63b..66e6359a 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -396,6 +396,9 @@ impl Server { let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table .inner_join(file_rev::table) .filter(file_rev::sha1.eq(sha1)) + // This NOT NULL is here to ensure the postgresql query planner that it can use an + // index + .filter(file_rev::sha1.is_not_null()) .filter(file_ident::is_live.eq(true)) .filter(file_ident::redirect_id.is_null()) .first(conn)?; @@ -434,6 +437,9 @@ impl Server { let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table .inner_join(release_rev::table) .filter(release_rev::doi.eq(doi)) + // This NOT NULL is here to ensure the postgresql query planner that it can use an + // index + .filter(release_rev::doi.is_not_null()) .filter(release_ident::is_live.eq(true)) .filter(release_ident::redirect_id.is_null()) .first(conn)?; -- cgit v1.2.3 From b8ca8ebdae304dcc2a694e5dfbc9070003f0df8a Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Mon, 20 Aug 2018 01:18:10 -0700 Subject: use index_val, not index, in SQL schema --- rust/migrations/2018-05-12-001226_init/up.sql | 8 ++++---- rust/src/api_server.rs | 12 ++++++------ rust/src/database_models.rs | 8 ++++---- rust/src/database_schema.rs | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'rust/src') diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql index 024273a7..b0733576 100644 --- a/rust/migrations/2018-05-12-001226_init/up.sql +++ b/rust/migrations/2018-05-12-001226_init/up.sql @@ -276,7 +276,7 @@ CREATE TABLE release_contrib ( creator_ident_id UUID REFERENCES creator_ident(id), raw_name TEXT, role TEXT, -- TODO: enum? - index BIGINT, + index_val BIGINT, extra_json JSON ); @@ -287,7 +287,7 @@ CREATE TABLE release_ref ( id BIGSERIAL PRIMARY KEY, release_rev UUID REFERENCES release_rev(id) NOT NULL, target_release_ident_id UUID REFERENCES release_ident(id), -- or work? - index BIGINT, + index_val BIGINT, key TEXT, extra_json JSON, -- title, year, container_title, locator (aka, page), oci_id container_title TEXT, @@ -432,12 +432,12 @@ INSERT INTO release_rev_abstract (release_rev, abstract_sha1, mimetype, lang) VA ('00000000-0000-0000-4444-FFF000000001', '1ba86bf8c2979a62d29b18b537e50b2b093be27e', 'text/plain', 'en'), ('00000000-0000-0000-4444-FFF000000002', '0da908ab584b5e445a06beb172e3fab8cb5169e3', 'application/xml+jats', 'en'); -INSERT INTO release_contrib (release_rev, creator_ident_id, raw_name, role, index) VALUES +INSERT INTO release_contrib (release_rev, creator_ident_id, raw_name, role, index_val) VALUES ('00000000-0000-0000-4444-FFF000000002', null, null, null, null), ('00000000-0000-0000-4444-FFF000000002', '00000000-0000-0000-2222-000000000002', 'some contrib', 'editor', 4), ('00000000-0000-0000-4444-FFF000000003', '00000000-0000-0000-2222-000000000003', 'John P. A. Ioannidis', 'author', 0); -INSERT INTO release_ref (release_rev, target_release_ident_id, index, extra_json) VALUES +INSERT INTO release_ref (release_rev, target_release_ident_id, index_val, extra_json) VALUES ('00000000-0000-0000-4444-FFF000000002', null, null, null), ('00000000-0000-0000-4444-FFF000000002', '00000000-0000-0000-4444-000000000001', 4, '{"unstructured":"citation note"}'), ('00000000-0000-0000-4444-FFF000000003', null, 0, '{"unstructured": "Ioannidis JP, Haidich AB, Lau J. Any casualties in the clash of randomised and observational evidence? BMJ. 2001;322:879–880"}'), diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 66e6359a..b0dff173 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -198,12 +198,12 @@ fn release_row2entity( let refs: Vec = release_ref::table .filter(release_ref::release_rev.eq(rev.id)) - .order(release_ref::index.asc()) + .order(release_ref::index_val.asc()) .get_results(conn) .expect("fetch release refs") .into_iter() .map(|r: ReleaseRefRow| ReleaseRef { - index: r.index, + index: r.index_val, key: r.key, extra: r.extra_json, container_title: r.container_title, @@ -216,12 +216,12 @@ fn release_row2entity( let contribs: Vec = release_contrib::table .filter(release_contrib::release_rev.eq(rev.id)) - .order((release_contrib::role.asc(), release_contrib::index.asc())) + .order((release_contrib::role.asc(), release_contrib::index_val.asc())) .get_results(conn) .expect("fetch release refs") .into_iter() .map(|c: ReleaseContribRow| ReleaseContrib { - index: c.index, + index: c.index_val, raw_name: c.raw_name, role: c.role, extra: c.extra_json, @@ -741,7 +741,7 @@ impl Server { target_release_ident_id: r.target_release_id .clone() .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), - index: r.index, + index_val: r.index, key: r.key.clone(), container_title: r.container_title.clone(), year: r.year, @@ -773,7 +773,7 @@ impl Server { .clone() .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), raw_name: c.raw_name.clone(), - index: c.index, + index_val: c.index, role: c.role.clone(), extra_json: c.extra.clone(), }) diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 258bcab7..47e00bcf 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -228,7 +228,7 @@ pub struct ReleaseContribRow { pub creator_ident_id: Option, pub raw_name: Option, pub role: Option, - pub index: Option, + pub index_val: Option, pub extra_json: Option, } @@ -239,7 +239,7 @@ pub struct ReleaseContribNewRow { pub creator_ident_id: Option, pub raw_name: Option, pub role: Option, - pub index: Option, + pub index_val: Option, pub extra_json: Option, } @@ -249,7 +249,7 @@ pub struct ReleaseRefRow { pub id: i64, pub release_rev: Uuid, pub target_release_ident_id: Option, - pub index: Option, + pub index_val: Option, pub key: Option, pub extra_json: Option, pub container_title: Option, @@ -263,7 +263,7 @@ pub struct ReleaseRefRow { pub struct ReleaseRefNewRow { pub release_rev: Uuid, pub target_release_ident_id: Option, - pub index: Option, + pub index_val: Option, pub key: Option, pub extra_json: Option, pub container_title: Option, diff --git a/rust/src/database_schema.rs b/rust/src/database_schema.rs index 3a8fe901..a6605d81 100644 --- a/rust/src/database_schema.rs +++ b/rust/src/database_schema.rs @@ -159,7 +159,7 @@ table! { creator_ident_id -> Nullable, raw_name -> Nullable, role -> Nullable, - index -> Nullable, + index_val -> Nullable, extra_json -> Nullable, } } @@ -191,7 +191,7 @@ table! { id -> Int8, release_rev -> Uuid, target_release_ident_id -> Nullable, - index -> Nullable, + index_val -> Nullable, key -> Nullable, extra_json -> Nullable, container_title -> Nullable, -- cgit v1.2.3 From 2f7cff3c597af42c8dd02313bdceb98a11c2e414 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Mon, 20 Aug 2018 02:16:08 -0700 Subject: implement autoaccept Need to review: - changelog creation - clobbering of existing editgroup flag --- rust/src/api_server.rs | 61 ++++++++++++++++++++++++++++++++++-------------- rust/src/api_wrappers.rs | 5 ++-- 2 files changed, 46 insertions(+), 20 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index b0dff173..0cb07e81 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -25,11 +25,23 @@ macro_rules! entity_batch_handler { pub fn $post_batch_handler( &self, entity_list: &[models::$model], + autoaccept: bool, conn: &DbConn, ) -> Result> { let mut ret: Vec = vec![]; + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth + let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) + .values((editgroup::editor_id.eq(editor_id),)) + .get_result(conn)?; for entity in entity_list { - ret.push(self.$post_handler(entity.clone(), conn)?); + let mut e = entity.clone(); + e.editgroup_id = Some(uuid2fcid(&eg_row.id)); + ret.push(self.$post_handler(e, autoaccept, conn)?); + } + if autoaccept { + let _clr: ChangelogRow = diesel::insert_into(changelog::table) + .values((changelog::editgroup_id.eq(eg_row.id),)) + .get_result(conn)?; } Ok(ret) } @@ -216,7 +228,10 @@ fn release_row2entity( let contribs: Vec = release_contrib::table .filter(release_contrib::release_rev.eq(rev.id)) - .order((release_contrib::role.asc(), release_contrib::index_val.asc())) + .order(( + release_contrib::role.asc(), + release_contrib::index_val.asc(), + )) .get_results(conn) .expect("fetch release refs") .into_iter() @@ -495,6 +510,7 @@ impl Server { pub fn create_container_handler( &self, entity: models::ContainerEntity, + autoaccept: bool, conn: &DbConn, ) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth @@ -513,11 +529,11 @@ impl Server { "WITH rev AS ( INSERT INTO container_rev (name, publisher, issnl, wikidata_qid, abbrev, coden, extra_json) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id ), - ident AS ( INSERT INTO container_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) + ident AS ( INSERT INTO container_ident (is_live, rev_id) + VALUES ($8, (SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO container_edit (editgroup_id, ident_id, rev_id) VALUES - ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($9, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::(entity.name) .bind::, _>(entity.publisher) @@ -526,6 +542,7 @@ impl Server { .bind::, _>(entity.abbrev) .bind::, _>(entity.coden) .bind::, _>(entity.extra) + .bind::(autoaccept) .bind::(editgroup_id) .get_result(conn)?; @@ -535,6 +552,7 @@ impl Server { pub fn create_creator_handler( &self, entity: models::CreatorEntity, + autoaccept: bool, conn: &DbConn, ) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth @@ -553,11 +571,11 @@ impl Server { "WITH rev AS ( INSERT INTO creator_rev (display_name, given_name, surname, orcid, wikidata_qid, extra_json) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id ), - ident AS ( INSERT INTO creator_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) + ident AS ( INSERT INTO creator_ident (is_live, rev_id) + VALUES ($7, (SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO creator_edit (editgroup_id, ident_id, rev_id) VALUES - ($7, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::(entity.display_name) .bind::, _>(entity.given_name) @@ -565,6 +583,7 @@ impl Server { .bind::, _>(entity.orcid) .bind::, _>(entity.wikidata_qid) .bind::, _>(entity.extra) + .bind::(autoaccept) .bind::(editgroup_id) .get_result(conn)?; @@ -574,6 +593,7 @@ impl Server { pub fn create_file_handler( &self, entity: models::FileEntity, + autoaccept: bool, conn: &DbConn, ) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth @@ -587,11 +607,11 @@ impl Server { "WITH rev AS ( INSERT INTO file_rev (size, sha1, sha256, md5, mimetype, extra_json) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id ), - ident AS ( INSERT INTO file_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) + ident AS ( INSERT INTO file_ident (is_live, rev_id) + VALUES ($7, (SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO file_edit (editgroup_id, ident_id, rev_id) VALUES - ($7, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::, _>(entity.size) .bind::, _>(entity.sha1) @@ -599,6 +619,7 @@ impl Server { .bind::, _>(entity.md5) .bind::, _>(entity.mimetype) .bind::, _>(entity.extra) + .bind::(autoaccept) .bind::(editgroup_id) .get_result(conn)?; @@ -654,6 +675,7 @@ impl Server { pub fn create_release_handler( &self, entity: models::ReleaseEntity, + autoaccept: bool, conn: &DbConn, ) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth @@ -686,7 +708,7 @@ impl Server { editgroup_id: Some(uuid2fcid(&editgroup_id)), extra: None, }; - let new_entity = self.create_work_handler(work_model, conn)?; + let new_entity = self.create_work_handler(work_model, autoaccept, conn)?; fcid2uuid(&new_entity.ident)? } }; @@ -700,11 +722,11 @@ impl Server { "WITH rev AS ( INSERT INTO release_rev (title, release_type, release_status, release_date, doi, pmid, pmcid, wikidata_qid, isbn13, core_id, volume, issue, pages, work_ident_id, container_ident_id, publisher, language, extra_json) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id ), - ident AS ( INSERT INTO release_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) + ident AS ( INSERT INTO release_ident (is_live, rev_id) + VALUES ($19, (SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO release_edit (editgroup_id, ident_id, rev_id) VALUES - ($19, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($20, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::(entity.title) .bind::, _>(entity.release_type) @@ -725,6 +747,7 @@ impl Server { .bind::, _>(entity.publisher) .bind::, _>(entity.language) .bind::, _>(entity.extra) + .bind::(autoaccept) .bind::(editgroup_id) .get_result(conn)?; @@ -829,6 +852,7 @@ impl Server { pub fn create_work_handler( &self, entity: models::WorkEntity, + autoaccept: bool, conn: &DbConn, ) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth @@ -842,13 +866,14 @@ impl Server { "WITH rev AS ( INSERT INTO work_rev (extra_json) VALUES ($1) RETURNING id ), - ident AS ( INSERT INTO work_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) + ident AS ( INSERT INTO work_ident (is_live, rev_id) + VALUES ($2, (SELECT rev.id FROM rev)) RETURNING id ) INSERT INTO work_edit (editgroup_id, ident_id, rev_id) VALUES - ($2, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::, _>(entity.extra) + .bind::(autoaccept) .bind::(editgroup_id) .get_result(conn)?; diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 95336d3f..4f126385 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -60,7 +60,7 @@ macro_rules! wrap_entity_handlers { _context: &Context, ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.$post_handler(entity, &conn)) { + let ret = match conn.transaction(|| self.$post_handler(entity, false, &conn)) { Ok(edit) => $post_resp::CreatedEntity(edit), Err(Error(ErrorKind::Diesel(e), _)) => @@ -83,10 +83,11 @@ macro_rules! wrap_entity_handlers { fn $post_batch_fn( &self, entity_list: &Vec, + autoaccept: Option, _context: &Context, ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, &conn)) { + let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, autoaccept.unwrap_or(false), &conn)) { Ok(edit) => $post_batch_resp::CreatedEntities(edit), Err(Error(ErrorKind::Diesel(e), _)) => -- cgit v1.2.3 From cc11d5000910b5fd53e40bfd01056c0db60c35aa Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 30 Aug 2018 16:52:03 -0700 Subject: editgroup parameter for batch inserts --- rust/src/api_server.rs | 24 +++++++++++++++++++----- rust/src/api_wrappers.rs | 3 ++- 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 0cb07e81..e8e7d062 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -26,21 +26,35 @@ macro_rules! entity_batch_handler { &self, entity_list: &[models::$model], autoaccept: bool, + editgroup: Option, conn: &DbConn, ) -> Result> { let mut ret: Vec = vec![]; let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) - .values((editgroup::editor_id.eq(editor_id),)) - .get_result(conn)?; + // editgroup override logic based on parameters + let eg_id: Option = match (editgroup, autoaccept) { + (Some(eg_string), _) => Some(Uuid::parse_str(&eg_string)?), + (None, true) => { + let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) + .values((editgroup::editor_id.eq(editor_id),)) + .get_result(conn)?; + Some(eg_row.id) + }, + (None, false) => None + }; for entity in entity_list { let mut e = entity.clone(); - e.editgroup_id = Some(uuid2fcid(&eg_row.id)); + // override individual editgroup IDs (if set earlier) + if let Some(inner_id) = eg_id { + e.editgroup_id = Some(uuid2fcid(&inner_id)); + } + // actual wrapped function call here ret.push(self.$post_handler(e, autoaccept, conn)?); } if autoaccept { + // if autoaccept, eg_id is always Some let _clr: ChangelogRow = diesel::insert_into(changelog::table) - .values((changelog::editgroup_id.eq(eg_row.id),)) + .values((changelog::editgroup_id.eq(eg_id.unwrap()),)) .get_result(conn)?; } Ok(ret) diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 4f126385..da6139d2 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -84,10 +84,11 @@ macro_rules! wrap_entity_handlers { &self, entity_list: &Vec, autoaccept: Option, + editgroup: Option, _context: &Context, ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, autoaccept.unwrap_or(false), &conn)) { + let ret = match conn.transaction(|| self.$post_batch_handler(entity_list, autoaccept.unwrap_or(false), editgroup, &conn)) { Ok(edit) => $post_batch_resp::CreatedEntities(edit), Err(Error(ErrorKind::Diesel(e), _)) => -- cgit v1.2.3 From d046bee7a76576cf14a2815b787247fca8c44433 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 30 Aug 2018 19:36:33 -0700 Subject: editgroup IDs are fcid, not uuid --- rust/src/api_server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index e8e7d062..88fd7063 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -33,7 +33,7 @@ macro_rules! entity_batch_handler { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth // editgroup override logic based on parameters let eg_id: Option = match (editgroup, autoaccept) { - (Some(eg_string), _) => Some(Uuid::parse_str(&eg_string)?), + (Some(eg_string), _) => Some(fcid2uuid(&eg_string)?), (None, true) => { let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) .values((editgroup::editor_id.eq(editor_id),)) -- cgit v1.2.3 From 0053d133f8ff96aa4dedc1ff7e2754812ddfc79a Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 31 Aug 2018 14:43:58 -0700 Subject: correct EditgroupAlreadyAccepted response --- rust/src/api_helpers.rs | 5 +---- rust/src/api_server.rs | 5 ++++- rust/src/api_wrappers.rs | 5 +++++ rust/src/lib.rs | 4 ++++ 4 files changed, 14 insertions(+), 5 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 020aad76..a68ed8a9 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -34,10 +34,7 @@ pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result 0 { - bail!( - "editgroup {} has already been accepted", - editgroup_id.to_string() - ); + return Err(ErrorKind::EditgroupAlreadyAccepted(uuid2fcid(&editgroup_id)).into()); } // for each entity type... diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index b0dff173..7bf3122b 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -216,7 +216,10 @@ fn release_row2entity( let contribs: Vec = release_contrib::table .filter(release_contrib::release_rev.eq(rev.id)) - .order((release_contrib::role.asc(), release_contrib::index_val.asc())) + .order(( + release_contrib::role.asc(), + release_contrib::index_val.asc(), + )) .get_results(conn) .expect("fetch release refs") .into_iter() diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 95336d3f..403c7caf 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -308,6 +308,11 @@ impl Api for Server { message: format!("No such editgroup: {}", id), }) } + Err(Error(ErrorKind::EditgroupAlreadyAccepted(e), _)) => { + AcceptEditgroupResponse::BadRequest(ErrorResponse { + message: ErrorKind::EditgroupAlreadyAccepted(e).to_string(), + }) + } Err(e) => AcceptEditgroupResponse::GenericError(ErrorResponse { message: e.to_string(), }), diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 50a7d410..12ce0973 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -47,6 +47,10 @@ mod errors { description("external identifier doesn't match required pattern") display("external identifier doesn't match required pattern") } + EditgroupAlreadyAccepted(id: String) { + description("editgroup was already accepted") + display("attempted to accept an editgroup which was already accepted: {}", id) + } } } } -- cgit v1.2.3 From 0f229e57965901a1230dd346fb676e8caf67ec2e Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 6 Sep 2018 15:02:57 -0700 Subject: skeleton create/delete endpoints for works Other entities just stubs --- rust/src/api_server.rs | 122 ++++++++++++++++++++++++++++++++++++++++++ rust/src/api_wrappers.rs | 108 ++++++++++++++++++++++++++++++++++++- rust/tests/test_api_server.rs | 65 ++++++++++++++++++++++ 3 files changed, 293 insertions(+), 2 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 7bf3122b..66c31f61 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -535,6 +535,19 @@ impl Server { edit.into_model() } + // XXX: + pub fn update_container_handler( + &self, + id: &Uuid, + entity: models::ContainerEntity, + conn: &DbConn, + ) -> Result { + unimplemented!() + } + pub fn delete_container_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + unimplemented!() + } + pub fn create_creator_handler( &self, entity: models::CreatorEntity, @@ -574,6 +587,19 @@ impl Server { edit.into_model() } + // XXX: + pub fn update_creator_handler( + &self, + id: &Uuid, + entity: models::CreatorEntity, + conn: &DbConn, + ) -> Result { + unimplemented!() + } + pub fn delete_creator_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + unimplemented!() + } + pub fn create_file_handler( &self, entity: models::FileEntity, @@ -654,6 +680,19 @@ impl Server { edit.into_model() } + // XXX: + pub fn update_file_handler( + &self, + id: &Uuid, + entity: models::FileEntity, + conn: &DbConn, + ) -> Result { + unimplemented!() + } + pub fn delete_file_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + unimplemented!() + } + pub fn create_release_handler( &self, entity: models::ReleaseEntity, @@ -829,6 +868,19 @@ impl Server { edit.into_model() } + // XXX: + pub fn update_release_handler( + &self, + id: &Uuid, + entity: models::ReleaseEntity, + conn: &DbConn, + ) -> Result { + unimplemented!() + } + pub fn delete_release_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + unimplemented!() + } + pub fn create_work_handler( &self, entity: models::WorkEntity, @@ -858,6 +910,76 @@ impl Server { edit.into_model() } + pub fn update_work_handler( + &self, + id: &Uuid, + entity: models::WorkEntity, + conn: &DbConn, + ) -> Result { + 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"), + Some(param) => fcid2uuid(¶m)?, + }; + + // TODO: refactor this into a check on WorkIdentRow + let current: WorkIdentRow = work_ident::table.find(id).first(conn)?; + if current.is_live != true { + // TODO: what if isn't live? 4xx not 5xx + bail!("can't delete an entity that doesn't exist yet"); + } + if current.rev_id.is_none() { + // TODO: what if it's already deleted? 4xx not 5xx + bail!("entity was already deleted"); + } + + let edit: WorkEditRow = + diesel::sql_query( + "WITH rev AS ( INSERT INTO work_rev (extra_json) + VALUES ($1) + RETURNING id ), + INSERT INTO work_edit (editgroup_id, ident_id, rev_id, prev_rev) VALUES + ($2, $3, (SELECT rev.id FROM rev), $4) + RETURNING *", + ).bind::, _>(entity.extra) + .bind::(editgroup_id) + .bind::(id) + .bind::(current.rev_id.unwrap()) + .get_result(conn)?; + + edit.into_model() + } + + pub fn delete_work_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth + let editgroup_id = match editgroup_id { + Some(egid) => egid, + None => get_or_create_editgroup(editor_id, conn)? + }; + + let current: WorkIdentRow = work_ident::table.find(id).first(conn)?; + if current.is_live != true { + // TODO: what if isn't live? 4xx not 5xx + bail!("can't delete an entity that doesn't exist yet"); + } + if current.rev_id.is_none() { + // TODO: what if it's already deleted? 4xx not 5xx + bail!("entity was already deleted"); + } + let edit: WorkEditRow = insert_into(work_edit::table) + .values(( + work_edit::editgroup_id.eq(editgroup_id), + work_edit::ident_id.eq(id), + work_edit::rev_id.eq(None::), + work_edit::redirect_id.eq(None::), + work_edit::prev_rev.eq(current.rev_id), + //work_edit::extra_json.eq(extra), + )) + .get_result(conn)?; + + edit.into_model() + } + pub fn accept_editgroup_handler(&self, id: &str, conn: &DbConn) -> Result<()> { accept_editgroup(fcid2uuid(id)?, conn)?; Ok(()) diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 403c7caf..de1fc9da 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -19,8 +19,9 @@ macro_rules! wrap_entity_handlers { // 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, $post_batch_fn:ident, $post_batch_handler:ident, - $post_batch_resp:ident, $get_history_fn:ident, $get_history_handler:ident, - $get_history_resp:ident, $model:ident) => { + $post_batch_resp:ident, $update_fn:ident, $update_handler:ident, $update_resp:ident, + $delete_fn:ident, $delete_handler:ident, $delete_resp:ident, $get_history_fn:ident, + $get_history_handler:ident, $get_history_resp:ident, $model:ident) => { fn $get_fn( &self, @@ -106,6 +107,79 @@ macro_rules! wrap_entity_handlers { Box::new(futures::done(Ok(ret))) } + fn $update_fn( + &self, + id: String, + entity: models::$model, + _context: &Context, + ) -> Box + Send> { + let id = if let Ok(parsed) = fcid2uuid(&id) { parsed } else { + return Box::new(futures::done(Ok($update_resp::BadRequest(ErrorResponse { + message: ErrorKind::InvalidFatcatId(id).to_string() })))); + }; + let conn = self.db_pool.get().expect("db_pool error"); + let ret = match conn.transaction(|| self.$update_handler(&id, entity, &conn)) { + Ok(edit) => + $update_resp::UpdatedEntity(edit), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $update_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }), + Err(Error(ErrorKind::Diesel(e), _)) => + $update_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::Uuid(e), _)) => + $update_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $update_resp::BadRequest(ErrorResponse { + message: ErrorKind::InvalidFatcatId(e).to_string() }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $update_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => { + error!("{}", e); + $update_resp::GenericError(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + + fn $delete_fn( + &self, + id: String, + editgroup_id: Option, + _context: &Context, + ) -> Box + Send> { + let id = if let Ok(parsed) = fcid2uuid(&id) { parsed } else { + return Box::new(futures::done(Ok($delete_resp::BadRequest(ErrorResponse { + message: ErrorKind::InvalidFatcatId(id).to_string() })))); + }; + let editgroup_id = match editgroup_id { + Some(raw) => if let Ok(parsed) = fcid2uuid(&raw) { Some(parsed) } else { + return Box::new(futures::done(Ok($delete_resp::BadRequest(ErrorResponse { + message: ErrorKind::InvalidFatcatId(raw).to_string() })))) + } + None => None + }; + let conn = self.db_pool.get().expect("db_pool error"); + let ret = match conn.transaction(|| self.$delete_handler(&id, editgroup_id, &conn)) { + Ok(edit) => + $delete_resp::DeletedEntity(edit), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $delete_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }), + Err(Error(ErrorKind::Diesel(e), _)) => + $delete_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::Uuid(e), _)) => + $delete_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(Error(ErrorKind::InvalidFatcatId(e), _)) => + $delete_resp::BadRequest(ErrorResponse { + message: ErrorKind::InvalidFatcatId(e).to_string() }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $delete_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => { + error!("{}", e); + $delete_resp::GenericError(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + fn $get_history_fn( &self, id: String, @@ -175,6 +249,12 @@ impl Api for Server { create_container_batch, create_container_batch_handler, CreateContainerBatchResponse, + update_container, + update_container_handler, + UpdateContainerResponse, + delete_container, + delete_container_handler, + DeleteContainerResponse, get_container_history, get_container_history_handler, GetContainerHistoryResponse, @@ -191,6 +271,12 @@ impl Api for Server { create_creator_batch, create_creator_batch_handler, CreateCreatorBatchResponse, + update_creator, + update_creator_handler, + UpdateCreatorResponse, + delete_creator, + delete_creator_handler, + DeleteCreatorResponse, get_creator_history, get_creator_history_handler, GetCreatorHistoryResponse, @@ -206,6 +292,12 @@ impl Api for Server { create_file_batch, create_file_batch_handler, CreateFileBatchResponse, + update_file, + update_file_handler, + UpdateFileResponse, + delete_file, + delete_file_handler, + DeleteFileResponse, get_file_history, get_file_history_handler, GetFileHistoryResponse, @@ -221,6 +313,12 @@ impl Api for Server { create_release_batch, create_release_batch_handler, CreateReleaseBatchResponse, + update_release, + update_release_handler, + UpdateReleaseResponse, + delete_release, + delete_release_handler, + DeleteReleaseResponse, get_release_history, get_release_history_handler, GetReleaseHistoryResponse, @@ -236,6 +334,12 @@ impl Api for Server { create_work_batch, create_work_batch_handler, CreateWorkBatchResponse, + update_work, + update_work_handler, + UpdateWorkResponse, + delete_work, + delete_work_handler, + DeleteWorkResponse, get_work_history, get_work_history_handler, GetWorkHistoryResponse, diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs index 02c77413..4b181188 100644 --- a/rust/tests/test_api_server.rs +++ b/rust/tests/test_api_server.rs @@ -468,6 +468,71 @@ fn test_post_work() { ); } +#[test] +fn test_update_work() { + let (headers, router, conn) = setup(); + + check_response( + request::post( + "http://localhost:9411/v0/work", + headers.clone(), + r#"{ + "extra": { "source": "other speculation" } + }"#, + &router, + ), + status::Created, + None, + ); + + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001").unwrap(); + let editgroup_id = get_or_create_editgroup(editor_id, &conn).unwrap(); + check_response( + request::post( + &format!( + "http://localhost:9411/v0/editgroup/{}/accept", + uuid2fcid(&editgroup_id) + ), + headers.clone(), + "", + &router, + ), + status::Ok, + None, + ); +} + +#[test] +fn test_delete_work() { + let (headers, router, conn) = setup(); + + check_response( + request::delete( + "http://localhost:9411/v0/work/aaaaaaaaaaaaavkvaaaaaaaaai", + headers.clone(), + &router, + ), + status::Ok, + None, + ); + + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001").unwrap(); + let editgroup_id = get_or_create_editgroup(editor_id, &conn).unwrap(); + check_response( + request::post( + &format!( + "http://localhost:9411/v0/editgroup/{}/accept", + uuid2fcid(&editgroup_id) + ), + headers.clone(), + "", + &router, + ), + status::Ok, + None, + ); +} + #[test] fn test_accept_editgroup() { let (headers, router, conn) = setup(); -- cgit v1.2.3 From 18eae2395863e32e4b7413010adafe1ffc95076e Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 10:49:15 -0700 Subject: major CRUD refactor This is the start of a large refactor to move all entity CRUD (create, read, update, delete) model/database code into it's own file. HACKING has been updated with an overview of what happens in each file. Next steps: - split rev (and sub-table) insertion in to db_rev_insert and make create/update generic - inserts should be batch (vector) by default - move all other entities into this new trait framework - bypass api_server wrappers and call into CRUD from api_wrappers for entity ops (should be a big cleanup) --- rust/HACKING.md | 34 ++++++ rust/src/api_helpers.rs | 27 +++++ rust/src/api_server.rs | 109 ++++------------- rust/src/database_entity_crud.rs | 250 +++++++++++++++++++++++++++++++++++++++ rust/src/lib.rs | 1 + 5 files changed, 336 insertions(+), 85 deletions(-) create mode 100644 rust/src/database_entity_crud.rs (limited to 'rust/src') diff --git a/rust/HACKING.md b/rust/HACKING.md index a399164c..622a4b5a 100644 --- a/rust/HACKING.md +++ b/rust/HACKING.md @@ -1,4 +1,38 @@ +## Code Structure + +Almost all of the rust code is either auto-generated or "glue" between the +swagger API spec on one side and the SQL schema on the other. + +- `./migrations/*/up.sql`: SQL schema +- `./src/database_schema.rs`: autogenerated per-table Diesel schemas +- `./src/database_models.rs`: hand- and macro-generated Rust structs matching + Diesel schemas, and a small number of row-level helpers +- `./src/database_entity_crud.rs`: "struct-relational-mapping"; trait + implementations of CRUD (create, read, update, delete) actions for each + entity model, hitting the database (and building on `database_model` structs) +- `./src/api_server.rs`: one function for each API endpoint, with rust-style + arguments and return types. mostly calls in to `database_entity_crud`. +- `./src/api_wrappers.rs`: hand- and macro-generated wrapper functions, one per + API endpoint, that map between API request and return types, and + rust-idiomatic request and return types (plus API models). +- `./fatcat-api`: autogenerated API models and endpoint types/signatures +- `../fatcat-openapi2.yaml`: + +When deciding to use this structure, it wasn't expected that either +`api_wrappers.rs` or `database_models.rs` would need to hand-maintained; both +are verbose and implemented in a very mechanical fashion. The return type +mapping in `api_wrappers` might be necessary, but `database_models.rs` in +particular feels unnecessary; other projects have attempted to completely +automate generation of this file, but it doesn't sound reliable. In particular, +both regular "Row" (queriable) and "NewRow" (insertable) structs need to be +defined. + +## Test Structure + +- `./tests/test_api_server.rs`: Iron (web framework) level raw HTTP JSON + request/response tests of API endpoints. + ## Updating Schemas Regenerate API schemas after editing the fatcat-openapi2 schema. This will, as diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index a68ed8a9..6c9a4e5f 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -6,6 +6,9 @@ use diesel::prelude::*; use errors::*; use regex::Regex; use uuid::Uuid; +use std::str::FromStr; + +pub type DbConn = diesel::r2d2::PooledConnection>; /// This function should always be run within a transaction pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result { @@ -87,6 +90,30 @@ pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result String { + uuid2fcid(&self.to_uuid()) + } +} + +impl FromStr for FatCatId { + type Err = Error; + fn from_str(s: &str) -> Result { + fcid2uuid(s).map(|u| FatCatId(u)) + } +} + +impl FatCatId { + pub fn to_uuid(&self) -> Uuid { + self.0 + } + pub fn from_uuid(u: &Uuid) -> FatCatId { + FatCatId(u.clone()) + } +} + /// Convert fatcat IDs (base32 strings) to UUID pub fn fcid2uuid(fcid: &str) -> Result { if fcid.len() != 26 { diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 66c31f61..20f71c7a 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -17,8 +17,8 @@ use fatcat_api::models::*; use sha1::Sha1; use uuid::Uuid; use ConnectionPool; - -type DbConn = diesel::r2d2::PooledConnection>; +use database_entity_crud::{EntityCrud, EditContext}; +use std::str::FromStr; macro_rules! entity_batch_handler { ($post_handler:ident, $post_batch_handler:ident, $model:ident) => { @@ -77,6 +77,19 @@ macro_rules! count_entity { }}; } +fn make_edit_context(conn: &DbConn, editgroup_id: Option) -> Result { + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth + let editgroup_id = match editgroup_id { + None => FatCatId::from_uuid(&get_or_create_editgroup(editor_id, conn).expect("current editgroup")), + Some(param) => param, + }; + Ok(EditContext { + editor_id: FatCatId::from_uuid(&editor_id), + editgroup_id: editgroup_id, + extra_json: None, + }) +} + #[derive(Clone)] pub struct Server { pub db_pool: ConnectionPool, @@ -281,6 +294,7 @@ fn release_row2entity( }) } +/* XXX: fn work_row2entity(ident: Option, rev: WorkRevRow) -> Result { let (state, ident_id, redirect_id) = match ident { Some(i) => ( @@ -299,6 +313,7 @@ fn work_row2entity(ident: Option, rev: WorkRevRow) -> Result, conn: &DbConn, ) -> Result { - let (ident, rev): (WorkIdentRow, WorkRevRow) = work_ident::table - .find(id) - .inner_join(work_rev::table) - .first(conn)?; - - work_row2entity(Some(ident), rev) + WorkEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn get_work_releases_handler(&self, id: &str, conn: &DbConn) -> Result> { @@ -886,27 +896,8 @@ impl Server { entity: models::WorkEntity, conn: &DbConn, ) -> Result { - 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"), - Some(param) => fcid2uuid(¶m)?, - }; - - let edit: WorkEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (extra_json) - VALUES ($1) - 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 - ($2, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::, _>(entity.extra) - .bind::(editgroup_id) - .get_result(conn)?; - + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } @@ -916,66 +907,14 @@ impl Server { entity: models::WorkEntity, conn: &DbConn, ) -> Result { - 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"), - Some(param) => fcid2uuid(¶m)?, - }; - - // TODO: refactor this into a check on WorkIdentRow - let current: WorkIdentRow = work_ident::table.find(id).first(conn)?; - if current.is_live != true { - // TODO: what if isn't live? 4xx not 5xx - bail!("can't delete an entity that doesn't exist yet"); - } - if current.rev_id.is_none() { - // TODO: what if it's already deleted? 4xx not 5xx - bail!("entity was already deleted"); - } - - let edit: WorkEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (extra_json) - VALUES ($1) - RETURNING id ), - INSERT INTO work_edit (editgroup_id, ident_id, rev_id, prev_rev) VALUES - ($2, $3, (SELECT rev.id FROM rev), $4) - RETURNING *", - ).bind::, _>(entity.extra) - .bind::(editgroup_id) - .bind::(id) - .bind::(current.rev_id.unwrap()) - .get_result(conn)?; - + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } pub fn delete_work_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id = match editgroup_id { - Some(egid) => egid, - None => get_or_create_editgroup(editor_id, conn)? - }; - - let current: WorkIdentRow = work_ident::table.find(id).first(conn)?; - if current.is_live != true { - // TODO: what if isn't live? 4xx not 5xx - bail!("can't delete an entity that doesn't exist yet"); - } - if current.rev_id.is_none() { - // TODO: what if it's already deleted? 4xx not 5xx - bail!("entity was already deleted"); - } - let edit: WorkEditRow = insert_into(work_edit::table) - .values(( - work_edit::editgroup_id.eq(editgroup_id), - work_edit::ident_id.eq(id), - work_edit::rev_id.eq(None::), - work_edit::redirect_id.eq(None::), - work_edit::prev_rev.eq(current.rev_id), - //work_edit::extra_json.eq(extra), - )) - .get_result(conn)?; + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = WorkEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs new file mode 100644 index 00000000..d532d166 --- /dev/null +++ b/rust/src/database_entity_crud.rs @@ -0,0 +1,250 @@ + +use diesel::prelude::*; +use diesel::{self, insert_into}; +use database_schema::*; +use database_models::*; +use errors::*; +use fatcat_api::models::*; +use api_helpers::{FatCatId, DbConn}; +use uuid::Uuid; +use std::marker::Sized; +use std::str::FromStr; +use serde_json; + +pub struct EditContext { + pub editor_id: FatCatId, + pub editgroup_id: FatCatId, + pub extra_json: Option, +} + +/* One goal here is to abstract the non-entity-specific bits into generic traits or functions, + * instead of macros. + * + * Notably: + * + * db_get + * db_get_rev + * db_create + * db_create_batch + * db_update + * db_delete + * db_get_history + * + * For now, these will probably be macros, until we can level up our trait/generics foo. + */ + +// Associated Type, not parametric +pub trait EntityCrud where Self: Sized { + type EditRow; // EntityEditRow + type IdentRow; // EntityIdentRow + type RevRow; + + fn parse_editgroup_id(&self) -> Result>; + + // Generic Methods + fn db_get(conn: &DbConn, ident: FatCatId) -> Result; + fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result; + fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result; + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result>; + fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; + fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; + fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result>; + + // Entity-specific Methods + fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; + fn db_insert_rev(&self, conn: &DbConn, edit_context: &EditContext) -> Result; +} + +// TODO: this could be a separate trait on all entities? +macro_rules! generic_parse_editgroup_id{ + () => { + fn parse_editgroup_id(&self) -> Result> { + match &self.editgroup_id { + Some(s) => Ok(Some(FatCatId::from_str(&s)?)), + None => Ok(None), + } + } + } +} + +macro_rules! generic_db_get { + ($ident_table: ident, $rev_table: ident) => { + fn db_get(conn: &DbConn, ident: FatCatId) -> Result { + let (ident, rev): (Self::IdentRow, Self::RevRow) = $ident_table::table + .find(ident.to_uuid()) + .inner_join($rev_table::table) + .first(conn)?; + + Self::db_from_row(conn, rev, Some(ident)) + } + } +} + +macro_rules! generic_db_get_rev { + ($rev_table: ident) => { + fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result { + let rev = $rev_table::table + .find(rev_id) + .first(conn)?; + + Self::db_from_row(conn, rev, None) + } + } +} + +macro_rules! generic_db_create_batch { + () => { + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result> { + let mut ret: Vec = vec![]; + for entity in models { + ret.push(entity.db_create(conn, edit_context)?); + } + Ok(ret) + } + } +} + +macro_rules! generic_db_delete { + ($ident_table: ident, $edit_table:ident) => { + fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result { + + let current: Self::IdentRow = $ident_table::table.find(ident.to_uuid()).first(conn)?; + if current.is_live != true { + // TODO: what if isn't live? 4xx not 5xx + bail!("can't delete an entity that doesn't exist yet"); + } + if current.rev_id.is_none() { + // TODO: what if it's already deleted? 4xx not 5xx + bail!("entity was already deleted"); + } + let edit: Self::EditRow = insert_into($edit_table::table) + .values(( + $edit_table::editgroup_id.eq(edit_context.editgroup_id.to_uuid()), + $edit_table::ident_id.eq(ident.to_uuid()), + $edit_table::rev_id.eq(None::), + $edit_table::redirect_id.eq(None::), + $edit_table::prev_rev.eq(current.rev_id), + $edit_table::extra_json.eq(&edit_context.extra_json), + )) + .get_result(conn)?; + + Ok(edit) + } + } +} + +macro_rules! generic_db_get_history { + // TODO: only actually need edit table? and maybe not that? + ($edit_table:ident) => { + fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result> { + let limit = limit.unwrap_or(50); // XXX: make a static + + let rows: Vec<(EditgroupRow, ChangelogRow, Self::EditRow)> = editgroup::table + .inner_join(changelog::table) + .inner_join($edit_table::table) + .filter($edit_table::ident_id.eq(ident.to_uuid())) + .order(changelog::id.desc()) + .limit(limit) + .get_results(conn)?; + + let history: Vec = rows.into_iter() + .map(|(eg_row, cl_row, e_row)| EntityHistoryEntry { + edit: e_row.into_model().expect("edit row to model"), + editgroup: eg_row.into_model_partial(), + changelog_entry: cl_row.into_model(), + }) + .collect(); + Ok(history) + } + } +} + +impl EntityCrud for WorkEntity { + type EditRow = WorkEditRow; + type IdentRow = WorkIdentRow; + type RevRow = WorkRevRow; + + generic_parse_editgroup_id!(); + generic_db_get!(work_ident, work_rev); + generic_db_get_rev!(work_rev); + generic_db_create_batch!(); + generic_db_delete!(work_ident, work_edit); + generic_db_get_history!(work_edit); + + fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result { + + // TODO: refactor to use insert_rev + let edit: WorkEditRow = + diesel::sql_query( + "WITH rev AS ( INSERT INTO work_rev (extra_json) + VALUES ($1) + 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 + ($2, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + RETURNING *", + ).bind::, _>(&self.extra) + .bind::(edit_context.editgroup_id.to_uuid()) + .get_result(conn)?; + + Ok(edit) + } + + fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result { + // TODO: refactor this into a check on WorkIdentRow + let current: WorkIdentRow = work_ident::table.find(ident.to_uuid()).first(conn)?; + if current.is_live != true { + // TODO: what if isn't live? 4xx not 5xx + bail!("can't delete an entity that doesn't exist yet"); + } + if current.rev_id.is_none() { + // TODO: what if it's already deleted? 4xx not 5xx + bail!("entity was already deleted"); + } + + let edit: WorkEditRow = + diesel::sql_query( + "WITH rev AS ( INSERT INTO work_rev (extra_json) + VALUES ($1) + RETURNING id ), + INSERT INTO work_edit (editgroup_id, ident_id, rev_id, prev_rev) VALUES + ($2, $3, (SELECT rev.id FROM rev), $4) + RETURNING *", + ).bind::, _>(&self.extra) + .bind::(edit_context.editgroup_id.to_uuid()) + .bind::(ident.to_uuid()) + .bind::(current.rev_id.unwrap()) + .get_result(conn)?; + + Ok(edit) + } + + + fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + + let (state, ident_id, redirect_id) = match ident_row { + Some(i) => ( + Some(i.state().unwrap().shortname()), + Some(FatCatId::from_uuid(&i.id).to_string()), + i.redirect_id.map(|u| FatCatId::from_uuid(&u).to_string()), + ), + None => (None, None, None), + }; + + Ok(WorkEntity { + state: state, + ident: ident_id, + revision: Some(rev_row.id.to_string()), + redirect: redirect_id, + editgroup_id: None, + extra: rev_row.extra_json, + }) + } + + fn db_insert_rev(&self, conn: &DbConn, edit_context: &EditContext) -> Result { + unimplemented!() + } +} + diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 12ce0973..a938486b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -27,6 +27,7 @@ pub mod api_server; pub mod api_wrappers; pub mod database_models; pub mod database_schema; +pub mod database_entity_crud; mod errors { // Create the Error, ErrorKind, ResultExt, and Result types -- cgit v1.2.3 From 21a40354a9c0684f0bbb6f0b3211aeafc05229b3 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 14:02:47 -0700 Subject: crud: refactor to split out rev insert --- rust/src/database_entity_crud.rs | 94 ++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 43 deletions(-) (limited to 'rust/src') diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index d532d166..7a5f333d 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -52,7 +52,7 @@ pub trait EntityCrud where Self: Sized { // Entity-specific Methods fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; - fn db_insert_rev(&self, conn: &DbConn, edit_context: &EditContext) -> Result; + fn db_insert_rev(&self, conn: &DbConn) -> Result; } // TODO: this could be a separate trait on all entities? @@ -104,6 +104,35 @@ macro_rules! generic_db_create_batch { } } +macro_rules! generic_db_update { + ($ident_table: ident, $edit_table: ident) => { + fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result { + let current: Self::IdentRow = $ident_table::table.find(ident.to_uuid()).first(conn)?; + if current.is_live != true { + // TODO: what if isn't live? 4xx not 5xx + bail!("can't delete an entity that doesn't exist yet"); + } + if current.rev_id.is_none() { + // TODO: what if it's already deleted? 4xx not 5xx + bail!("entity was already deleted"); + } + + let rev_id = self.db_insert_rev(conn)?; + let edit: Self::EditRow = insert_into($edit_table::table) + .values(( + $edit_table::editgroup_id.eq(edit_context.editgroup_id.to_uuid()), + $edit_table::ident_id.eq(&ident.to_uuid()), + $edit_table::rev_id.eq(&rev_id), + $edit_table::prev_rev.eq(current.rev_id.unwrap()), + $edit_table::extra_json.eq(&self.extra), + )) + .get_result(conn)?; + + Ok(edit) + } + } +} + macro_rules! generic_db_delete { ($ident_table: ident, $edit_table:ident) => { fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result { @@ -168,60 +197,30 @@ impl EntityCrud for WorkEntity { generic_db_get!(work_ident, work_rev); generic_db_get_rev!(work_rev); generic_db_create_batch!(); + generic_db_update!(work_ident, work_edit); generic_db_delete!(work_ident, work_edit); generic_db_get_history!(work_edit); fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result { - // TODO: refactor to use insert_rev + let rev_id = self.db_insert_rev(conn)?; let edit: WorkEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (extra_json) - VALUES ($1) - 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 - ($2, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) + "WITH ident AS ( + INSERT INTO work_ident (rev_id) + VALUES ($1) + RETURNING id ) + INSERT INTO work_edit (editgroup_id, ident_id, rev_id, extra) VALUES + ($2, (SELECT ident.id FROM ident), $1, $3) RETURNING *", - ).bind::, _>(&self.extra) + ).bind::(rev_id) .bind::(edit_context.editgroup_id.to_uuid()) + .bind::, _>(&edit_context.extra_json) .get_result(conn)?; Ok(edit) } - fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result { - // TODO: refactor this into a check on WorkIdentRow - let current: WorkIdentRow = work_ident::table.find(ident.to_uuid()).first(conn)?; - if current.is_live != true { - // TODO: what if isn't live? 4xx not 5xx - bail!("can't delete an entity that doesn't exist yet"); - } - if current.rev_id.is_none() { - // TODO: what if it's already deleted? 4xx not 5xx - bail!("entity was already deleted"); - } - - let edit: WorkEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO work_rev (extra_json) - VALUES ($1) - RETURNING id ), - INSERT INTO work_edit (editgroup_id, ident_id, rev_id, prev_rev) VALUES - ($2, $3, (SELECT rev.id FROM rev), $4) - RETURNING *", - ).bind::, _>(&self.extra) - .bind::(edit_context.editgroup_id.to_uuid()) - .bind::(ident.to_uuid()) - .bind::(current.rev_id.unwrap()) - .get_result(conn)?; - - Ok(edit) - } - - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { let (state, ident_id, redirect_id) = match ident_row { @@ -243,8 +242,17 @@ impl EntityCrud for WorkEntity { }) } - fn db_insert_rev(&self, conn: &DbConn, edit_context: &EditContext) -> Result { - unimplemented!() + fn db_insert_rev(&self, conn: &DbConn) -> Result { + let rev_row: WorkRevRow = insert_into(work_rev::table) + .values(( + work_rev::extra_json.eq(&self.extra) + )) + .get_result(conn)?; + /* TODO: only return id + .returning(work_rev::id) + .first(conn)?; + */ + Ok(rev_row.id) } } -- cgit v1.2.3 From cdf46b6a5246b8d750bb7aba60acebca11507540 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 14:21:38 -0700 Subject: generic create (multiple inserts) This does away entirely with the fancy CTE-based custom SQL inserts. Because performance is currently only important for batch inserts, I think this is acceptable, and will refactor to do batch inserts as actual batch SQL operations next. --- rust/src/database_entity_crud.rs | 50 +++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 26 deletions(-) (limited to 'rust/src') diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index 7a5f333d..24d5277c 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -92,6 +92,26 @@ macro_rules! generic_db_get_rev { } } +macro_rules! generic_db_create { + ($ident_table: ident, $edit_table: ident) => { + fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result { + let rev_id = self.db_insert_rev(conn)?; + let ident: Uuid = insert_into($ident_table::table) + .values($ident_table::rev_id.eq(&rev_id)) + .returning($ident_table::id) + .get_result(conn)?; + let edit: Self::EditRow = insert_into($edit_table::table) + .values(( + $edit_table::editgroup_id.eq(edit_context.editgroup_id.to_uuid()), + $edit_table::rev_id.eq(&rev_id), + $edit_table::ident_id.eq(&ident), + )) + .get_result(conn)?; + Ok(edit) + } + } +} + macro_rules! generic_db_create_batch { () => { fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result> { @@ -196,31 +216,12 @@ impl EntityCrud for WorkEntity { generic_parse_editgroup_id!(); generic_db_get!(work_ident, work_rev); generic_db_get_rev!(work_rev); + generic_db_create!(work_ident, work_edit); generic_db_create_batch!(); generic_db_update!(work_ident, work_edit); generic_db_delete!(work_ident, work_edit); generic_db_get_history!(work_edit); - fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result { - - let rev_id = self.db_insert_rev(conn)?; - let edit: WorkEditRow = - diesel::sql_query( - "WITH ident AS ( - INSERT INTO work_ident (rev_id) - VALUES ($1) - RETURNING id ) - INSERT INTO work_edit (editgroup_id, ident_id, rev_id, extra) VALUES - ($2, (SELECT ident.id FROM ident), $1, $3) - RETURNING *", - ).bind::(rev_id) - .bind::(edit_context.editgroup_id.to_uuid()) - .bind::, _>(&edit_context.extra_json) - .get_result(conn)?; - - Ok(edit) - } - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { let (state, ident_id, redirect_id) = match ident_row { @@ -243,16 +244,13 @@ impl EntityCrud for WorkEntity { } fn db_insert_rev(&self, conn: &DbConn) -> Result { - let rev_row: WorkRevRow = insert_into(work_rev::table) + let rev_row: Uuid = insert_into(work_rev::table) .values(( work_rev::extra_json.eq(&self.extra) )) - .get_result(conn)?; - /* TODO: only return id .returning(work_rev::id) - .first(conn)?; - */ - Ok(rev_row.id) + .get_result(conn)?; + Ok(rev_row) } } -- cgit v1.2.3 From e73b56f9354596c556cfbb2d45584a6bb86ad60e Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 16:00:54 -0700 Subject: batch inserts by default --- rust/src/api_server.rs | 1 + rust/src/database_entity_crud.rs | 76 +++++++++++++++++++++++++++++----------- rust/src/database_models.rs | 45 ++++++++++++++++++++---- 3 files changed, 95 insertions(+), 27 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 20f71c7a..5060ba0f 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -87,6 +87,7 @@ fn make_edit_context(conn: &DbConn, editgroup_id: Option) -> Result, + pub autoapprove: bool, } /* One goal here is to abstract the non-entity-specific bits into generic traits or functions, @@ -35,8 +36,11 @@ pub struct EditContext { // Associated Type, not parametric pub trait EntityCrud where Self: Sized { + // TODO: could these be generic structs? Or do they need to be bound to a specific table? type EditRow; // EntityEditRow + type EditNewRow; type IdentRow; // EntityIdentRow + type IdentNewRow; type RevRow; fn parse_editgroup_id(&self) -> Result>; @@ -45,14 +49,15 @@ pub trait EntityCrud where Self: Sized { fn db_get(conn: &DbConn, ident: FatCatId) -> Result; fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result; fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result; - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result>; + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result>; fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result>; // Entity-specific Methods - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; + fn from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; fn db_insert_rev(&self, conn: &DbConn) -> Result; + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result>; } // TODO: this could be a separate trait on all entities? @@ -75,7 +80,7 @@ macro_rules! generic_db_get { .inner_join($rev_table::table) .first(conn)?; - Self::db_from_row(conn, rev, Some(ident)) + Self::from_row(conn, rev, Some(ident)) } } } @@ -87,7 +92,7 @@ macro_rules! generic_db_get_rev { .find(rev_id) .first(conn)?; - Self::db_from_row(conn, rev, None) + Self::from_row(conn, rev, None) } } } @@ -113,13 +118,32 @@ macro_rules! generic_db_create { } macro_rules! generic_db_create_batch { - () => { - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[Self]) -> Result> { - let mut ret: Vec = vec![]; - for entity in models { - ret.push(entity.db_create(conn, edit_context)?); - } - Ok(ret) + ($ident_table: ident, $edit_table: ident) => { + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result> { + let rev_ids: Vec = Self::db_insert_revs(conn, models)?; + let ident_ids: Vec = insert_into($ident_table::table) + .values(rev_ids.iter() + .map(|rev_id| Self::IdentNewRow { + rev_id: Some(rev_id.clone()), + is_live: edit_context.autoapprove, + redirect_id: None, + }) + .collect::>()) + .returning($ident_table::id) + .get_results(conn)?; + let edits: Vec = insert_into($edit_table::table) + .values(rev_ids.into_iter().zip(ident_ids.into_iter()) + .map(|(rev_id, ident_id)| Self::EditNewRow { + editgroup_id: edit_context.editgroup_id.to_uuid(), + rev_id: Some(rev_id), + ident_id: ident_id, + redirect_id: None, + prev_rev: None, + extra_json: edit_context.extra_json.clone(), + }) + .collect::>()) + .get_results(conn)?; + Ok(edits) } } } @@ -183,7 +207,6 @@ macro_rules! generic_db_delete { } macro_rules! generic_db_get_history { - // TODO: only actually need edit table? and maybe not that? ($edit_table:ident) => { fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result> { let limit = limit.unwrap_or(50); // XXX: make a static @@ -208,21 +231,32 @@ macro_rules! generic_db_get_history { } } +macro_rules! generic_db_insert_rev { + () => { + fn db_insert_rev(&self, conn: &DbConn) -> Result { + Self::db_insert_revs(conn, &vec![self]).map(|id_list| id_list[0]) + } + } +} + impl EntityCrud for WorkEntity { type EditRow = WorkEditRow; + type EditNewRow = WorkEditNewRow; type IdentRow = WorkIdentRow; + type IdentNewRow = WorkIdentNewRow; type RevRow = WorkRevRow; generic_parse_editgroup_id!(); generic_db_get!(work_ident, work_rev); generic_db_get_rev!(work_rev); generic_db_create!(work_ident, work_edit); - generic_db_create_batch!(); + generic_db_create_batch!(work_ident, work_edit); generic_db_update!(work_ident, work_edit); generic_db_delete!(work_ident, work_edit); generic_db_get_history!(work_edit); + generic_db_insert_rev!(); - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + fn from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( @@ -243,14 +277,14 @@ impl EntityCrud for WorkEntity { }) } - fn db_insert_rev(&self, conn: &DbConn) -> Result { - let rev_row: Uuid = insert_into(work_rev::table) - .values(( - work_rev::extra_json.eq(&self.extra) - )) + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { + let rev_ids: Vec = insert_into(work_rev::table) + .values(models.iter() + .map(|model| WorkRevNewRow { extra_json: model.extra.clone() } ) + .collect::>()) .returning(work_rev::id) - .get_result(conn)?; - Ok(rev_row) + .get_results(conn)?; + Ok(rev_ids) } } diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 47e00bcf..14215a3c 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -37,7 +37,9 @@ pub trait EntityEditRow { // Helper for constructing tables macro_rules! entity_structs { - ($edit_table:expr, $edit_struct:ident, $ident_table:expr, $ident_struct:ident) => { + ($edit_table:expr, $edit_struct:ident, $edit_new_struct:ident, $ident_table:expr, + $ident_struct:ident, $ident_new_struct:ident) => { + #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset, QueryableByName)] #[table_name = $edit_table] pub struct $edit_struct { @@ -51,6 +53,17 @@ macro_rules! entity_structs { pub extra_json: Option, } + #[derive(Debug, Associations, AsChangeset, QueryableByName, Insertable)] + #[table_name = $edit_table] + pub struct $edit_new_struct { + pub editgroup_id: Uuid, + pub ident_id: Uuid, + pub rev_id: Option, + pub redirect_id: Option, + pub prev_rev: Option, + pub extra_json: Option, + } + impl EntityEditRow for $edit_struct { /// Go from a row (SQL model) to an API model fn into_model(self) -> Result { @@ -75,6 +88,14 @@ macro_rules! entity_structs { pub redirect_id: Option, } + #[derive(Debug, Associations, AsChangeset, Insertable)] + #[table_name = $ident_table] + pub struct $ident_new_struct { + pub is_live: bool, + pub rev_id: Option, + pub redirect_id: Option, + } + impl EntityIdentRow for $ident_struct { fn state(&self) -> Result { if !self.is_live { @@ -107,8 +128,10 @@ pub struct ContainerRevRow { entity_structs!( "container_edit", ContainerEditRow, + ContainerEditNewRow, "container_ident", - ContainerIdentRow + ContainerIdentRow, + ContainerIdentNewRow ); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -126,8 +149,10 @@ pub struct CreatorRevRow { entity_structs!( "creator_edit", CreatorEditRow, + CreatorEditNewRow, "creator_ident", - CreatorIdentRow + CreatorIdentRow, + CreatorIdentNewRow ); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -159,7 +184,7 @@ pub struct FileRevRow { pub mimetype: Option, } -entity_structs!("file_edit", FileEditRow, "file_ident", FileIdentRow); +entity_structs!("file_edit", FileEditRow, FileEditNewRow, "file_ident", FileIdentRow, FileIdentNewRow); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "release_rev"] @@ -188,8 +213,10 @@ pub struct ReleaseRevRow { entity_structs!( "release_edit", ReleaseEditRow, + ReleaseEditNewRow, "release_ident", - ReleaseIdentRow + ReleaseIdentRow, + ReleaseIdentNewRow ); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -199,7 +226,13 @@ pub struct WorkRevRow { pub extra_json: Option, } -entity_structs!("work_edit", WorkEditRow, "work_ident", WorkIdentRow); +#[derive(Debug, Associations, AsChangeset, Insertable)] +#[table_name = "work_rev"] +pub struct WorkRevNewRow { + pub extra_json: Option, +} + +entity_structs!("work_edit", WorkEditRow, WorkEditNewRow, "work_ident", WorkIdentRow, WorkIdentNewRow); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "release_rev_abstract"] -- cgit v1.2.3 From 47cb21bdc31466dd827800898a4ad543a6297696 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 18:57:11 -0700 Subject: mostly done with CRUD refactor One failing test in this commit. --- rust/src/api_server.rs | 687 ++++----------------------------------- rust/src/database_entity_crud.rs | 626 ++++++++++++++++++++++++++++++++++- rust/src/database_models.rs | 57 ++++ 3 files changed, 745 insertions(+), 625 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 5060ba0f..ecf0c242 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -21,47 +21,16 @@ use database_entity_crud::{EntityCrud, EditContext}; use std::str::FromStr; macro_rules! entity_batch_handler { - ($post_handler:ident, $post_batch_handler:ident, $model:ident) => { + ($post_batch_handler:ident, $model:ident) => { pub fn $post_batch_handler( &self, entity_list: &[models::$model], conn: &DbConn, ) -> Result> { - let mut ret: Vec = vec![]; - for entity in entity_list { - ret.push(self.$post_handler(entity.clone(), conn)?); - } - Ok(ret) - } - } -} - -macro_rules! entity_history_handler { - ($history_handler:ident, $edit_row_type:ident, $edit_table:ident) => { - pub fn $history_handler( - &self, - id: &Uuid, - limit: Option, - conn: &DbConn, - ) -> Result> { - let limit = limit.unwrap_or(50); - - let rows: Vec<(EditgroupRow, ChangelogRow, $edit_row_type)> = editgroup::table - .inner_join(changelog::table) - .inner_join($edit_table::table) - .filter($edit_table::ident_id.eq(id)) - .order(changelog::id.desc()) - .limit(limit) - .get_results(conn)?; - - let history: Vec = rows.into_iter() - .map(|(eg_row, cl_row, e_row)| EntityHistoryEntry { - edit: e_row.into_model().expect("edit row to model"), - editgroup: eg_row.into_model_partial(), - changelog_entry: cl_row.into_model(), - }) - .collect(); - Ok(history) + let edit_context = make_edit_context(conn, None)?; + let model_list: Vec<&models::$model> = entity_list.iter().map(|e| e).collect(); + let edits = $model::db_create_batch(conn, &edit_context, model_list.as_slice())?; + edits.into_iter().map(|e| e.into_model()).collect() } } } @@ -96,226 +65,6 @@ pub struct Server { pub db_pool: ConnectionPool, } -fn container_row2entity( - ident: Option, - rev: ContainerRevRow, -) -> Result { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - Ok(ContainerEntity { - issnl: rev.issnl, - wikidata_qid: rev.wikidata_qid, - publisher: rev.publisher, - name: rev.name, - abbrev: rev.abbrev, - coden: rev.coden, - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - extra: rev.extra_json, - editgroup_id: None, - }) -} - -fn creator_row2entity(ident: Option, rev: CreatorRevRow) -> Result { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - Ok(CreatorEntity { - display_name: rev.display_name, - given_name: rev.given_name, - surname: rev.surname, - orcid: rev.orcid, - wikidata_qid: rev.wikidata_qid, - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) -} - -fn file_row2entity( - ident: Option, - rev: FileRevRow, - conn: &DbConn, -) -> Result { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - - let releases: Vec = file_release::table - .filter(file_release::file_rev.eq(rev.id)) - .get_results(conn)? - .into_iter() - .map(|r: FileReleaseRow| uuid2fcid(&r.target_release_ident_id)) - .collect(); - - let urls: Vec = file_rev_url::table - .filter(file_rev_url::file_rev.eq(rev.id)) - .get_results(conn)? - .into_iter() - .map(|r: FileRevUrlRow| FileEntityUrls { - rel: r.rel, - url: r.url, - }) - .collect(); - - Ok(FileEntity { - sha1: rev.sha1, - sha256: rev.sha256, - md5: rev.md5, - size: rev.size.map(|v| v as i64), - urls: Some(urls), - mimetype: rev.mimetype, - releases: Some(releases), - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) -} - -fn release_row2entity( - ident: Option, - rev: ReleaseRevRow, - conn: &DbConn, -) -> Result { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - - let refs: Vec = release_ref::table - .filter(release_ref::release_rev.eq(rev.id)) - .order(release_ref::index_val.asc()) - .get_results(conn) - .expect("fetch release refs") - .into_iter() - .map(|r: ReleaseRefRow| ReleaseRef { - index: r.index_val, - key: r.key, - extra: r.extra_json, - container_title: r.container_title, - year: r.year, - title: r.title, - locator: r.locator, - target_release_id: r.target_release_ident_id.map(|v| uuid2fcid(&v)), - }) - .collect(); - - let contribs: Vec = release_contrib::table - .filter(release_contrib::release_rev.eq(rev.id)) - .order(( - release_contrib::role.asc(), - release_contrib::index_val.asc(), - )) - .get_results(conn) - .expect("fetch release refs") - .into_iter() - .map(|c: ReleaseContribRow| ReleaseContrib { - index: c.index_val, - raw_name: c.raw_name, - role: c.role, - extra: c.extra_json, - creator_id: c.creator_ident_id.map(|v| uuid2fcid(&v)), - creator: None, - }) - .collect(); - - let abstracts: Vec = release_rev_abstract::table - .inner_join(abstracts::table) - .filter(release_rev_abstract::release_rev.eq(rev.id)) - .get_results(conn)? - .into_iter() - .map( - |r: (ReleaseRevAbstractRow, AbstractsRow)| ReleaseEntityAbstracts { - sha1: Some(r.0.abstract_sha1), - mimetype: r.0.mimetype, - lang: r.0.lang, - content: Some(r.1.content), - }, - ) - .collect(); - - Ok(ReleaseEntity { - title: rev.title, - release_type: rev.release_type, - release_status: rev.release_status, - release_date: rev.release_date - .map(|v| chrono::DateTime::from_utc(v.and_hms(0, 0, 0), chrono::Utc)), - doi: rev.doi, - pmid: rev.pmid, - pmcid: rev.pmcid, - isbn13: rev.isbn13, - core_id: rev.core_id, - wikidata_qid: rev.wikidata_qid, - volume: rev.volume, - issue: rev.issue, - pages: rev.pages, - files: None, - container: None, - container_id: rev.container_ident_id.map(|u| uuid2fcid(&u)), - publisher: rev.publisher, - language: rev.language, - work_id: Some(uuid2fcid(&rev.work_ident_id)), - refs: Some(refs), - contribs: Some(contribs), - abstracts: Some(abstracts), - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) -} - -/* XXX: -fn work_row2entity(ident: Option, rev: WorkRevRow) -> Result { - let (state, ident_id, redirect_id) = match ident { - Some(i) => ( - Some(i.state().unwrap().shortname()), - Some(uuid2fcid(&i.id)), - i.redirect_id.map(|u| uuid2fcid(&u)), - ), - None => (None, None, None), - }; - Ok(WorkEntity { - state: state, - ident: ident_id, - revision: Some(rev.id.to_string()), - redirect: redirect_id, - editgroup_id: None, - extra: rev.extra_json, - }) -} -*/ - impl Server { pub fn get_container_handler( &self, @@ -323,13 +72,7 @@ impl Server { _expand: Option, conn: &DbConn, ) -> Result { - // TODO: handle Deletions - let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table - .find(id) - .inner_join(container_rev::table) - .first(conn)?; - - container_row2entity(Some(ident), rev) + ContainerEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn lookup_container_handler(&self, issnl: &str, conn: &DbConn) -> Result { @@ -344,7 +87,7 @@ impl Server { .filter(container_ident::redirect_id.is_null()) .first(conn)?; - container_row2entity(Some(ident), rev) + ContainerEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_creator_handler( @@ -353,12 +96,8 @@ impl Server { _expand: Option, conn: &DbConn, ) -> Result { - let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table - .find(id) - .inner_join(creator_rev::table) - .first(conn)?; - creator_row2entity(Some(ident), rev) + CreatorEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn lookup_creator_handler(&self, orcid: &str, conn: &DbConn) -> Result { @@ -373,7 +112,7 @@ impl Server { .filter(creator_ident::redirect_id.is_null()) .first(conn)?; - creator_row2entity(Some(ident), rev) + CreatorEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_creator_releases_handler( @@ -392,8 +131,9 @@ impl Server { .filter(release_ident::redirect_id.is_null()) .load(conn)?; + // TODO: from_rows, not from_row? rows.into_iter() - .map(|(rev, ident, _)| release_row2entity(Some(ident), rev, conn)) + .map(|(rev, ident, _)| ReleaseEntity::db_from_row(conn, rev, Some(ident))) .collect() } @@ -403,12 +143,7 @@ impl Server { _expand: Option, conn: &DbConn, ) -> Result { - let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table - .find(id) - .inner_join(file_rev::table) - .first(conn)?; - - file_row2entity(Some(ident), rev, conn) + FileEntity::db_get(conn, FatCatId::from_uuid(id)) } pub fn lookup_file_handler(&self, sha1: &str, conn: &DbConn) -> Result { @@ -422,7 +157,7 @@ impl Server { .filter(file_ident::redirect_id.is_null()) .first(conn)?; - file_row2entity(Some(ident), rev, conn) + FileEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_release_handler( @@ -431,12 +166,8 @@ impl Server { expand: Option, conn: &DbConn, ) -> Result { - let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table - .find(id) - .inner_join(release_rev::table) - .first(conn)?; - let mut release = release_row2entity(Some(ident), rev, conn)?; + let mut release = ReleaseEntity::db_get(conn, FatCatId::from_uuid(id))?; // For now, if there is any expand param we do them all if expand.is_some() { @@ -447,7 +178,6 @@ impl Server { Some(self.get_container_handler(&fcid2uuid(&cid)?, None, conn)?); } } - Ok(release) } @@ -463,22 +193,23 @@ impl Server { .filter(release_ident::redirect_id.is_null()) .first(conn)?; - release_row2entity(Some(ident), rev, conn) + ReleaseEntity::db_from_row(conn, rev, Some(ident)) } pub fn get_release_files_handler(&self, id: &str, conn: &DbConn) -> Result> { - let id = fcid2uuid(&id)?; + + let ident = FatCatId::from_str(id)?; let rows: Vec<(FileRevRow, FileIdentRow, FileReleaseRow)> = file_rev::table .inner_join(file_ident::table) .inner_join(file_release::table) - .filter(file_release::target_release_ident_id.eq(&id)) + .filter(file_release::target_release_ident_id.eq(&ident.to_uuid())) .filter(file_ident::is_live.eq(true)) .filter(file_ident::redirect_id.is_null()) .load(conn)?; rows.into_iter() - .map(|(rev, ident, _)| file_row2entity(Some(ident), rev, conn)) + .map(|(rev, ident, _)| FileEntity::db_from_row(conn, rev, Some(ident))) .collect() } @@ -502,7 +233,7 @@ impl Server { .load(conn)?; rows.into_iter() - .map(|(rev, ident)| release_row2entity(Some(ident), rev, conn)) + .map(|(rev, ident)| ReleaseEntity::db_from_row(conn, rev, Some(ident))) .collect() } @@ -511,52 +242,25 @@ impl Server { entity: models::ContainerEntity, conn: &DbConn, ) -> Result { - 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)?, - Some(param) => fcid2uuid(¶m)?, - }; - if let Some(ref extid) = entity.wikidata_qid { - check_wikidata_qid(extid)?; - } - if let Some(ref extid) = entity.issnl { - check_issn(extid)?; - } - - let edit: ContainerEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO container_rev (name, publisher, issnl, wikidata_qid, abbrev, coden, extra_json) - VALUES ($1, $2, $3, $4, $5, $6, $7) - RETURNING id ), - ident AS ( INSERT INTO container_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO container_edit (editgroup_id, ident_id, rev_id) VALUES - ($8, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::(entity.name) - .bind::, _>(entity.publisher) - .bind::, _>(entity.issnl) - .bind::, _>(entity.wikidata_qid) - .bind::, _>(entity.abbrev) - .bind::, _>(entity.coden) - .bind::, _>(entity.extra) - .bind::(editgroup_id) - .get_result(conn)?; - + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } - // XXX: pub fn update_container_handler( &self, id: &Uuid, entity: models::ContainerEntity, conn: &DbConn, ) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn delete_container_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = ContainerEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn create_creator_handler( @@ -564,51 +268,26 @@ impl Server { entity: models::CreatorEntity, conn: &DbConn, ) -> Result { - 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"), - Some(param) => fcid2uuid(¶m)?, - }; - if let Some(ref extid) = entity.orcid { - check_orcid(extid)?; - } - if let Some(ref extid) = entity.wikidata_qid { - check_wikidata_qid(extid)?; - } - - let edit: CreatorEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO creator_rev (display_name, given_name, surname, orcid, wikidata_qid, extra_json) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id ), - ident AS ( INSERT INTO creator_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO creator_edit (editgroup_id, ident_id, rev_id) VALUES - ($7, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::(entity.display_name) - .bind::, _>(entity.given_name) - .bind::, _>(entity.surname) - .bind::, _>(entity.orcid) - .bind::, _>(entity.wikidata_qid) - .bind::, _>(entity.extra) - .bind::(editgroup_id) - .get_result(conn)?; - + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; edit.into_model() + } - // XXX: pub fn update_creator_handler( &self, id: &Uuid, entity: models::CreatorEntity, conn: &DbConn, ) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn delete_creator_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = CreatorEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn create_file_handler( @@ -616,92 +295,25 @@ impl Server { entity: models::FileEntity, conn: &DbConn, ) -> Result { - 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"), - Some(param) => fcid2uuid(¶m)?, - }; - - let edit: FileEditRow = - diesel::sql_query( - "WITH rev AS ( INSERT INTO file_rev (size, sha1, sha256, md5, mimetype, extra_json) - VALUES ($1, $2, $3, $4, $5, $6) - RETURNING id ), - ident AS ( INSERT INTO file_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO file_edit (editgroup_id, ident_id, rev_id) VALUES - ($7, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::, _>(entity.size) - .bind::, _>(entity.sha1) - .bind::, _>(entity.sha256) - .bind::, _>(entity.md5) - .bind::, _>(entity.mimetype) - .bind::, _>(entity.extra) - .bind::(editgroup_id) - .get_result(conn)?; - - let _releases: Option> = match entity.releases { - None => None, - Some(release_list) => { - if release_list.is_empty() { - Some(vec![]) - } else { - let release_rows: Vec = release_list - .iter() - .map(|r| FileReleaseRow { - file_rev: edit.rev_id.unwrap(), - target_release_ident_id: fcid2uuid(r) - .expect("invalid fatcat identifier"), - }) - .collect(); - let release_rows: Vec = insert_into(file_release::table) - .values(release_rows) - .get_results(conn) - .expect("error inserting file_releases"); - Some(release_rows) - } - } - }; - - let _urls: Option> = match entity.urls { - None => None, - Some(url_list) => { - if url_list.is_empty() { - Some(vec![]) - } else { - let url_rows: Vec = url_list - .into_iter() - .map(|u| FileRevUrlNewRow { - file_rev: edit.rev_id.unwrap(), - rel: u.rel, - url: u.url, - }) - .collect(); - let url_rows: Vec = insert_into(file_rev_url::table) - .values(url_rows) - .get_results(conn) - .expect("error inserting file_rev_url"); - Some(url_rows) - } - } - }; - + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } - // XXX: pub fn update_file_handler( &self, id: &Uuid, entity: models::FileEntity, conn: &DbConn, ) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn delete_file_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = FileEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn create_release_handler( @@ -709,187 +321,25 @@ impl Server { entity: models::ReleaseEntity, conn: &DbConn, ) -> Result { - 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"), - Some(param) => fcid2uuid(¶m)?, - }; - if let Some(ref extid) = entity.doi { - check_doi(extid)?; - } - if let Some(ref extid) = entity.pmid { - check_pmid(extid)?; - } - if let Some(ref extid) = entity.pmcid { - check_pmcid(extid)?; - } - if let Some(ref extid) = entity.wikidata_qid { - check_wikidata_qid(extid)?; - } - - let work_id = match entity.work_id { - Some(work_id) => fcid2uuid(&work_id)?, - None => { - // If a work_id wasn't passed, create a new work under the current editgroup - let work_model = models::WorkEntity { - ident: None, - revision: None, - redirect: None, - state: None, - editgroup_id: Some(uuid2fcid(&editgroup_id)), - extra: None, - }; - let new_entity = self.create_work_handler(work_model, conn)?; - fcid2uuid(&new_entity.ident)? - } - }; - - let container_id: Option = match entity.container_id { - Some(id) => Some(fcid2uuid(&id)?), - None => None, - }; - - let edit: ReleaseEditRow = diesel::sql_query( - "WITH rev AS ( INSERT INTO release_rev (title, release_type, release_status, release_date, doi, pmid, pmcid, wikidata_qid, isbn13, core_id, volume, issue, pages, work_ident_id, container_ident_id, publisher, language, extra_json) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) - RETURNING id ), - ident AS ( INSERT INTO release_ident (rev_id) - VALUES ((SELECT rev.id FROM rev)) - RETURNING id ) - INSERT INTO release_edit (editgroup_id, ident_id, rev_id) VALUES - ($19, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) - RETURNING *", - ).bind::(entity.title) - .bind::, _>(entity.release_type) - .bind::, _>(entity.release_status) - .bind::, _>( - entity.release_date.map(|v| v.naive_utc().date())) - .bind::, _>(entity.doi) - .bind::, _>(entity.pmid) - .bind::, _>(entity.pmcid) - .bind::, _>(entity.wikidata_qid) - .bind::, _>(entity.isbn13) - .bind::, _>(entity.core_id) - .bind::, _>(entity.volume) - .bind::, _>(entity.issue) - .bind::, _>(entity.pages) - .bind::(work_id) - .bind::, _>(container_id) - .bind::, _>(entity.publisher) - .bind::, _>(entity.language) - .bind::, _>(entity.extra) - .bind::(editgroup_id) - .get_result(conn)?; - - let _refs: Option> = match entity.refs { - None => None, - Some(ref_list) => { - if ref_list.is_empty() { - Some(vec![]) - } else { - let ref_rows: Vec = ref_list - .iter() - .map(|r| ReleaseRefNewRow { - release_rev: edit.rev_id.unwrap(), - target_release_ident_id: r.target_release_id - .clone() - .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), - index_val: r.index, - key: r.key.clone(), - container_title: r.container_title.clone(), - year: r.year, - title: r.title.clone(), - locator: r.locator.clone(), - extra_json: r.extra.clone(), - }) - .collect(); - let ref_rows: Vec = insert_into(release_ref::table) - .values(ref_rows) - .get_results(conn) - .expect("error inserting release_refs"); - Some(ref_rows) - } - } - }; - - let _contribs: Option> = match entity.contribs { - None => None, - Some(contrib_list) => { - if contrib_list.is_empty() { - Some(vec![]) - } else { - let contrib_rows: Vec = contrib_list - .iter() - .map(|c| ReleaseContribNewRow { - release_rev: edit.rev_id.unwrap(), - creator_ident_id: c.creator_id - .clone() - .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), - raw_name: c.raw_name.clone(), - index_val: c.index, - role: c.role.clone(), - extra_json: c.extra.clone(), - }) - .collect(); - let contrib_rows: Vec = insert_into(release_contrib::table) - .values(contrib_rows) - .get_results(conn) - .expect("error inserting release_contribs"); - Some(contrib_rows) - } - } - }; - - if let Some(abstract_list) = entity.abstracts { - // For rows that specify content, we need to insert the abstract if it doesn't exist - // already - let new_abstracts: Vec = abstract_list - .iter() - .filter(|ea| ea.content.is_some()) - .map(|c| AbstractsRow { - sha1: Sha1::from(c.content.clone().unwrap()).hexdigest(), - content: c.content.clone().unwrap(), - }) - .collect(); - if !new_abstracts.is_empty() { - // Sort of an "upsert"; only inserts new abstract rows if they don't already exist - insert_into(abstracts::table) - .values(&new_abstracts) - .on_conflict(abstracts::sha1) - .do_nothing() - .execute(conn)?; - } - let release_abstract_rows: Vec = abstract_list - .into_iter() - .map(|c| ReleaseRevAbstractNewRow { - release_rev: edit.rev_id.unwrap(), - abstract_sha1: match c.content { - Some(ref content) => Sha1::from(content).hexdigest(), - None => c.sha1.expect("either abstract_sha1 or content is required"), - }, - lang: c.lang, - mimetype: c.mimetype, - }) - .collect(); - insert_into(release_rev_abstract::table) - .values(release_abstract_rows) - .execute(conn)?; - } - + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } - // XXX: pub fn update_release_handler( &self, id: &Uuid, entity: models::ReleaseEntity, conn: &DbConn, ) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn delete_release_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { - unimplemented!() + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit = ReleaseEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; + edit.into_model() } pub fn create_work_handler( @@ -1150,30 +600,33 @@ impl Server { } entity_batch_handler!( - create_container_handler, create_container_batch_handler, ContainerEntity ); entity_batch_handler!( - create_creator_handler, create_creator_batch_handler, CreatorEntity ); - entity_batch_handler!(create_file_handler, create_file_batch_handler, FileEntity); + entity_batch_handler!(create_file_batch_handler, FileEntity); entity_batch_handler!( - create_release_handler, create_release_batch_handler, ReleaseEntity ); - entity_batch_handler!(create_work_handler, create_work_batch_handler, WorkEntity); + entity_batch_handler!(create_work_batch_handler, WorkEntity); - entity_history_handler!( - get_container_history_handler, - ContainerEditRow, - container_edit - ); - entity_history_handler!(get_creator_history_handler, CreatorEditRow, creator_edit); - entity_history_handler!(get_file_history_handler, FileEditRow, file_edit); - entity_history_handler!(get_release_history_handler, ReleaseEditRow, release_edit); - entity_history_handler!(get_work_history_handler, WorkEditRow, work_edit); + pub fn get_container_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + ContainerEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_creator_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + CreatorEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_file_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + FileEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_release_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + ReleaseEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } + pub fn get_work_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + WorkEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) + } } diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index e97e134b..b8ff195a 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -1,11 +1,13 @@ +use sha1::Sha1; +use chrono; use diesel::prelude::*; use diesel::{self, insert_into}; use database_schema::*; use database_models::*; use errors::*; use fatcat_api::models::*; -use api_helpers::{FatCatId, DbConn}; +use api_helpers::*; use uuid::Uuid; use std::marker::Sized; use std::str::FromStr; @@ -55,7 +57,7 @@ pub trait EntityCrud where Self: Sized { fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result>; // Entity-specific Methods - fn from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; + fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; fn db_insert_rev(&self, conn: &DbConn) -> Result; fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result>; } @@ -80,7 +82,7 @@ macro_rules! generic_db_get { .inner_join($rev_table::table) .first(conn)?; - Self::from_row(conn, rev, Some(ident)) + Self::db_from_row(conn, rev, Some(ident)) } } } @@ -92,12 +94,13 @@ macro_rules! generic_db_get_rev { .find(rev_id) .first(conn)?; - Self::from_row(conn, rev, None) + Self::db_from_row(conn, rev, None) } } } macro_rules! generic_db_create { + // TODO: this path should call generic_db_create_batch ($ident_table: ident, $edit_table: ident) => { fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result { let rev_id = self.db_insert_rev(conn)?; @@ -128,7 +131,7 @@ macro_rules! generic_db_create_batch { is_live: edit_context.autoapprove, redirect_id: None, }) - .collect::>()) + .collect::>()) .returning($ident_table::id) .get_results(conn)?; let edits: Vec = insert_into($edit_table::table) @@ -141,7 +144,7 @@ macro_rules! generic_db_create_batch { prev_rev: None, extra_json: edit_context.extra_json.clone(), }) - .collect::>()) + .collect::>()) .get_results(conn)?; Ok(edits) } @@ -209,7 +212,7 @@ macro_rules! generic_db_delete { macro_rules! generic_db_get_history { ($edit_table:ident) => { fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result> { - let limit = limit.unwrap_or(50); // XXX: make a static + let limit = limit.unwrap_or(50); // TODO: make a static let rows: Vec<(EditgroupRow, ChangelogRow, Self::EditRow)> = editgroup::table .inner_join(changelog::table) @@ -239,6 +242,613 @@ macro_rules! generic_db_insert_rev { } } +impl EntityCrud for ContainerEntity { + type EditRow = ContainerEditRow; + type EditNewRow = ContainerEditNewRow; + type IdentRow = ContainerIdentRow; + type IdentNewRow = ContainerIdentNewRow; + type RevRow = ContainerRevRow; + + generic_parse_editgroup_id!(); + generic_db_get!(container_ident, container_rev); + generic_db_get_rev!(container_rev); + generic_db_create!(container_ident, container_edit); + generic_db_create_batch!(container_ident, container_edit); + generic_db_update!(container_ident, container_edit); + generic_db_delete!(container_ident, container_edit); + generic_db_get_history!(container_edit); + generic_db_insert_rev!(); + + fn db_from_row(_conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + + let (state, ident_id, redirect_id) = match ident_row { + Some(i) => ( + Some(i.state().unwrap().shortname()), + Some(FatCatId::from_uuid(&i.id).to_string()), + i.redirect_id.map(|u| FatCatId::from_uuid(&u).to_string()), + ), + None => (None, None, None), + }; + + Ok(ContainerEntity { + issnl: rev_row.issnl, + wikidata_qid: rev_row.wikidata_qid, + publisher: rev_row.publisher, + name: rev_row.name, + abbrev: rev_row.abbrev, + coden: rev_row.coden, + state: state, + ident: ident_id, + revision: Some(rev_row.id.to_string()), + redirect: redirect_id, + extra: rev_row.extra_json, + editgroup_id: None, + }) + } + + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { + + // first verify external identifier syntax + for entity in models { + if let Some(ref extid) = entity.wikidata_qid { + check_wikidata_qid(extid)?; + } + if let Some(ref extid) = entity.issnl { + check_issn(extid)?; + } + } + + let rev_ids: Vec = insert_into(container_rev::table) + .values(models.iter() + .map(|model| ContainerRevNewRow { + name: model.name.clone(), + publisher: model.publisher.clone(), + issnl: model.issnl.clone(), + wikidata_qid: model.wikidata_qid.clone(), + abbrev: model.abbrev.clone(), + coden: model.coden.clone(), + extra_json: model.extra.clone() + }) + .collect::>()) + .returning(container_rev::id) + .get_results(conn)?; + Ok(rev_ids) + } +} + +impl EntityCrud for CreatorEntity { + type EditRow = CreatorEditRow; + type EditNewRow = CreatorEditNewRow; + type IdentRow = CreatorIdentRow; + type IdentNewRow = CreatorIdentNewRow; + type RevRow = CreatorRevRow; + + generic_parse_editgroup_id!(); + generic_db_get!(creator_ident, creator_rev); + generic_db_get_rev!(creator_rev); + generic_db_create!(creator_ident, creator_edit); + generic_db_create_batch!(creator_ident, creator_edit); + generic_db_update!(creator_ident, creator_edit); + generic_db_delete!(creator_ident, creator_edit); + generic_db_get_history!(creator_edit); + generic_db_insert_rev!(); + + fn db_from_row(_conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + let (state, ident_id, redirect_id) = match ident_row { + Some(i) => ( + Some(i.state().unwrap().shortname()), + Some(FatCatId::from_uuid(&i.id).to_string()), + i.redirect_id.map(|u| FatCatId::from_uuid(&u).to_string()), + ), + None => (None, None, None), + }; + Ok(CreatorEntity { + display_name: rev_row.display_name, + given_name: rev_row.given_name, + surname: rev_row.surname, + orcid: rev_row.orcid, + wikidata_qid: rev_row.wikidata_qid, + state: state, + ident: ident_id, + revision: Some(rev_row.id.to_string()), + redirect: redirect_id, + editgroup_id: None, + extra: rev_row.extra_json, + }) + } + + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { + + // first verify external identifier syntax + for entity in models { + if let Some(ref extid) = entity.orcid { + check_orcid(extid)?; + } + if let Some(ref extid) = entity.wikidata_qid { + check_wikidata_qid(extid)?; + } + } + + let rev_ids: Vec = insert_into(creator_rev::table) + .values(models.iter() + .map(|model| CreatorRevNewRow { + display_name: model.display_name.clone(), + given_name: model.given_name.clone(), + surname: model.surname.clone(), + orcid: model.orcid.clone(), + wikidata_qid: model.wikidata_qid.clone(), + extra_json: model.extra.clone() + }) + .collect::>()) + .returning(creator_rev::id) + .get_results(conn)?; + Ok(rev_ids) + } +} + +impl EntityCrud for FileEntity { + type EditRow = FileEditRow; + type EditNewRow = FileEditNewRow; + type IdentRow = FileIdentRow; + type IdentNewRow = FileIdentNewRow; + type RevRow = FileRevRow; + + generic_parse_editgroup_id!(); + generic_db_get!(file_ident, file_rev); + generic_db_get_rev!(file_rev); + generic_db_create!(file_ident, file_edit); + generic_db_create_batch!(file_ident, file_edit); + generic_db_update!(file_ident, file_edit); + generic_db_delete!(file_ident, file_edit); + generic_db_get_history!(file_edit); + generic_db_insert_rev!(); + + fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + let (state, ident_id, redirect_id) = match ident_row { + Some(i) => ( + Some(i.state().unwrap().shortname()), + Some(FatCatId::from_uuid(&i.id).to_string()), + i.redirect_id.map(|u| FatCatId::from_uuid(&u).to_string()), + ), + None => (None, None, None), + }; + + let releases: Vec = file_release::table + .filter(file_release::file_rev.eq(rev_row.id)) + .get_results(conn)? + .into_iter() + .map(|r: FileReleaseRow| FatCatId::from_uuid(&r.target_release_ident_id)) + .collect(); + + let urls: Vec = file_rev_url::table + .filter(file_rev_url::file_rev.eq(rev_row.id)) + .get_results(conn)? + .into_iter() + .map(|r: FileRevUrlRow| FileEntityUrls { + rel: r.rel, + url: r.url, + }) + .collect(); + + Ok(FileEntity { + sha1: rev_row.sha1, + sha256: rev_row.sha256, + md5: rev_row.md5, + size: rev_row.size.map(|v| v as i64), + urls: Some(urls), + mimetype: rev_row.mimetype, + releases: Some(releases.iter().map(|fcid| fcid.to_string()).collect()), + state: state, + ident: ident_id, + revision: Some(rev_row.id.to_string()), + redirect: redirect_id, + editgroup_id: None, + extra: rev_row.extra_json, + }) + } + + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { + + let rev_ids: Vec = insert_into(file_rev::table) + .values(models.iter() + .map(|model| FileRevNewRow { + size: model.size, + sha1: model.sha1.clone(), + sha256: model.sha256.clone(), + md5: model.md5.clone(), + mimetype: model.mimetype.clone(), + extra_json: model.extra.clone() + }) + .collect::>()) + .returning(file_rev::id) + .get_results(conn)?; + + let mut file_release_rows: Vec = vec![]; + let mut file_url_rows: Vec = vec![]; + + for (model, rev_id) in models.iter().zip(rev_ids.iter()) { + match &model.releases { + None => (), + Some(release_list) => { + let these_release_rows: Vec = release_list + .iter() + .map(|r| FileReleaseRow { + file_rev: rev_id.clone(), + target_release_ident_id: fcid2uuid(r) + // XXX: shouldn't expect + .expect("invalid fatcat identifier"), + }) + .collect(); + file_release_rows.extend(these_release_rows); + } + }; + + match &model.urls { + None => (), + Some(url_list) => { + let these_url_rows: Vec = url_list + .into_iter() + .map(|u| FileRevUrlNewRow { + file_rev: rev_id.clone(), + rel: u.rel.clone(), + url: u.url.clone(), + }) + .collect(); + file_url_rows.extend(these_url_rows); + } + }; + } + + if !file_release_rows.is_empty() { + // TODO: shouldn't it be "file_rev_release"? + insert_into(file_release::table) + .values(file_release_rows) + .execute(conn)?; + } + + if !file_url_rows.is_empty() { + insert_into(file_rev_url::table) + .values(file_url_rows) + .execute(conn)?; + } + + Ok(rev_ids) + } +} + +impl EntityCrud for ReleaseEntity { + type EditRow = ReleaseEditRow; + type EditNewRow = ReleaseEditNewRow; + type IdentRow = ReleaseIdentRow; + type IdentNewRow = ReleaseIdentNewRow; + type RevRow = ReleaseRevRow; + + generic_parse_editgroup_id!(); + generic_db_get!(release_ident, release_rev); + generic_db_get_rev!(release_rev); + //generic_db_create!(release_ident, release_edit); + //generic_db_create_batch!(release_ident, release_edit); + generic_db_update!(release_ident, release_edit); + generic_db_delete!(release_ident, release_edit); + generic_db_get_history!(release_edit); + generic_db_insert_rev!(); + + fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result { + let mut edits = Self::db_create_batch(conn, edit_context, &vec![self])?; + // probably a more elegant way to destroy the vec and take first element + Ok(edits.pop().unwrap()) + } + + fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result> { + // This isn't the generic implementation because we need to create Work entities for each + // of the release entities passed (at least in the common case) + + // Generate the set of new work entities to insert (usually one for each release, but some + // releases might be pointed to a work already) + let mut new_work_models: Vec<&WorkEntity> = vec![]; + for entity in models { + if entity.work_id.is_none() { + new_work_models.push(&WorkEntity { + ident: None, + revision: None, + redirect: None, + state: None, + editgroup_id: None, + extra: None, + }); + }; + } + + // create the works, then pluck the list of idents from the result + let new_work_edits = WorkEntity::db_create_batch(conn, edit_context, new_work_models.as_slice())?; + let mut new_work_ids: Vec = new_work_edits.iter().map(|edit| edit.ident_id).collect(); + + // Copy all the release models, and ensure that each has work_id set, using the new work + // idents. There should be one new work ident for each release missing one. + let models_with_work_ids: Vec = models.iter().map(|model| { + let mut model = (*model).clone(); + if model.work_id.is_none() { + model.work_id = Some(FatCatId::from_uuid(&new_work_ids.pop().unwrap()).to_string()) + } + model + }).collect(); + let model_refs: Vec<&Self> = models_with_work_ids.iter().map(|s| s).collect(); + let models = model_refs.as_slice(); + + // The rest here is copy/pasta from the generic (how to avoid copypasta?) + let rev_ids: Vec = Self::db_insert_revs(conn, models)?; + let ident_ids: Vec = insert_into(release_ident::table) + .values(rev_ids.iter() + .map(|rev_id| Self::IdentNewRow { + rev_id: Some(rev_id.clone()), + is_live: edit_context.autoapprove, + redirect_id: None, + }) + .collect::>()) + .returning(release_ident::id) + .get_results(conn)?; + let edits: Vec = insert_into(release_edit::table) + .values(rev_ids.into_iter().zip(ident_ids.into_iter()) + .map(|(rev_id, ident_id)| Self::EditNewRow { + editgroup_id: edit_context.editgroup_id.to_uuid(), + rev_id: Some(rev_id), + ident_id: ident_id, + redirect_id: None, + prev_rev: None, + extra_json: edit_context.extra_json.clone(), + }) + .collect::>()) + .get_results(conn)?; + Ok(edits) + } + + fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + let (state, ident_id, redirect_id) = match ident_row { + Some(i) => ( + Some(i.state().unwrap().shortname()), + Some(FatCatId::from_uuid(&i.id).to_string()), + i.redirect_id.map(|u| FatCatId::from_uuid(&u).to_string()), + ), + None => (None, None, None), + }; + + let refs: Vec = release_ref::table + .filter(release_ref::release_rev.eq(rev_row.id)) + .order(release_ref::index_val.asc()) + .get_results(conn) + .expect("fetch release refs") + .into_iter() + .map(|r: ReleaseRefRow| ReleaseRef { + index: r.index_val, + key: r.key, + extra: r.extra_json, + container_title: r.container_title, + year: r.year, + title: r.title, + locator: r.locator, + target_release_id: r.target_release_ident_id.map(|v| FatCatId::from_uuid(&v).to_string()), + }) + .collect(); + + let contribs: Vec = release_contrib::table + .filter(release_contrib::release_rev.eq(rev_row.id)) + .order(( + release_contrib::role.asc(), + release_contrib::index_val.asc(), + )) + .get_results(conn) + .expect("fetch release refs") + .into_iter() + .map(|c: ReleaseContribRow| ReleaseContrib { + index: c.index_val, + raw_name: c.raw_name, + role: c.role, + extra: c.extra_json, + creator_id: c.creator_ident_id.map(|v| FatCatId::from_uuid(&v).to_string()), + creator: None, + }) + .collect(); + + let abstracts: Vec = release_rev_abstract::table + .inner_join(abstracts::table) + .filter(release_rev_abstract::release_rev.eq(rev_row.id)) + .get_results(conn)? + .into_iter() + .map( + |r: (ReleaseRevAbstractRow, AbstractsRow)| ReleaseEntityAbstracts { + sha1: Some(r.0.abstract_sha1), + mimetype: r.0.mimetype, + lang: r.0.lang, + content: Some(r.1.content), + }, + ) + .collect(); + + Ok(ReleaseEntity { + title: rev_row.title, + release_type: rev_row.release_type, + release_status: rev_row.release_status, + release_date: rev_row.release_date + .map(|v| chrono::DateTime::from_utc(v.and_hms(0, 0, 0), chrono::Utc)), + doi: rev_row.doi, + pmid: rev_row.pmid, + pmcid: rev_row.pmcid, + isbn13: rev_row.isbn13, + core_id: rev_row.core_id, + wikidata_qid: rev_row.wikidata_qid, + volume: rev_row.volume, + issue: rev_row.issue, + pages: rev_row.pages, + files: None, + container: None, + container_id: rev_row.container_ident_id.map(|u| FatCatId::from_uuid(&u).to_string()), + publisher: rev_row.publisher, + language: rev_row.language, + work_id: Some(FatCatId::from_uuid(&rev_row.work_ident_id).to_string()), + refs: Some(refs), + contribs: Some(contribs), + abstracts: Some(abstracts), + state: state, + ident: ident_id, + revision: Some(rev_row.id.to_string()), + redirect: redirect_id, + editgroup_id: None, + extra: rev_row.extra_json, + }) + } + + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { + + // first verify external identifier syntax + for entity in models { + if let Some(ref extid) = entity.doi { + check_doi(extid)?; + } + if let Some(ref extid) = entity.pmid { + check_pmid(extid)?; + } + if let Some(ref extid) = entity.pmcid { + check_pmcid(extid)?; + } + if let Some(ref extid) = entity.wikidata_qid { + check_wikidata_qid(extid)?; + } + } + + let rev_ids: Vec = insert_into(release_rev::table) + .values(models.iter() + .map(|model| ReleaseRevNewRow { + title: model.title.clone(), + release_type: model.release_type.clone(), + release_status: model.release_status.clone(), + release_date: model.release_date.map(|v| v.naive_utc().date()), + doi: model.doi.clone(), + pmid: model.pmid.clone(), + pmcid: model.pmcid.clone(), + wikidata_qid: model.wikidata_qid.clone(), + isbn13: model.isbn13.clone(), + core_id: model.core_id.clone(), + volume: model.volume.clone(), + issue: model.issue.clone(), + pages: model.pages.clone(), + work_ident_id: model.work_id + .clone() + .map(|s| FatCatId::from_str(&s).expect("invalid fatcat identifier").to_uuid()) + .expect("release_revs must have a work_id by the time they are inserted; this is an internal soundness error"), + container_ident_id: model.container_id + .clone() + .map(|s| FatCatId::from_str(&s).expect("invalid fatcat identifier").to_uuid()), + publisher: model.publisher.clone(), + language: model.language.clone(), + extra_json: model.extra.clone() + }) + .collect::>()) + .returning(release_rev::id) + .get_results(conn)?; + + let mut release_ref_rows: Vec = vec![]; + let mut release_contrib_rows: Vec = vec![]; + + for (model, rev_id) in models.iter().zip(rev_ids.iter()) { + match &model.refs { + None => (), + Some(ref_list) => { + let these_ref_rows: Vec = ref_list + .iter() + .map(|r| ReleaseRefNewRow { + release_rev: rev_id.clone(), + target_release_ident_id: r.target_release_id + .clone() + .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), + index_val: r.index, + key: r.key.clone(), + container_title: r.container_title.clone(), + year: r.year, + title: r.title.clone(), + locator: r.locator.clone(), + extra_json: r.extra.clone(), + }) + .collect(); + release_ref_rows.extend(these_ref_rows); + } + }; + + match &model.contribs { + None => (), + Some(contrib_list) => { + let these_contrib_rows: Vec = contrib_list + .iter() + .map(|c| ReleaseContribNewRow { + release_rev: rev_id.clone(), + creator_ident_id: c.creator_id + .clone() + // XXX: shouldn't have these expects + .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), + raw_name: c.raw_name.clone(), + index_val: c.index, + role: c.role.clone(), + extra_json: c.extra.clone(), + }) + .collect(); + release_contrib_rows.extend(these_contrib_rows); + } + }; + + // TODO: this part still isn't parallelized + if let Some(abstract_list) = &model.abstracts { + // For rows that specify content, we need to insert the abstract if it doesn't exist + // already + let new_abstracts: Vec = abstract_list + .iter() + .filter(|ea| ea.content.is_some()) + .map(|c| AbstractsRow { + sha1: Sha1::from(c.content.clone().unwrap()).hexdigest(), + content: c.content.clone().unwrap(), + }) + .collect(); + if !new_abstracts.is_empty() { + // Sort of an "upsert"; only inserts new abstract rows if they don't already exist + insert_into(abstracts::table) + .values(&new_abstracts) + .on_conflict(abstracts::sha1) + .do_nothing() + .execute(conn)?; + } + let release_abstract_rows: Vec = abstract_list + .into_iter() + .map(|c| ReleaseRevAbstractNewRow { + release_rev: rev_id.clone(), + abstract_sha1: match c.content { + Some(ref content) => Sha1::from(content).hexdigest(), + // XXX: shouldn't have these expects + None => c.sha1.clone().expect("either abstract_sha1 or content is required"), + }, + lang: c.lang.clone(), + mimetype: c.mimetype.clone(), + }) + .collect(); + insert_into(release_rev_abstract::table) + .values(release_abstract_rows) + .execute(conn)?; + } + } + + if !release_ref_rows.is_empty() { + insert_into(release_ref::table) + .values(release_ref_rows) + .execute(conn)?; + } + + if !release_contrib_rows.is_empty() { + insert_into(release_contrib::table) + .values(release_contrib_rows) + .execute(conn)?; + } + + Ok(rev_ids) + } +} + impl EntityCrud for WorkEntity { type EditRow = WorkEditRow; type EditNewRow = WorkEditNewRow; @@ -256,7 +866,7 @@ impl EntityCrud for WorkEntity { generic_db_get_history!(work_edit); generic_db_insert_rev!(); - fn from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + fn db_from_row(_conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 14215a3c..2d6788eb 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -125,6 +125,18 @@ pub struct ContainerRevRow { pub coden: Option, } +#[derive(Debug, Associations, AsChangeset, Insertable)] +#[table_name = "container_rev"] +pub struct ContainerRevNewRow { + pub extra_json: Option, + pub name: String, + pub publisher: Option, + pub issnl: Option, + pub wikidata_qid: Option, + pub abbrev: Option, + pub coden: Option, +} + entity_structs!( "container_edit", ContainerEditRow, @@ -146,6 +158,17 @@ pub struct CreatorRevRow { pub wikidata_qid: Option, } +#[derive(Debug, Associations, AsChangeset, Insertable)] +#[table_name = "creator_rev"] +pub struct CreatorRevNewRow { + pub extra_json: Option, + pub display_name: String, + pub given_name: Option, + pub surname: Option, + pub orcid: Option, + pub wikidata_qid: Option, +} + entity_structs!( "creator_edit", CreatorEditRow, @@ -184,6 +207,17 @@ pub struct FileRevRow { pub mimetype: Option, } +#[derive(Debug, Associations, AsChangeset, Insertable)] +#[table_name = "file_rev"] +pub struct FileRevNewRow { + pub extra_json: Option, + pub size: Option, + pub sha1: Option, + pub sha256: Option, + pub md5: Option, + pub mimetype: Option, +} + entity_structs!("file_edit", FileEditRow, FileEditNewRow, "file_ident", FileIdentRow, FileIdentNewRow); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] @@ -210,6 +244,29 @@ pub struct ReleaseRevRow { pub language: Option, } +#[derive(Debug, Associations, AsChangeset, Insertable)] +#[table_name = "release_rev"] +pub struct ReleaseRevNewRow { + pub extra_json: Option, + pub work_ident_id: Uuid, + pub container_ident_id: Option, + pub title: String, + pub release_type: Option, + pub release_status: Option, + pub release_date: Option, + pub doi: Option, + pub pmid: Option, + pub pmcid: Option, + pub wikidata_qid: Option, + pub isbn13: Option, + pub core_id: Option, + pub volume: Option, + pub issue: Option, + pub pages: Option, + pub publisher: Option, + pub language: Option, +} + entity_structs!( "release_edit", ReleaseEditRow, -- cgit v1.2.3 From 20c5cf5a8b9acf98db7487ab49de8dcbc1ddb2f9 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 19:36:11 -0700 Subject: fix/replace a lot of expect() calls --- rust/TODO | 2 + rust/src/api_server.rs | 5 +-- rust/src/database_entity_crud.rs | 88 ++++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 47 deletions(-) (limited to 'rust/src') diff --git a/rust/TODO b/rust/TODO index 1ed580e6..ed38b915 100644 --- a/rust/TODO +++ b/rust/TODO @@ -1,4 +1,6 @@ +- fatcat_api -> fatcat_api_schema (or spec? models? types?) +- fatcat -> fatcat-api-server - refactor rev creation (from an entity) into it's own function, across the board => attach to database module structs? => should make cockroachdb compatible (single use of CTE) diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index ecf0c242..57aeebea 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -49,7 +49,7 @@ macro_rules! count_entity { fn make_edit_context(conn: &DbConn, editgroup_id: Option) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth let editgroup_id = match editgroup_id { - None => FatCatId::from_uuid(&get_or_create_editgroup(editor_id, conn).expect("current editgroup")), + None => FatCatId::from_uuid(&get_or_create_editgroup(editor_id, conn)?), Some(param) => param, }; Ok(EditContext { @@ -386,8 +386,7 @@ impl Server { editgroup::description.eq(entity.description), editgroup::extra_json.eq(entity.extra), )) - .get_result(conn) - .expect("error creating edit group"); + .get_result(conn)?; Ok(Editgroup { id: Some(uuid2fcid(&row.id)), diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index b8ff195a..0f5c5f9d 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -222,14 +222,14 @@ macro_rules! generic_db_get_history { .limit(limit) .get_results(conn)?; - let history: Vec = rows.into_iter() - .map(|(eg_row, cl_row, e_row)| EntityHistoryEntry { - edit: e_row.into_model().expect("edit row to model"), + let history: Result> = rows.into_iter() + .map(|(eg_row, cl_row, e_row)| Ok(EntityHistoryEntry { + edit: e_row.into_model()?, editgroup: eg_row.into_model_partial(), changelog_entry: cl_row.into_model(), - }) + })) .collect(); - Ok(history) + history } } } @@ -470,16 +470,14 @@ impl EntityCrud for FileEntity { match &model.releases { None => (), Some(release_list) => { - let these_release_rows: Vec = release_list + let these_release_rows: Result> = release_list .iter() - .map(|r| FileReleaseRow { + .map(|r| Ok(FileReleaseRow { file_rev: rev_id.clone(), - target_release_ident_id: fcid2uuid(r) - // XXX: shouldn't expect - .expect("invalid fatcat identifier"), - }) + target_release_ident_id: FatCatId::from_str(r)?.to_uuid(), + })) .collect(); - file_release_rows.extend(these_release_rows); + file_release_rows.extend(these_release_rows?); } }; @@ -615,8 +613,7 @@ impl EntityCrud for ReleaseEntity { let refs: Vec = release_ref::table .filter(release_ref::release_rev.eq(rev_row.id)) .order(release_ref::index_val.asc()) - .get_results(conn) - .expect("fetch release refs") + .get_results(conn)? .into_iter() .map(|r: ReleaseRefRow| ReleaseRef { index: r.index_val, @@ -636,8 +633,7 @@ impl EntityCrud for ReleaseEntity { release_contrib::role.asc(), release_contrib::index_val.asc(), )) - .get_results(conn) - .expect("fetch release refs") + .get_results(conn)? .into_iter() .map(|c: ReleaseContribRow| ReleaseContrib { index: c.index_val, @@ -717,7 +713,7 @@ impl EntityCrud for ReleaseEntity { let rev_ids: Vec = insert_into(release_rev::table) .values(models.iter() - .map(|model| ReleaseRevNewRow { + .map(|model| Ok(ReleaseRevNewRow { title: model.title.clone(), release_type: model.release_type.clone(), release_status: model.release_status.clone(), @@ -731,18 +727,19 @@ impl EntityCrud for ReleaseEntity { volume: model.volume.clone(), issue: model.issue.clone(), pages: model.pages.clone(), - work_ident_id: model.work_id - .clone() - .map(|s| FatCatId::from_str(&s).expect("invalid fatcat identifier").to_uuid()) - .expect("release_revs must have a work_id by the time they are inserted; this is an internal soundness error"), - container_ident_id: model.container_id - .clone() - .map(|s| FatCatId::from_str(&s).expect("invalid fatcat identifier").to_uuid()), + work_ident_id: match model.work_id.clone() { + None => bail!("release_revs must have a work_id by the time they are inserted; this is an internal soundness error"), + Some(s) => FatCatId::from_str(&s)?.to_uuid(), + }, + container_ident_id: match model.container_id.clone() { + None => None, + Some(s) => Some(FatCatId::from_str(&s)?.to_uuid()), + }, publisher: model.publisher.clone(), language: model.language.clone(), extra_json: model.extra.clone() - }) - .collect::>()) + })) + .collect::>>()?) .returning(release_rev::id) .get_results(conn)?; @@ -755,11 +752,12 @@ impl EntityCrud for ReleaseEntity { Some(ref_list) => { let these_ref_rows: Vec = ref_list .iter() - .map(|r| ReleaseRefNewRow { + .map(|r| Ok(ReleaseRefNewRow { release_rev: rev_id.clone(), - target_release_ident_id: r.target_release_id - .clone() - .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), + target_release_ident_id: match r.target_release_id.clone() { + None => None, + Some(v) => Some(FatCatId::from_str(&v)?.to_uuid()), + }, index_val: r.index, key: r.key.clone(), container_title: r.container_title.clone(), @@ -767,8 +765,8 @@ impl EntityCrud for ReleaseEntity { title: r.title.clone(), locator: r.locator.clone(), extra_json: r.extra.clone(), - }) - .collect(); + })) + .collect::>>()?; release_ref_rows.extend(these_ref_rows); } }; @@ -778,18 +776,18 @@ impl EntityCrud for ReleaseEntity { Some(contrib_list) => { let these_contrib_rows: Vec = contrib_list .iter() - .map(|c| ReleaseContribNewRow { + .map(|c| Ok(ReleaseContribNewRow { release_rev: rev_id.clone(), - creator_ident_id: c.creator_id - .clone() - // XXX: shouldn't have these expects - .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")), + creator_ident_id: match c.creator_id.clone() { + None => None, + Some(v) => Some(FatCatId::from_str(&v)?.to_uuid()), + }, raw_name: c.raw_name.clone(), index_val: c.index, role: c.role.clone(), extra_json: c.extra.clone(), - }) - .collect(); + })) + .collect::>>()?; release_contrib_rows.extend(these_contrib_rows); } }; @@ -816,17 +814,19 @@ impl EntityCrud for ReleaseEntity { } let release_abstract_rows: Vec = abstract_list .into_iter() - .map(|c| ReleaseRevAbstractNewRow { + .map(|c| Ok(ReleaseRevAbstractNewRow { release_rev: rev_id.clone(), abstract_sha1: match c.content { Some(ref content) => Sha1::from(content).hexdigest(), - // XXX: shouldn't have these expects - None => c.sha1.clone().expect("either abstract_sha1 or content is required"), + None => match c.sha1.clone() { + Some(v) => v, + None => { bail!("either abstract_sha1 or content is required") } + }, }, lang: c.lang.clone(), mimetype: c.mimetype.clone(), - }) - .collect(); + })) + .collect::>>()?; insert_into(release_rev_abstract::table) .values(release_abstract_rows) .execute(conn)?; -- cgit v1.2.3 From a74644fe121564fb130ef1978177a39cc574e25d Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 21:53:17 -0700 Subject: fmt --- rust/src/api_helpers.rs | 5 +- rust/src/api_server.rs | 93 +++++--- rust/src/database_entity_crud.rs | 442 ++++++++++++++++++++++++--------------- rust/src/database_models.rs | 29 ++- rust/src/lib.rs | 2 +- 5 files changed, 364 insertions(+), 207 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 6c9a4e5f..fd0ad9e7 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -5,10 +5,11 @@ use diesel; use diesel::prelude::*; use errors::*; use regex::Regex; -use uuid::Uuid; use std::str::FromStr; +use uuid::Uuid; -pub type DbConn = diesel::r2d2::PooledConnection>; +pub type DbConn = + diesel::r2d2::PooledConnection>; /// This function should always be run within a transaction pub fn get_or_create_editgroup(editor_id: Uuid, conn: &PgConnection) -> Result { diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 602fbfb7..0e68777b 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -2,6 +2,7 @@ use api_helpers::*; use chrono; +use database_entity_crud::{EditContext, EntityCrud}; use database_models::*; use database_schema::{ abstracts, changelog, container_edit, container_ident, container_rev, creator_edit, @@ -15,10 +16,9 @@ use errors::*; use fatcat_api::models; use fatcat_api::models::*; use sha1::Sha1; +use std::str::FromStr; use uuid::Uuid; use ConnectionPool; -use database_entity_crud::{EntityCrud, EditContext}; -use std::str::FromStr; macro_rules! entity_batch_handler { ($post_batch_handler:ident, $model:ident) => { @@ -125,7 +125,6 @@ impl Server { _expand: Option, conn: &DbConn, ) -> Result { - CreatorEntity::db_get(conn, FatCatId::from_uuid(id)) } @@ -195,7 +194,6 @@ impl Server { expand: Option, conn: &DbConn, ) -> Result { - let mut release = ReleaseEntity::db_get(conn, FatCatId::from_uuid(id))?; // For now, if there is any expand param we do them all @@ -226,7 +224,6 @@ impl Server { } pub fn get_release_files_handler(&self, id: &str, conn: &DbConn) -> Result> { - let ident = FatCatId::from_str(id)?; let rows: Vec<(FileRevRow, FileIdentRow, FileReleaseRow)> = file_rev::table @@ -286,7 +283,12 @@ impl Server { let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } - pub fn delete_container_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + pub fn delete_container_handler( + &self, + id: &Uuid, + editgroup_id: Option, + conn: &DbConn, + ) -> Result { let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; let edit = ContainerEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() @@ -300,7 +302,6 @@ impl Server { let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; let edit = entity.db_create(conn, &edit_context)?; edit.into_model() - } pub fn update_creator_handler( @@ -313,7 +314,12 @@ impl Server { let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } - pub fn delete_creator_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + pub fn delete_creator_handler( + &self, + id: &Uuid, + editgroup_id: Option, + conn: &DbConn, + ) -> Result { let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; let edit = CreatorEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() @@ -339,7 +345,12 @@ impl Server { let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } - pub fn delete_file_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + pub fn delete_file_handler( + &self, + id: &Uuid, + editgroup_id: Option, + conn: &DbConn, + ) -> Result { let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; let edit = FileEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() @@ -365,7 +376,12 @@ impl Server { let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } - pub fn delete_release_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + pub fn delete_release_handler( + &self, + id: &Uuid, + editgroup_id: Option, + conn: &DbConn, + ) -> Result { let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; let edit = ReleaseEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() @@ -392,7 +408,12 @@ impl Server { edit.into_model() } - pub fn delete_work_handler(&self, id: &Uuid, editgroup_id: Option, conn: &DbConn) -> Result { + pub fn delete_work_handler( + &self, + id: &Uuid, + editgroup_id: Option, + conn: &DbConn, + ) -> Result { let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; let edit = WorkEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; @@ -627,34 +648,50 @@ impl Server { Ok(StatsResponse { extra: Some(val) }) } - entity_batch_handler!( - create_container_batch_handler, - ContainerEntity - ); - entity_batch_handler!( - create_creator_batch_handler, - CreatorEntity - ); + entity_batch_handler!(create_container_batch_handler, ContainerEntity); + entity_batch_handler!(create_creator_batch_handler, CreatorEntity); entity_batch_handler!(create_file_batch_handler, FileEntity); - entity_batch_handler!( - create_release_batch_handler, - ReleaseEntity - ); + entity_batch_handler!(create_release_batch_handler, ReleaseEntity); entity_batch_handler!(create_work_batch_handler, WorkEntity); - pub fn get_container_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + pub fn get_container_history_handler( + &self, + id: &Uuid, + limit: Option, + conn: &DbConn, + ) -> Result> { ContainerEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) } - pub fn get_creator_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + pub fn get_creator_history_handler( + &self, + id: &Uuid, + limit: Option, + conn: &DbConn, + ) -> Result> { CreatorEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) } - pub fn get_file_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + pub fn get_file_history_handler( + &self, + id: &Uuid, + limit: Option, + conn: &DbConn, + ) -> Result> { FileEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) } - pub fn get_release_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + pub fn get_release_history_handler( + &self, + id: &Uuid, + limit: Option, + conn: &DbConn, + ) -> Result> { ReleaseEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) } - pub fn get_work_history_handler(&self, id: &Uuid, limit: Option, conn: &DbConn,) -> Result> { + pub fn get_work_history_handler( + &self, + id: &Uuid, + limit: Option, + conn: &DbConn, + ) -> Result> { WorkEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) } } diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index 0f5c5f9d..3f13a27d 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -1,17 +1,16 @@ - -use sha1::Sha1; +use api_helpers::*; use chrono; +use database_models::*; +use database_schema::*; use diesel::prelude::*; use diesel::{self, insert_into}; -use database_schema::*; -use database_models::*; use errors::*; use fatcat_api::models::*; -use api_helpers::*; -use uuid::Uuid; +use serde_json; +use sha1::Sha1; use std::marker::Sized; use std::str::FromStr; -use serde_json; +use uuid::Uuid; pub struct EditContext { pub editor_id: FatCatId, @@ -37,7 +36,10 @@ pub struct EditContext { */ // Associated Type, not parametric -pub trait EntityCrud where Self: Sized { +pub trait EntityCrud +where + Self: Sized, +{ // TODO: could these be generic structs? Or do they need to be bound to a specific table? type EditRow; // EntityEditRow type EditNewRow; @@ -51,13 +53,34 @@ pub trait EntityCrud where Self: Sized { fn db_get(conn: &DbConn, ident: FatCatId) -> Result; fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result; fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result; - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result>; - fn db_update(&self, conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; - fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result; - fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result>; + fn db_create_batch( + conn: &DbConn, + edit_context: &EditContext, + models: &[&Self], + ) -> Result>; + fn db_update( + &self, + conn: &DbConn, + edit_context: &EditContext, + ident: FatCatId, + ) -> Result; + fn db_delete( + conn: &DbConn, + edit_context: &EditContext, + ident: FatCatId, + ) -> Result; + fn db_get_history( + conn: &DbConn, + ident: FatCatId, + limit: Option, + ) -> Result>; // Entity-specific Methods - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result; + fn db_from_row( + conn: &DbConn, + rev_row: Self::RevRow, + ident_row: Option, + ) -> Result; fn db_insert_rev(&self, conn: &DbConn) -> Result; fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result>; } @@ -75,7 +98,7 @@ macro_rules! generic_parse_editgroup_id{ } macro_rules! generic_db_get { - ($ident_table: ident, $rev_table: ident) => { + ($ident_table:ident, $rev_table:ident) => { fn db_get(conn: &DbConn, ident: FatCatId) -> Result { let (ident, rev): (Self::IdentRow, Self::RevRow) = $ident_table::table .find(ident.to_uuid()) @@ -84,19 +107,17 @@ macro_rules! generic_db_get { Self::db_from_row(conn, rev, Some(ident)) } - } + }; } macro_rules! generic_db_get_rev { - ($rev_table: ident) => { + ($rev_table:ident) => { fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result { - let rev = $rev_table::table - .find(rev_id) - .first(conn)?; - + let rev = $rev_table::table.find(rev_id).first(conn)?; + Self::db_from_row(conn, rev, None) } - } + }; } macro_rules! generic_db_create { @@ -121,34 +142,45 @@ macro_rules! generic_db_create { } macro_rules! generic_db_create_batch { - ($ident_table: ident, $edit_table: ident) => { - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result> { + ($ident_table:ident, $edit_table:ident) => { + fn db_create_batch( + conn: &DbConn, + edit_context: &EditContext, + models: &[&Self], + ) -> Result> { let rev_ids: Vec = Self::db_insert_revs(conn, models)?; let ident_ids: Vec = insert_into($ident_table::table) - .values(rev_ids.iter() - .map(|rev_id| Self::IdentNewRow { - rev_id: Some(rev_id.clone()), - is_live: edit_context.autoapprove, - redirect_id: None, - }) - .collect::>()) + .values( + rev_ids + .iter() + .map(|rev_id| Self::IdentNewRow { + rev_id: Some(rev_id.clone()), + is_live: edit_context.autoapprove, + redirect_id: None, + }) + .collect::>(), + ) .returning($ident_table::id) .get_results(conn)?; let edits: Vec = insert_into($edit_table::table) - .values(rev_ids.into_iter().zip(ident_ids.into_iter()) - .map(|(rev_id, ident_id)| Self::EditNewRow { - editgroup_id: edit_context.editgroup_id.to_uuid(), - rev_id: Some(rev_id), - ident_id: ident_id, - redirect_id: None, - prev_rev: None, - extra_json: edit_context.extra_json.clone(), - }) - .collect::>()) + .values( + rev_ids + .into_iter() + .zip(ident_ids.into_iter()) + .map(|(rev_id, ident_id)| Self::EditNewRow { + editgroup_id: edit_context.editgroup_id.to_uuid(), + rev_id: Some(rev_id), + ident_id: ident_id, + redirect_id: None, + prev_rev: None, + extra_json: edit_context.extra_json.clone(), + }) + .collect::>(), + ) .get_results(conn)?; Ok(edits) } - } + }; } macro_rules! generic_db_update { @@ -181,9 +213,12 @@ macro_rules! generic_db_update { } macro_rules! generic_db_delete { - ($ident_table: ident, $edit_table:ident) => { - fn db_delete(conn: &DbConn, edit_context: &EditContext, ident: FatCatId) -> Result { - + ($ident_table:ident, $edit_table:ident) => { + fn db_delete( + conn: &DbConn, + edit_context: &EditContext, + ident: FatCatId, + ) -> Result { let current: Self::IdentRow = $ident_table::table.find(ident.to_uuid()).first(conn)?; if current.is_live != true { // TODO: what if isn't live? 4xx not 5xx @@ -206,12 +241,16 @@ macro_rules! generic_db_delete { Ok(edit) } - } + }; } macro_rules! generic_db_get_history { ($edit_table:ident) => { - fn db_get_history(conn: &DbConn, ident: FatCatId, limit: Option) -> Result> { + fn db_get_history( + conn: &DbConn, + ident: FatCatId, + limit: Option, + ) -> Result> { let limit = limit.unwrap_or(50); // TODO: make a static let rows: Vec<(EditgroupRow, ChangelogRow, Self::EditRow)> = editgroup::table @@ -223,15 +262,17 @@ macro_rules! generic_db_get_history { .get_results(conn)?; let history: Result> = rows.into_iter() - .map(|(eg_row, cl_row, e_row)| Ok(EntityHistoryEntry { - edit: e_row.into_model()?, - editgroup: eg_row.into_model_partial(), - changelog_entry: cl_row.into_model(), - })) + .map(|(eg_row, cl_row, e_row)| { + Ok(EntityHistoryEntry { + edit: e_row.into_model()?, + editgroup: eg_row.into_model_partial(), + changelog_entry: cl_row.into_model(), + }) + }) .collect(); history } - } + }; } macro_rules! generic_db_insert_rev { @@ -259,8 +300,11 @@ impl EntityCrud for ContainerEntity { generic_db_get_history!(container_edit); generic_db_insert_rev!(); - fn db_from_row(_conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { - + fn db_from_row( + _conn: &DbConn, + rev_row: Self::RevRow, + ident_row: Option, + ) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( Some(i.state().unwrap().shortname()), @@ -287,7 +331,6 @@ impl EntityCrud for ContainerEntity { } fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { - // first verify external identifier syntax for entity in models { if let Some(ref extid) = entity.wikidata_qid { @@ -299,17 +342,20 @@ impl EntityCrud for ContainerEntity { } let rev_ids: Vec = insert_into(container_rev::table) - .values(models.iter() - .map(|model| ContainerRevNewRow { - name: model.name.clone(), - publisher: model.publisher.clone(), - issnl: model.issnl.clone(), - wikidata_qid: model.wikidata_qid.clone(), - abbrev: model.abbrev.clone(), - coden: model.coden.clone(), - extra_json: model.extra.clone() - }) - .collect::>()) + .values( + models + .iter() + .map(|model| ContainerRevNewRow { + name: model.name.clone(), + publisher: model.publisher.clone(), + issnl: model.issnl.clone(), + wikidata_qid: model.wikidata_qid.clone(), + abbrev: model.abbrev.clone(), + coden: model.coden.clone(), + extra_json: model.extra.clone(), + }) + .collect::>(), + ) .returning(container_rev::id) .get_results(conn)?; Ok(rev_ids) @@ -333,7 +379,11 @@ impl EntityCrud for CreatorEntity { generic_db_get_history!(creator_edit); generic_db_insert_rev!(); - fn db_from_row(_conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + fn db_from_row( + _conn: &DbConn, + rev_row: Self::RevRow, + ident_row: Option, + ) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( Some(i.state().unwrap().shortname()), @@ -358,7 +408,6 @@ impl EntityCrud for CreatorEntity { } fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { - // first verify external identifier syntax for entity in models { if let Some(ref extid) = entity.orcid { @@ -370,16 +419,19 @@ impl EntityCrud for CreatorEntity { } let rev_ids: Vec = insert_into(creator_rev::table) - .values(models.iter() - .map(|model| CreatorRevNewRow { - display_name: model.display_name.clone(), - given_name: model.given_name.clone(), - surname: model.surname.clone(), - orcid: model.orcid.clone(), - wikidata_qid: model.wikidata_qid.clone(), - extra_json: model.extra.clone() - }) - .collect::>()) + .values( + models + .iter() + .map(|model| CreatorRevNewRow { + display_name: model.display_name.clone(), + given_name: model.given_name.clone(), + surname: model.surname.clone(), + orcid: model.orcid.clone(), + wikidata_qid: model.wikidata_qid.clone(), + extra_json: model.extra.clone(), + }) + .collect::>(), + ) .returning(creator_rev::id) .get_results(conn)?; Ok(rev_ids) @@ -403,7 +455,11 @@ impl EntityCrud for FileEntity { generic_db_get_history!(file_edit); generic_db_insert_rev!(); - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + fn db_from_row( + conn: &DbConn, + rev_row: Self::RevRow, + ident_row: Option, + ) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( Some(i.state().unwrap().shortname()), @@ -448,18 +504,20 @@ impl EntityCrud for FileEntity { } fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { - let rev_ids: Vec = insert_into(file_rev::table) - .values(models.iter() - .map(|model| FileRevNewRow { - size: model.size, - sha1: model.sha1.clone(), - sha256: model.sha256.clone(), - md5: model.md5.clone(), - mimetype: model.mimetype.clone(), - extra_json: model.extra.clone() - }) - .collect::>()) + .values( + models + .iter() + .map(|model| FileRevNewRow { + size: model.size, + sha1: model.sha1.clone(), + sha256: model.sha256.clone(), + md5: model.md5.clone(), + mimetype: model.mimetype.clone(), + extra_json: model.extra.clone(), + }) + .collect::>(), + ) .returning(file_rev::id) .get_results(conn)?; @@ -472,10 +530,12 @@ impl EntityCrud for FileEntity { Some(release_list) => { let these_release_rows: Result> = release_list .iter() - .map(|r| Ok(FileReleaseRow { - file_rev: rev_id.clone(), - target_release_ident_id: FatCatId::from_str(r)?.to_uuid(), - })) + .map(|r| { + Ok(FileReleaseRow { + file_rev: rev_id.clone(), + target_release_ident_id: FatCatId::from_str(r)?.to_uuid(), + }) + }) .collect(); file_release_rows.extend(these_release_rows?); } @@ -537,7 +597,11 @@ impl EntityCrud for ReleaseEntity { Ok(edits.pop().unwrap()) } - fn db_create_batch(conn: &DbConn, edit_context: &EditContext, models: &[&Self]) -> Result> { + fn db_create_batch( + conn: &DbConn, + edit_context: &EditContext, + models: &[&Self], + ) -> Result> { // This isn't the generic implementation because we need to create Work entities for each // of the release entities passed (at least in the common case) @@ -558,49 +622,65 @@ impl EntityCrud for ReleaseEntity { } // create the works, then pluck the list of idents from the result - let new_work_edits = WorkEntity::db_create_batch(conn, edit_context, new_work_models.as_slice())?; + let new_work_edits = + WorkEntity::db_create_batch(conn, edit_context, new_work_models.as_slice())?; let mut new_work_ids: Vec = new_work_edits.iter().map(|edit| edit.ident_id).collect(); // Copy all the release models, and ensure that each has work_id set, using the new work // idents. There should be one new work ident for each release missing one. - let models_with_work_ids: Vec = models.iter().map(|model| { - let mut model = (*model).clone(); - if model.work_id.is_none() { - model.work_id = Some(FatCatId::from_uuid(&new_work_ids.pop().unwrap()).to_string()) - } - model - }).collect(); + let models_with_work_ids: Vec = models + .iter() + .map(|model| { + let mut model = (*model).clone(); + if model.work_id.is_none() { + model.work_id = + Some(FatCatId::from_uuid(&new_work_ids.pop().unwrap()).to_string()) + } + model + }) + .collect(); let model_refs: Vec<&Self> = models_with_work_ids.iter().map(|s| s).collect(); let models = model_refs.as_slice(); // The rest here is copy/pasta from the generic (how to avoid copypasta?) let rev_ids: Vec = Self::db_insert_revs(conn, models)?; let ident_ids: Vec = insert_into(release_ident::table) - .values(rev_ids.iter() - .map(|rev_id| Self::IdentNewRow { - rev_id: Some(rev_id.clone()), - is_live: edit_context.autoapprove, - redirect_id: None, - }) - .collect::>()) + .values( + rev_ids + .iter() + .map(|rev_id| Self::IdentNewRow { + rev_id: Some(rev_id.clone()), + is_live: edit_context.autoapprove, + redirect_id: None, + }) + .collect::>(), + ) .returning(release_ident::id) .get_results(conn)?; let edits: Vec = insert_into(release_edit::table) - .values(rev_ids.into_iter().zip(ident_ids.into_iter()) - .map(|(rev_id, ident_id)| Self::EditNewRow { - editgroup_id: edit_context.editgroup_id.to_uuid(), - rev_id: Some(rev_id), - ident_id: ident_id, - redirect_id: None, - prev_rev: None, - extra_json: edit_context.extra_json.clone(), - }) - .collect::>()) + .values( + rev_ids + .into_iter() + .zip(ident_ids.into_iter()) + .map(|(rev_id, ident_id)| Self::EditNewRow { + editgroup_id: edit_context.editgroup_id.to_uuid(), + rev_id: Some(rev_id), + ident_id: ident_id, + redirect_id: None, + prev_rev: None, + extra_json: edit_context.extra_json.clone(), + }) + .collect::>(), + ) .get_results(conn)?; Ok(edits) } - fn db_from_row(conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { + fn db_from_row( + conn: &DbConn, + rev_row: Self::RevRow, + ident_row: Option, + ) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( Some(i.state().unwrap().shortname()), @@ -623,7 +703,8 @@ impl EntityCrud for ReleaseEntity { year: r.year, title: r.title, locator: r.locator, - target_release_id: r.target_release_ident_id.map(|v| FatCatId::from_uuid(&v).to_string()), + target_release_id: r.target_release_ident_id + .map(|v| FatCatId::from_uuid(&v).to_string()), }) .collect(); @@ -640,7 +721,8 @@ impl EntityCrud for ReleaseEntity { raw_name: c.raw_name, role: c.role, extra: c.extra_json, - creator_id: c.creator_ident_id.map(|v| FatCatId::from_uuid(&v).to_string()), + creator_id: c.creator_ident_id + .map(|v| FatCatId::from_uuid(&v).to_string()), creator: None, }) .collect(); @@ -664,7 +746,8 @@ impl EntityCrud for ReleaseEntity { title: rev_row.title, release_type: rev_row.release_type, release_status: rev_row.release_status, - release_date: rev_row.release_date + release_date: rev_row + .release_date .map(|v| chrono::DateTime::from_utc(v.and_hms(0, 0, 0), chrono::Utc)), doi: rev_row.doi, pmid: rev_row.pmid, @@ -677,7 +760,9 @@ impl EntityCrud for ReleaseEntity { pages: rev_row.pages, files: None, container: None, - container_id: rev_row.container_ident_id.map(|u| FatCatId::from_uuid(&u).to_string()), + container_id: rev_row + .container_ident_id + .map(|u| FatCatId::from_uuid(&u).to_string()), publisher: rev_row.publisher, language: rev_row.language, work_id: Some(FatCatId::from_uuid(&rev_row.work_ident_id).to_string()), @@ -694,7 +779,6 @@ impl EntityCrud for ReleaseEntity { } fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { - // first verify external identifier syntax for entity in models { if let Some(ref extid) = entity.doi { @@ -712,8 +796,10 @@ impl EntityCrud for ReleaseEntity { } let rev_ids: Vec = insert_into(release_rev::table) - .values(models.iter() - .map(|model| Ok(ReleaseRevNewRow { + .values(models + .iter() + .map(|model| { + Ok(ReleaseRevNewRow { title: model.title.clone(), release_type: model.release_type.clone(), release_status: model.release_status.clone(), @@ -738,7 +824,8 @@ impl EntityCrud for ReleaseEntity { publisher: model.publisher.clone(), language: model.language.clone(), extra_json: model.extra.clone() - })) + }) + }) .collect::>>()?) .returning(release_rev::id) .get_results(conn)?; @@ -752,20 +839,22 @@ impl EntityCrud for ReleaseEntity { Some(ref_list) => { let these_ref_rows: Vec = ref_list .iter() - .map(|r| Ok(ReleaseRefNewRow { - release_rev: rev_id.clone(), - target_release_ident_id: match r.target_release_id.clone() { - None => None, - Some(v) => Some(FatCatId::from_str(&v)?.to_uuid()), - }, - index_val: r.index, - key: r.key.clone(), - container_title: r.container_title.clone(), - year: r.year, - title: r.title.clone(), - locator: r.locator.clone(), - extra_json: r.extra.clone(), - })) + .map(|r| { + Ok(ReleaseRefNewRow { + release_rev: rev_id.clone(), + target_release_ident_id: match r.target_release_id.clone() { + None => None, + Some(v) => Some(FatCatId::from_str(&v)?.to_uuid()), + }, + index_val: r.index, + key: r.key.clone(), + container_title: r.container_title.clone(), + year: r.year, + title: r.title.clone(), + locator: r.locator.clone(), + extra_json: r.extra.clone(), + }) + }) .collect::>>()?; release_ref_rows.extend(these_ref_rows); } @@ -776,17 +865,19 @@ impl EntityCrud for ReleaseEntity { Some(contrib_list) => { let these_contrib_rows: Vec = contrib_list .iter() - .map(|c| Ok(ReleaseContribNewRow { - release_rev: rev_id.clone(), - creator_ident_id: match c.creator_id.clone() { - None => None, - Some(v) => Some(FatCatId::from_str(&v)?.to_uuid()), - }, - raw_name: c.raw_name.clone(), - index_val: c.index, - role: c.role.clone(), - extra_json: c.extra.clone(), - })) + .map(|c| { + Ok(ReleaseContribNewRow { + release_rev: rev_id.clone(), + creator_ident_id: match c.creator_id.clone() { + None => None, + Some(v) => Some(FatCatId::from_str(&v)?.to_uuid()), + }, + raw_name: c.raw_name.clone(), + index_val: c.index, + role: c.role.clone(), + extra_json: c.extra.clone(), + }) + }) .collect::>>()?; release_contrib_rows.extend(these_contrib_rows); } @@ -814,18 +905,20 @@ impl EntityCrud for ReleaseEntity { } let release_abstract_rows: Vec = abstract_list .into_iter() - .map(|c| Ok(ReleaseRevAbstractNewRow { - release_rev: rev_id.clone(), - abstract_sha1: match c.content { - Some(ref content) => Sha1::from(content).hexdigest(), - None => match c.sha1.clone() { - Some(v) => v, - None => { bail!("either abstract_sha1 or content is required") } + .map(|c| { + Ok(ReleaseRevAbstractNewRow { + release_rev: rev_id.clone(), + abstract_sha1: match c.content { + Some(ref content) => Sha1::from(content).hexdigest(), + None => match c.sha1.clone() { + Some(v) => v, + None => bail!("either abstract_sha1 or content is required"), + }, }, - }, - lang: c.lang.clone(), - mimetype: c.mimetype.clone(), - })) + lang: c.lang.clone(), + mimetype: c.mimetype.clone(), + }) + }) .collect::>>()?; insert_into(release_rev_abstract::table) .values(release_abstract_rows) @@ -866,8 +959,11 @@ impl EntityCrud for WorkEntity { generic_db_get_history!(work_edit); generic_db_insert_rev!(); - fn db_from_row(_conn: &DbConn, rev_row: Self::RevRow, ident_row: Option) -> Result { - + fn db_from_row( + _conn: &DbConn, + rev_row: Self::RevRow, + ident_row: Option, + ) -> Result { let (state, ident_id, redirect_id) = match ident_row { Some(i) => ( Some(i.state().unwrap().shortname()), @@ -889,12 +985,16 @@ impl EntityCrud for WorkEntity { fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result> { let rev_ids: Vec = insert_into(work_rev::table) - .values(models.iter() - .map(|model| WorkRevNewRow { extra_json: model.extra.clone() } ) - .collect::>()) + .values( + models + .iter() + .map(|model| WorkRevNewRow { + extra_json: model.extra.clone(), + }) + .collect::>(), + ) .returning(work_rev::id) .get_results(conn)?; Ok(rev_ids) } } - diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 2d6788eb..93e6a0fe 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -37,9 +37,14 @@ pub trait EntityEditRow { // Helper for constructing tables macro_rules! entity_structs { - ($edit_table:expr, $edit_struct:ident, $edit_new_struct:ident, $ident_table:expr, - $ident_struct:ident, $ident_new_struct:ident) => { - + ( + $edit_table:expr, + $edit_struct:ident, + $edit_new_struct:ident, + $ident_table:expr, + $ident_struct:ident, + $ident_new_struct:ident + ) => { #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset, QueryableByName)] #[table_name = $edit_table] pub struct $edit_struct { @@ -218,7 +223,14 @@ pub struct FileRevNewRow { pub mimetype: Option, } -entity_structs!("file_edit", FileEditRow, FileEditNewRow, "file_ident", FileIdentRow, FileIdentNewRow); +entity_structs!( + "file_edit", + FileEditRow, + FileEditNewRow, + "file_ident", + FileIdentRow, + FileIdentNewRow +); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "release_rev"] @@ -289,7 +301,14 @@ pub struct WorkRevNewRow { pub extra_json: Option, } -entity_structs!("work_edit", WorkEditRow, WorkEditNewRow, "work_ident", WorkIdentRow, WorkIdentNewRow); +entity_structs!( + "work_edit", + WorkEditRow, + WorkEditNewRow, + "work_ident", + WorkIdentRow, + WorkIdentNewRow +); #[derive(Debug, Queryable, Identifiable, Associations, AsChangeset)] #[table_name = "release_rev_abstract"] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a938486b..38f9b343 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -25,9 +25,9 @@ extern crate sha1; pub mod api_helpers; pub mod api_server; pub mod api_wrappers; +pub mod database_entity_crud; pub mod database_models; pub mod database_schema; -pub mod database_entity_crud; mod errors { // Create the Error, ErrorKind, ResultExt, and Result types -- cgit v1.2.3 From fc2025b8db9e058d429136f382749ade52df326c Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 22:32:20 -0700 Subject: fixes after autoaccept merge --- rust/src/api_helpers.rs | 1 + rust/src/api_server.rs | 79 ++++++++++++++++++---------------------- rust/src/api_wrappers.rs | 2 +- rust/src/database_entity_crud.rs | 8 ++-- 4 files changed, 41 insertions(+), 49 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index fd0ad9e7..925a6073 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -91,6 +91,7 @@ pub fn accept_editgroup(editgroup_id: Uuid, conn: &PgConnection) -> Result, conn: &DbConn, ) -> Result> { - // editgroup override logic based on parameters - let eg_id: Option = match (editgroup, autoaccept) { - (Some(eg_string), _) => Some(fcid2uuid(&eg_string)?), - (None, true) => { - let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) - .values((editgroup::editor_id.eq(editor_id),)) - .get_result(conn)?; - Some(eg_row.id) - }, - (None, false) => None + + let editgroup_id: Option = match editgroup { + Some(s) => Some(FatCatId::from_str(&s)?), + None => None, }; - for entity in entity_list { - let mut e = entity.clone(); - // override individual editgroup IDs (if set earlier) - if let Some(inner_id) = eg_id { - e.editgroup_id = Some(uuid2fcid(&inner_id)); - } - // actual wrapped function call here - ret.push(self.$post_handler(e, autoaccept, conn)?); - } - let mut edit_context = make_edit_context(conn, eg_id)?; - edit_context.autoaccept = autoaccept; + let edit_context = make_edit_context(conn, editgroup_id.clone(), autoaccept)?; let model_list: Vec<&models::$model> = entity_list.iter().map(|e| e).collect(); let edits = $model::db_create_batch(conn, &edit_context, model_list.as_slice())?; + if autoaccept { - // if autoaccept, eg_id is always Some let _clr: ChangelogRow = diesel::insert_into(changelog::table) - .values((changelog::editgroup_id.eq(eg_id.unwrap()),)) + // if autoaccept, eg_id is always Some + .values((changelog::editgroup_id.eq(edit_context.editgroup_id.to_uuid()),)) .get_result(conn)?; } edits.into_iter().map(|e| e.into_model()).collect() @@ -75,17 +59,24 @@ macro_rules! count_entity { }}; } -fn make_edit_context(conn: &DbConn, editgroup_id: Option) -> Result { +fn make_edit_context(conn: &DbConn, editgroup_id: Option, autoaccept: bool) -> Result { let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - let editgroup_id = match editgroup_id { - None => FatCatId::from_uuid(&get_or_create_editgroup(editor_id, conn)?), - Some(param) => param, + let editgroup_id: FatCatId = match (editgroup_id, autoaccept) { + (Some(eg), _) => eg, + // If autoaccept and no editgroup_id passed, always create a new one for this transaction + (None, true) => { + let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) + .values((editgroup::editor_id.eq(editor_id),)) + .get_result(conn)?; + FatCatId::from_uuid(&eg_row.id) + }, + (None, false) => FatCatId::from_uuid(&get_or_create_editgroup(editor_id, conn)?), }; Ok(EditContext { editor_id: FatCatId::from_uuid(&editor_id), editgroup_id: editgroup_id, extra_json: None, - autoapprove: false, + autoaccept: autoaccept, }) } @@ -268,7 +259,7 @@ impl Server { entity: models::ContainerEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } @@ -279,7 +270,7 @@ impl Server { entity: models::ContainerEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -289,7 +280,7 @@ impl Server { editgroup_id: Option, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)), false)?; let edit = ContainerEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -299,7 +290,7 @@ impl Server { entity: models::CreatorEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } @@ -310,7 +301,7 @@ impl Server { entity: models::CreatorEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -320,7 +311,7 @@ impl Server { editgroup_id: Option, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)), false)?; let edit = CreatorEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -330,7 +321,7 @@ impl Server { entity: models::FileEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } @@ -341,7 +332,7 @@ impl Server { entity: models::FileEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -351,7 +342,7 @@ impl Server { editgroup_id: Option, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)), false)?; let edit = FileEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -361,7 +352,7 @@ impl Server { entity: models::ReleaseEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } @@ -372,7 +363,7 @@ impl Server { entity: models::ReleaseEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -382,7 +373,7 @@ impl Server { editgroup_id: Option, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)), false)?; let edit = ReleaseEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -392,7 +383,7 @@ impl Server { entity: models::WorkEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_create(conn, &edit_context)?; edit.into_model() } @@ -403,7 +394,7 @@ impl Server { entity: models::WorkEntity, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?)?; + let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; let edit = entity.db_update(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() } @@ -414,7 +405,7 @@ impl Server { editgroup_id: Option, conn: &DbConn, ) -> Result { - let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)))?; + let edit_context = make_edit_context(conn, editgroup_id.map(|u| FatCatId::from_uuid(&u)), false)?; let edit = WorkEntity::db_delete(conn, &edit_context, FatCatId::from_uuid(id))?; edit.into_model() diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 6272814e..faafe984 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -61,7 +61,7 @@ macro_rules! wrap_entity_handlers { _context: &Context, ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.$post_handler(entity, false, &conn)) { + let ret = match conn.transaction(|| self.$post_handler(entity, &conn)) { Ok(edit) => $post_resp::CreatedEntity(edit), Err(Error(ErrorKind::Diesel(e), _)) => diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index 3f13a27d..9a47c2de 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -3,7 +3,7 @@ use chrono; use database_models::*; use database_schema::*; use diesel::prelude::*; -use diesel::{self, insert_into}; +use diesel::insert_into; use errors::*; use fatcat_api::models::*; use serde_json; @@ -16,7 +16,7 @@ pub struct EditContext { pub editor_id: FatCatId, pub editgroup_id: FatCatId, pub extra_json: Option, - pub autoapprove: bool, + pub autoaccept: bool, } /* One goal here is to abstract the non-entity-specific bits into generic traits or functions, @@ -155,7 +155,7 @@ macro_rules! generic_db_create_batch { .iter() .map(|rev_id| Self::IdentNewRow { rev_id: Some(rev_id.clone()), - is_live: edit_context.autoapprove, + is_live: edit_context.autoaccept, redirect_id: None, }) .collect::>(), @@ -650,7 +650,7 @@ impl EntityCrud for ReleaseEntity { .iter() .map(|rev_id| Self::IdentNewRow { rev_id: Some(rev_id.clone()), - is_live: edit_context.autoapprove, + is_live: edit_context.autoaccept, redirect_id: None, }) .collect::>(), -- cgit v1.2.3 From 7354899493f6448bed5698ad6ade1dbebcf39379 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 7 Sep 2018 22:37:09 -0700 Subject: TODO and comment --- python/README_codegen.md | 10 + python/fatcat_client/api/default_api.py | 1948 ++++++++++++++++++------ python/tests/codegen_tests/test_default_api.py | 60 + rust/TODO | 2 + rust/src/api_server.rs | 1 - 5 files changed, 1561 insertions(+), 460 deletions(-) (limited to 'rust/src') diff --git a/python/README_codegen.md b/python/README_codegen.md index 393fae32..0d072dde 100644 --- a/python/README_codegen.md +++ b/python/README_codegen.md @@ -80,6 +80,11 @@ Class | Method | HTTP request | Description *DefaultApi* | [**create_release_batch**](docs/DefaultApi.md#create_release_batch) | **POST** /release/batch | *DefaultApi* | [**create_work**](docs/DefaultApi.md#create_work) | **POST** /work | *DefaultApi* | [**create_work_batch**](docs/DefaultApi.md#create_work_batch) | **POST** /work/batch | +*DefaultApi* | [**delete_container**](docs/DefaultApi.md#delete_container) | **DELETE** /container/{id} | +*DefaultApi* | [**delete_creator**](docs/DefaultApi.md#delete_creator) | **DELETE** /creator/{id} | +*DefaultApi* | [**delete_file**](docs/DefaultApi.md#delete_file) | **DELETE** /file/{id} | +*DefaultApi* | [**delete_release**](docs/DefaultApi.md#delete_release) | **DELETE** /release/{id} | +*DefaultApi* | [**delete_work**](docs/DefaultApi.md#delete_work) | **DELETE** /work/{id} | *DefaultApi* | [**get_changelog**](docs/DefaultApi.md#get_changelog) | **GET** /changelog | *DefaultApi* | [**get_changelog_entry**](docs/DefaultApi.md#get_changelog_entry) | **GET** /changelog/{id} | *DefaultApi* | [**get_container**](docs/DefaultApi.md#get_container) | **GET** /container/{id} | @@ -103,6 +108,11 @@ Class | Method | HTTP request | Description *DefaultApi* | [**lookup_creator**](docs/DefaultApi.md#lookup_creator) | **GET** /creator/lookup | *DefaultApi* | [**lookup_file**](docs/DefaultApi.md#lookup_file) | **GET** /file/lookup | *DefaultApi* | [**lookup_release**](docs/DefaultApi.md#lookup_release) | **GET** /release/lookup | +*DefaultApi* | [**update_container**](docs/DefaultApi.md#update_container) | **PUT** /container/{id} | +*DefaultApi* | [**update_creator**](docs/DefaultApi.md#update_creator) | **PUT** /creator/{id} | +*DefaultApi* | [**update_file**](docs/DefaultApi.md#update_file) | **PUT** /file/{id} | +*DefaultApi* | [**update_release**](docs/DefaultApi.md#update_release) | **PUT** /release/{id} | +*DefaultApi* | [**update_work**](docs/DefaultApi.md#update_work) | **PUT** /work/{id} | ## Documentation For Models diff --git a/python/fatcat_client/api/default_api.py b/python/fatcat_client/api/default_api.py index a0298750..23c8d7ca 100644 --- a/python/fatcat_client/api/default_api.py +++ b/python/fatcat_client/api/default_api.py @@ -1245,43 +1245,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_changelog(self, **kwargs): # noqa: E501 - """get_changelog # noqa: E501 + def delete_container(self, id, **kwargs): # noqa: E501 + """delete_container # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_changelog(async=True) + >>> thread = api.delete_container(id, async=True) >>> result = thread.get() :param async bool - :param int limit: - :return: list[ChangelogEntry] + :param str id: (required) + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_changelog_with_http_info(**kwargs) # noqa: E501 + return self.delete_container_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_changelog_with_http_info(**kwargs) # noqa: E501 + (data) = self.delete_container_with_http_info(id, **kwargs) # noqa: E501 return data - def get_changelog_with_http_info(self, **kwargs): # noqa: E501 - """get_changelog # noqa: E501 + def delete_container_with_http_info(self, id, **kwargs): # noqa: E501 + """delete_container # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_changelog_with_http_info(async=True) + >>> thread = api.delete_container_with_http_info(id, async=True) >>> result = thread.get() :param async bool - :param int limit: - :return: list[ChangelogEntry] + :param str id: (required) + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['limit'] # noqa: E501 + all_params = ['id', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1292,18 +1294,24 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_changelog" % key + " to method delete_container" % key ) params[key] = val del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `delete_container`") # noqa: E501 collection_formats = {} path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'limit' in params: - query_params.append(('limit', params['limit'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -1323,14 +1331,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/changelog', 'GET', + '/container/{id}', 'DELETE', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[ChangelogEntry]', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1338,43 +1346,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_changelog_entry(self, id, **kwargs): # noqa: E501 - """get_changelog_entry # noqa: E501 + def delete_creator(self, id, **kwargs): # noqa: E501 + """delete_creator # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_changelog_entry(id, async=True) + >>> thread = api.delete_creator(id, async=True) >>> result = thread.get() :param async bool - :param int id: (required) - :return: ChangelogEntry + :param str id: (required) + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_changelog_entry_with_http_info(id, **kwargs) # noqa: E501 + return self.delete_creator_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_changelog_entry_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.delete_creator_with_http_info(id, **kwargs) # noqa: E501 return data - def get_changelog_entry_with_http_info(self, id, **kwargs): # noqa: E501 - """get_changelog_entry # noqa: E501 + def delete_creator_with_http_info(self, id, **kwargs): # noqa: E501 + """delete_creator # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_changelog_entry_with_http_info(id, async=True) + >>> thread = api.delete_creator_with_http_info(id, async=True) >>> result = thread.get() :param async bool - :param int id: (required) - :return: ChangelogEntry + :param str id: (required) + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['id'] # noqa: E501 + all_params = ['id', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1385,14 +1395,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_changelog_entry" % key + " to method delete_creator" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_changelog_entry`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `delete_creator`") # noqa: E501 collection_formats = {} @@ -1401,6 +1411,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -1420,14 +1432,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/changelog/{id}', 'GET', + '/creator/{id}', 'DELETE', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='ChangelogEntry', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1435,45 +1447,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_container(self, id, **kwargs): # noqa: E501 - """get_container # noqa: E501 + def delete_file(self, id, **kwargs): # noqa: E501 + """delete_file # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_container(id, async=True) + >>> thread = api.delete_file(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: ContainerEntity + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_container_with_http_info(id, **kwargs) # noqa: E501 + return self.delete_file_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_container_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.delete_file_with_http_info(id, **kwargs) # noqa: E501 return data - def get_container_with_http_info(self, id, **kwargs): # noqa: E501 - """get_container # noqa: E501 + def delete_file_with_http_info(self, id, **kwargs): # noqa: E501 + """delete_file # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_container_with_http_info(id, async=True) + >>> thread = api.delete_file_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: ContainerEntity + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'expand'] # noqa: E501 + all_params = ['id', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1484,14 +1496,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_container" % key + " to method delete_file" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_container`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `delete_file`") # noqa: E501 collection_formats = {} @@ -1500,8 +1512,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'expand' in params: - query_params.append(('expand', params['expand'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -1521,14 +1533,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/container/{id}', 'GET', + '/file/{id}', 'DELETE', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='ContainerEntity', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1536,45 +1548,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_container_history(self, id, **kwargs): # noqa: E501 - """get_container_history # noqa: E501 + def delete_release(self, id, **kwargs): # noqa: E501 + """delete_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_container_history(id, async=True) + >>> thread = api.delete_release(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_container_history_with_http_info(id, **kwargs) # noqa: E501 + return self.delete_release_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_container_history_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.delete_release_with_http_info(id, **kwargs) # noqa: E501 return data - def get_container_history_with_http_info(self, id, **kwargs): # noqa: E501 - """get_container_history # noqa: E501 + def delete_release_with_http_info(self, id, **kwargs): # noqa: E501 + """delete_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_container_history_with_http_info(id, async=True) + >>> thread = api.delete_release_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'limit'] # noqa: E501 + all_params = ['id', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1585,14 +1597,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_container_history" % key + " to method delete_release" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_container_history`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `delete_release`") # noqa: E501 collection_formats = {} @@ -1601,8 +1613,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'limit' in params: - query_params.append(('limit', params['limit'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -1622,14 +1634,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/container/{id}/history', 'GET', + '/release/{id}', 'DELETE', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[EntityHistoryEntry]', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1637,45 +1649,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_creator(self, id, **kwargs): # noqa: E501 - """get_creator # noqa: E501 + def delete_work(self, id, **kwargs): # noqa: E501 + """delete_work # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_creator(id, async=True) + >>> thread = api.delete_work(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: CreatorEntity + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_creator_with_http_info(id, **kwargs) # noqa: E501 + return self.delete_work_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_creator_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.delete_work_with_http_info(id, **kwargs) # noqa: E501 return data - def get_creator_with_http_info(self, id, **kwargs): # noqa: E501 - """get_creator # noqa: E501 + def delete_work_with_http_info(self, id, **kwargs): # noqa: E501 + """delete_work # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_creator_with_http_info(id, async=True) + >>> thread = api.delete_work_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: CreatorEntity + :param str editgroup: + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'expand'] # noqa: E501 + all_params = ['id', 'editgroup'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1686,14 +1698,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_creator" % key + " to method delete_work" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_creator`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `delete_work`") # noqa: E501 collection_formats = {} @@ -1702,8 +1714,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'expand' in params: - query_params.append(('expand', params['expand'])) # noqa: E501 + if 'editgroup' in params: + query_params.append(('editgroup', params['editgroup'])) # noqa: E501 header_params = {} @@ -1723,14 +1735,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/creator/{id}', 'GET', + '/work/{id}', 'DELETE', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='CreatorEntity', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1738,45 +1750,43 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_creator_history(self, id, **kwargs): # noqa: E501 - """get_creator_history # noqa: E501 + def get_changelog(self, **kwargs): # noqa: E501 + """get_changelog # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_creator_history(id, async=True) + >>> thread = api.get_changelog(async=True) >>> result = thread.get() :param async bool - :param str id: (required) :param int limit: - :return: list[EntityHistoryEntry] + :return: list[ChangelogEntry] If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_creator_history_with_http_info(id, **kwargs) # noqa: E501 + return self.get_changelog_with_http_info(**kwargs) # noqa: E501 else: - (data) = self.get_creator_history_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_changelog_with_http_info(**kwargs) # noqa: E501 return data - def get_creator_history_with_http_info(self, id, **kwargs): # noqa: E501 - """get_creator_history # noqa: E501 + def get_changelog_with_http_info(self, **kwargs): # noqa: E501 + """get_changelog # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_creator_history_with_http_info(id, async=True) + >>> thread = api.get_changelog_with_http_info(async=True) >>> result = thread.get() :param async bool - :param str id: (required) :param int limit: - :return: list[EntityHistoryEntry] + :return: list[ChangelogEntry] If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'limit'] # noqa: E501 + all_params = ['limit'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1787,20 +1797,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_creator_history" % key + " to method get_changelog" % key ) params[key] = val del params['kwargs'] - # verify the required parameter 'id' is set - if ('id' not in params or - params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_creator_history`") # noqa: E501 collection_formats = {} path_params = {} - if 'id' in params: - path_params['id'] = params['id'] # noqa: E501 query_params = [] if 'limit' in params: @@ -1824,14 +1828,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/creator/{id}/history', 'GET', + '/changelog', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[EntityHistoryEntry]', # noqa: E501 + response_type='list[ChangelogEntry]', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1839,38 +1843,38 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_creator_releases(self, id, **kwargs): # noqa: E501 - """get_creator_releases # noqa: E501 + def get_changelog_entry(self, id, **kwargs): # noqa: E501 + """get_changelog_entry # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_creator_releases(id, async=True) + >>> thread = api.get_changelog_entry(id, async=True) >>> result = thread.get() :param async bool - :param str id: (required) - :return: list[ReleaseEntity] + :param int id: (required) + :return: ChangelogEntry If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_creator_releases_with_http_info(id, **kwargs) # noqa: E501 + return self.get_changelog_entry_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_creator_releases_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_changelog_entry_with_http_info(id, **kwargs) # noqa: E501 return data - def get_creator_releases_with_http_info(self, id, **kwargs): # noqa: E501 - """get_creator_releases # noqa: E501 + def get_changelog_entry_with_http_info(self, id, **kwargs): # noqa: E501 + """get_changelog_entry # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_creator_releases_with_http_info(id, async=True) + >>> thread = api.get_changelog_entry_with_http_info(id, async=True) >>> result = thread.get() :param async bool - :param str id: (required) - :return: list[ReleaseEntity] + :param int id: (required) + :return: ChangelogEntry If the method is called asynchronously, returns the request thread. """ @@ -1886,14 +1890,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_creator_releases" % key + " to method get_changelog_entry" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_creator_releases`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_changelog_entry`") # noqa: E501 collection_formats = {} @@ -1921,14 +1925,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/creator/{id}/releases', 'GET', + '/changelog/{id}', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[ReleaseEntity]', # noqa: E501 + response_type='ChangelogEntry', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -1936,43 +1940,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_editgroup(self, id, **kwargs): # noqa: E501 - """get_editgroup # noqa: E501 + def get_container(self, id, **kwargs): # noqa: E501 + """get_container # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_editgroup(id, async=True) + >>> thread = api.get_container(id, async=True) >>> result = thread.get() :param async bool - :param str id: base32-encoded unique identifier (required) - :return: Editgroup + :param str id: (required) + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: ContainerEntity If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_editgroup_with_http_info(id, **kwargs) # noqa: E501 + return self.get_container_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_editgroup_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_container_with_http_info(id, **kwargs) # noqa: E501 return data - def get_editgroup_with_http_info(self, id, **kwargs): # noqa: E501 - """get_editgroup # noqa: E501 + def get_container_with_http_info(self, id, **kwargs): # noqa: E501 + """get_container # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_editgroup_with_http_info(id, async=True) + >>> thread = api.get_container_with_http_info(id, async=True) >>> result = thread.get() :param async bool - :param str id: base32-encoded unique identifier (required) - :return: Editgroup + :param str id: (required) + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: ContainerEntity If the method is called asynchronously, returns the request thread. """ - all_params = ['id'] # noqa: E501 + all_params = ['id', 'expand'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -1983,23 +1989,15 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_editgroup" % key + " to method get_container" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_editgroup`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_container`") # noqa: E501 - if ('id' in params and - len(params['id']) > 26): - raise ValueError("Invalid value for parameter `id` when calling `get_editgroup`, length must be less than or equal to `26`") # noqa: E501 - if ('id' in params and - len(params['id']) < 26): - raise ValueError("Invalid value for parameter `id` when calling `get_editgroup`, length must be greater than or equal to `26`") # noqa: E501 - if 'id' in params and not re.search('[a-zA-Z2-7]{26}', params['id']): # noqa: E501 - raise ValueError("Invalid value for parameter `id` when calling `get_editgroup`, must conform to the pattern `/[a-zA-Z2-7]{26}/`") # noqa: E501 collection_formats = {} path_params = {} @@ -2007,6 +2005,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] + if 'expand' in params: + query_params.append(('expand', params['expand'])) # noqa: E501 header_params = {} @@ -2026,14 +2026,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/editgroup/{id}', 'GET', + '/container/{id}', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='Editgroup', # noqa: E501 + response_type='ContainerEntity', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2041,43 +2041,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_editor(self, id, **kwargs): # noqa: E501 - """get_editor # noqa: E501 + def get_container_history(self, id, **kwargs): # noqa: E501 + """get_container_history # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_editor(id, async=True) + >>> thread = api.get_container_history(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: Editor + :param int limit: + :return: list[EntityHistoryEntry] If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_editor_with_http_info(id, **kwargs) # noqa: E501 + return self.get_container_history_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_editor_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_container_history_with_http_info(id, **kwargs) # noqa: E501 return data - def get_editor_with_http_info(self, id, **kwargs): # noqa: E501 - """get_editor # noqa: E501 - + def get_container_history_with_http_info(self, id, **kwargs): # noqa: E501 + """get_container_history # noqa: E501 + This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_editor_with_http_info(id, async=True) + >>> thread = api.get_container_history_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: Editor + :param int limit: + :return: list[EntityHistoryEntry] If the method is called asynchronously, returns the request thread. """ - all_params = ['id'] # noqa: E501 + all_params = ['id', 'limit'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2088,14 +2090,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_editor" % key + " to method get_container_history" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_editor`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_container_history`") # noqa: E501 collection_formats = {} @@ -2104,6 +2106,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] + if 'limit' in params: + query_params.append(('limit', params['limit'])) # noqa: E501 header_params = {} @@ -2123,14 +2127,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/editor/{id}', 'GET', + '/container/{id}/history', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='Editor', # noqa: E501 + response_type='list[EntityHistoryEntry]', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2138,43 +2142,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_editor_changelog(self, id, **kwargs): # noqa: E501 - """get_editor_changelog # noqa: E501 + def get_creator(self, id, **kwargs): # noqa: E501 + """get_creator # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_editor_changelog(id, async=True) + >>> thread = api.get_creator(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: list[ChangelogEntry] + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: CreatorEntity If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_editor_changelog_with_http_info(id, **kwargs) # noqa: E501 + return self.get_creator_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_editor_changelog_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_creator_with_http_info(id, **kwargs) # noqa: E501 return data - def get_editor_changelog_with_http_info(self, id, **kwargs): # noqa: E501 - """get_editor_changelog # noqa: E501 + def get_creator_with_http_info(self, id, **kwargs): # noqa: E501 + """get_creator # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_editor_changelog_with_http_info(id, async=True) + >>> thread = api.get_creator_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: list[ChangelogEntry] + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: CreatorEntity If the method is called asynchronously, returns the request thread. """ - all_params = ['id'] # noqa: E501 + all_params = ['id', 'expand'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2185,14 +2191,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_editor_changelog" % key + " to method get_creator" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_editor_changelog`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_creator`") # noqa: E501 collection_formats = {} @@ -2201,6 +2207,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] + if 'expand' in params: + query_params.append(('expand', params['expand'])) # noqa: E501 header_params = {} @@ -2220,14 +2228,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/editor/{id}/changelog', 'GET', + '/creator/{id}', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[ChangelogEntry]', # noqa: E501 + response_type='CreatorEntity', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2235,45 +2243,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_file(self, id, **kwargs): # noqa: E501 - """get_file # noqa: E501 + def get_creator_history(self, id, **kwargs): # noqa: E501 + """get_creator_history # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_file(id, async=True) + >>> thread = api.get_creator_history(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: FileEntity + :param int limit: + :return: list[EntityHistoryEntry] If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_file_with_http_info(id, **kwargs) # noqa: E501 + return self.get_creator_history_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_file_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_creator_history_with_http_info(id, **kwargs) # noqa: E501 return data - def get_file_with_http_info(self, id, **kwargs): # noqa: E501 - """get_file # noqa: E501 + def get_creator_history_with_http_info(self, id, **kwargs): # noqa: E501 + """get_creator_history # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_file_with_http_info(id, async=True) + >>> thread = api.get_creator_history_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: FileEntity + :param int limit: + :return: list[EntityHistoryEntry] If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'expand'] # noqa: E501 + all_params = ['id', 'limit'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2284,14 +2292,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_file" % key + " to method get_creator_history" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_file`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_creator_history`") # noqa: E501 collection_formats = {} @@ -2300,8 +2308,8 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'expand' in params: - query_params.append(('expand', params['expand'])) # noqa: E501 + if 'limit' in params: + query_params.append(('limit', params['limit'])) # noqa: E501 header_params = {} @@ -2321,14 +2329,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/file/{id}', 'GET', + '/creator/{id}/history', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='FileEntity', # noqa: E501 + response_type='list[EntityHistoryEntry]', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2336,45 +2344,43 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_file_history(self, id, **kwargs): # noqa: E501 - """get_file_history # noqa: E501 + def get_creator_releases(self, id, **kwargs): # noqa: E501 + """get_creator_releases # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_file_history(id, async=True) + >>> thread = api.get_creator_releases(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :return: list[ReleaseEntity] If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_file_history_with_http_info(id, **kwargs) # noqa: E501 + return self.get_creator_releases_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_file_history_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_creator_releases_with_http_info(id, **kwargs) # noqa: E501 return data - def get_file_history_with_http_info(self, id, **kwargs): # noqa: E501 - """get_file_history # noqa: E501 + def get_creator_releases_with_http_info(self, id, **kwargs): # noqa: E501 + """get_creator_releases # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_file_history_with_http_info(id, async=True) + >>> thread = api.get_creator_releases_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :return: list[ReleaseEntity] If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'limit'] # noqa: E501 + all_params = ['id'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2385,14 +2391,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_file_history" % key + " to method get_creator_releases" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_file_history`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_creator_releases`") # noqa: E501 collection_formats = {} @@ -2401,8 +2407,6 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'limit' in params: - query_params.append(('limit', params['limit'])) # noqa: E501 header_params = {} @@ -2422,14 +2426,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/file/{id}/history', 'GET', + '/creator/{id}/releases', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[EntityHistoryEntry]', # noqa: E501 + response_type='list[ReleaseEntity]', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2437,45 +2441,43 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_release(self, id, **kwargs): # noqa: E501 - """get_release # noqa: E501 + def get_editgroup(self, id, **kwargs): # noqa: E501 + """get_editgroup # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_release(id, async=True) + >>> thread = api.get_editgroup(id, async=True) >>> result = thread.get() :param async bool - :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: ReleaseEntity + :param str id: base32-encoded unique identifier (required) + :return: Editgroup If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_release_with_http_info(id, **kwargs) # noqa: E501 + return self.get_editgroup_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_release_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_editgroup_with_http_info(id, **kwargs) # noqa: E501 return data - def get_release_with_http_info(self, id, **kwargs): # noqa: E501 - """get_release # noqa: E501 + def get_editgroup_with_http_info(self, id, **kwargs): # noqa: E501 + """get_editgroup # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_release_with_http_info(id, async=True) + >>> thread = api.get_editgroup_with_http_info(id, async=True) >>> result = thread.get() :param async bool - :param str id: (required) - :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: ReleaseEntity + :param str id: base32-encoded unique identifier (required) + :return: Editgroup If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'expand'] # noqa: E501 + all_params = ['id'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2486,15 +2488,23 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_release" % key + " to method get_editgroup" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_release`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_editgroup`") # noqa: E501 + if ('id' in params and + len(params['id']) > 26): + raise ValueError("Invalid value for parameter `id` when calling `get_editgroup`, length must be less than or equal to `26`") # noqa: E501 + if ('id' in params and + len(params['id']) < 26): + raise ValueError("Invalid value for parameter `id` when calling `get_editgroup`, length must be greater than or equal to `26`") # noqa: E501 + if 'id' in params and not re.search('[a-zA-Z2-7]{26}', params['id']): # noqa: E501 + raise ValueError("Invalid value for parameter `id` when calling `get_editgroup`, must conform to the pattern `/[a-zA-Z2-7]{26}/`") # noqa: E501 collection_formats = {} path_params = {} @@ -2502,8 +2512,6 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'expand' in params: - query_params.append(('expand', params['expand'])) # noqa: E501 header_params = {} @@ -2523,14 +2531,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/release/{id}', 'GET', + '/editgroup/{id}', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='ReleaseEntity', # noqa: E501 + response_type='Editgroup', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2538,38 +2546,38 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_release_files(self, id, **kwargs): # noqa: E501 - """get_release_files # noqa: E501 + def get_editor(self, id, **kwargs): # noqa: E501 + """get_editor # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_release_files(id, async=True) + >>> thread = api.get_editor(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: list[FileEntity] + :return: Editor If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_release_files_with_http_info(id, **kwargs) # noqa: E501 + return self.get_editor_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_release_files_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_editor_with_http_info(id, **kwargs) # noqa: E501 return data - def get_release_files_with_http_info(self, id, **kwargs): # noqa: E501 - """get_release_files # noqa: E501 + def get_editor_with_http_info(self, id, **kwargs): # noqa: E501 + """get_editor # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_release_files_with_http_info(id, async=True) + >>> thread = api.get_editor_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: list[FileEntity] + :return: Editor If the method is called asynchronously, returns the request thread. """ @@ -2585,14 +2593,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_release_files" % key + " to method get_editor" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_release_files`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_editor`") # noqa: E501 collection_formats = {} @@ -2620,14 +2628,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/release/{id}/files', 'GET', + '/editor/{id}', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[FileEntity]', # noqa: E501 + response_type='Editor', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2635,45 +2643,43 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_release_history(self, id, **kwargs): # noqa: E501 - """get_release_history # noqa: E501 + def get_editor_changelog(self, id, **kwargs): # noqa: E501 + """get_editor_changelog # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_release_history(id, async=True) + >>> thread = api.get_editor_changelog(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :return: list[ChangelogEntry] If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_release_history_with_http_info(id, **kwargs) # noqa: E501 + return self.get_editor_changelog_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_release_history_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_editor_changelog_with_http_info(id, **kwargs) # noqa: E501 return data - def get_release_history_with_http_info(self, id, **kwargs): # noqa: E501 - """get_release_history # noqa: E501 + def get_editor_changelog_with_http_info(self, id, **kwargs): # noqa: E501 + """get_editor_changelog # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_release_history_with_http_info(id, async=True) + >>> thread = api.get_editor_changelog_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :return: list[ChangelogEntry] If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'limit'] # noqa: E501 + all_params = ['id'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2684,14 +2690,14 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_release_history" % key + " to method get_editor_changelog" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_release_history`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_editor_changelog`") # noqa: E501 collection_formats = {} @@ -2700,8 +2706,6 @@ class DefaultApi(object): path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'limit' in params: - query_params.append(('limit', params['limit'])) # noqa: E501 header_params = {} @@ -2721,14 +2725,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/release/{id}/history', 'GET', + '/editor/{id}/changelog', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[EntityHistoryEntry]', # noqa: E501 + response_type='list[ChangelogEntry]', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2736,43 +2740,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_stats(self, **kwargs): # noqa: E501 - """get_stats # noqa: E501 + def get_file(self, id, **kwargs): # noqa: E501 + """get_file # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_stats(async=True) + >>> thread = api.get_file(id, async=True) >>> result = thread.get() :param async bool - :param str more: - :return: StatsResponse + :param str id: (required) + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: FileEntity If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_stats_with_http_info(**kwargs) # noqa: E501 + return self.get_file_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_stats_with_http_info(**kwargs) # noqa: E501 + (data) = self.get_file_with_http_info(id, **kwargs) # noqa: E501 return data - def get_stats_with_http_info(self, **kwargs): # noqa: E501 - """get_stats # noqa: E501 + def get_file_with_http_info(self, id, **kwargs): # noqa: E501 + """get_file # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_stats_with_http_info(async=True) + >>> thread = api.get_file_with_http_info(id, async=True) >>> result = thread.get() :param async bool - :param str more: - :return: StatsResponse + :param str id: (required) + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: FileEntity If the method is called asynchronously, returns the request thread. """ - all_params = ['more'] # noqa: E501 + all_params = ['id', 'expand'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2783,18 +2789,24 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_stats" % key + " to method get_file" % key ) params[key] = val del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_file`") # noqa: E501 collection_formats = {} path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'more' in params: - query_params.append(('more', params['more'])) # noqa: E501 + if 'expand' in params: + query_params.append(('expand', params['expand'])) # noqa: E501 header_params = {} @@ -2814,14 +2826,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/stats', 'GET', + '/file/{id}', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='StatsResponse', # noqa: E501 + response_type='FileEntity', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2829,40 +2841,141 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_work(self, id, **kwargs): # noqa: E501 - """get_work # noqa: E501 + def get_file_history(self, id, **kwargs): # noqa: E501 + """get_file_history # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_work(id, async=True) + >>> thread = api.get_file_history(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param int limit: + :return: list[EntityHistoryEntry] + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_file_history_with_http_info(id, **kwargs) # noqa: E501 + else: + (data) = self.get_file_history_with_http_info(id, **kwargs) # noqa: E501 + return data + + def get_file_history_with_http_info(self, id, **kwargs): # noqa: E501 + """get_file_history # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_file_history_with_http_info(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param int limit: + :return: list[EntityHistoryEntry] + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['id', 'limit'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_file_history" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_file_history`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + if 'limit' in params: + query_params.append(('limit', params['limit'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/file/{id}/history', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='list[EntityHistoryEntry]', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_release(self, id, **kwargs): # noqa: E501 + """get_release # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_release(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: WorkEntity + :return: ReleaseEntity If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_work_with_http_info(id, **kwargs) # noqa: E501 + return self.get_release_with_http_info(id, **kwargs) # noqa: E501 else: - (data) = self.get_work_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.get_release_with_http_info(id, **kwargs) # noqa: E501 return data - def get_work_with_http_info(self, id, **kwargs): # noqa: E501 - """get_work # noqa: E501 + def get_release_with_http_info(self, id, **kwargs): # noqa: E501 + """get_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_work_with_http_info(id, async=True) + >>> thread = api.get_release_with_http_info(id, async=True) >>> result = thread.get() :param async bool :param str id: (required) :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. - :return: WorkEntity + :return: ReleaseEntity If the method is called asynchronously, returns the request thread. """ @@ -2878,24 +2991,921 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_work" % key + " to method get_release" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_work`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `get_release`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + if 'expand' in params: + query_params.append(('expand', params['expand'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/release/{id}', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='ReleaseEntity', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_release_files(self, id, **kwargs): # noqa: E501 + """get_release_files # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_release_files(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :return: list[FileEntity] + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_release_files_with_http_info(id, **kwargs) # noqa: E501 + else: + (data) = self.get_release_files_with_http_info(id, **kwargs) # noqa: E501 + return data + + def get_release_files_with_http_info(self, id, **kwargs): # noqa: E501 + """get_release_files # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_release_files_with_http_info(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :return: list[FileEntity] + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['id'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_release_files" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_release_files`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/release/{id}/files', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='list[FileEntity]', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_release_history(self, id, **kwargs): # noqa: E501 + """get_release_history # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_release_history(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param int limit: + :return: list[EntityHistoryEntry] + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_release_history_with_http_info(id, **kwargs) # noqa: E501 + else: + (data) = self.get_release_history_with_http_info(id, **kwargs) # noqa: E501 + return data + + def get_release_history_with_http_info(self, id, **kwargs): # noqa: E501 + """get_release_history # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_release_history_with_http_info(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param int limit: + :return: list[EntityHistoryEntry] + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['id', 'limit'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_release_history" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_release_history`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + if 'limit' in params: + query_params.append(('limit', params['limit'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/release/{id}/history', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='list[EntityHistoryEntry]', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_stats(self, **kwargs): # noqa: E501 + """get_stats # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_stats(async=True) + >>> result = thread.get() + + :param async bool + :param str more: + :return: StatsResponse + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_stats_with_http_info(**kwargs) # noqa: E501 + else: + (data) = self.get_stats_with_http_info(**kwargs) # noqa: E501 + return data + + def get_stats_with_http_info(self, **kwargs): # noqa: E501 + """get_stats # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_stats_with_http_info(async=True) + >>> result = thread.get() + + :param async bool + :param str more: + :return: StatsResponse + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['more'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_stats" % key + ) + params[key] = val + del params['kwargs'] + + collection_formats = {} + + path_params = {} + + query_params = [] + if 'more' in params: + query_params.append(('more', params['more'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/stats', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='StatsResponse', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_work(self, id, **kwargs): # noqa: E501 + """get_work # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_work(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: WorkEntity + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_work_with_http_info(id, **kwargs) # noqa: E501 + else: + (data) = self.get_work_with_http_info(id, **kwargs) # noqa: E501 + return data + + def get_work_with_http_info(self, id, **kwargs): # noqa: E501 + """get_work # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_work_with_http_info(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param str expand: List of sub-entities to expand in response. For now, only 'all' accepted. + :return: WorkEntity + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['id', 'expand'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_work" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_work`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + if 'expand' in params: + query_params.append(('expand', params['expand'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/work/{id}', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='WorkEntity', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_work_history(self, id, **kwargs): # noqa: E501 + """get_work_history # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_work_history(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param int limit: + :return: list[EntityHistoryEntry] + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_work_history_with_http_info(id, **kwargs) # noqa: E501 + else: + (data) = self.get_work_history_with_http_info(id, **kwargs) # noqa: E501 + return data + + def get_work_history_with_http_info(self, id, **kwargs): # noqa: E501 + """get_work_history # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_work_history_with_http_info(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :param int limit: + :return: list[EntityHistoryEntry] + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['id', 'limit'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_work_history" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_work_history`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + if 'limit' in params: + query_params.append(('limit', params['limit'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/work/{id}/history', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='list[EntityHistoryEntry]', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def get_work_releases(self, id, **kwargs): # noqa: E501 + """get_work_releases # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_work_releases(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :return: list[ReleaseEntity] + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.get_work_releases_with_http_info(id, **kwargs) # noqa: E501 + else: + (data) = self.get_work_releases_with_http_info(id, **kwargs) # noqa: E501 + return data + + def get_work_releases_with_http_info(self, id, **kwargs): # noqa: E501 + """get_work_releases # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.get_work_releases_with_http_info(id, async=True) + >>> result = thread.get() + + :param async bool + :param str id: (required) + :return: list[ReleaseEntity] + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['id'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method get_work_releases" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `get_work_releases`") # noqa: E501 + + collection_formats = {} + + path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 + + query_params = [] + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/work/{id}/releases', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='list[ReleaseEntity]', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def lookup_container(self, issnl, **kwargs): # noqa: E501 + """lookup_container # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.lookup_container(issnl, async=True) + >>> result = thread.get() + + :param async bool + :param str issnl: (required) + :return: ContainerEntity + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.lookup_container_with_http_info(issnl, **kwargs) # noqa: E501 + else: + (data) = self.lookup_container_with_http_info(issnl, **kwargs) # noqa: E501 + return data + + def lookup_container_with_http_info(self, issnl, **kwargs): # noqa: E501 + """lookup_container # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.lookup_container_with_http_info(issnl, async=True) + >>> result = thread.get() + + :param async bool + :param str issnl: (required) + :return: ContainerEntity + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['issnl'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method lookup_container" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'issnl' is set + if ('issnl' not in params or + params['issnl'] is None): + raise ValueError("Missing the required parameter `issnl` when calling `lookup_container`") # noqa: E501 + + if ('issnl' in params and + len(params['issnl']) > 9): + raise ValueError("Invalid value for parameter `issnl` when calling `lookup_container`, length must be less than or equal to `9`") # noqa: E501 + if ('issnl' in params and + len(params['issnl']) < 9): + raise ValueError("Invalid value for parameter `issnl` when calling `lookup_container`, length must be greater than or equal to `9`") # noqa: E501 + if 'issnl' in params and not re.search('\\d{4}-\\d{3}[0-9X]', params['issnl']): # noqa: E501 + raise ValueError("Invalid value for parameter `issnl` when calling `lookup_container`, must conform to the pattern `/\\d{4}-\\d{3}[0-9X]/`") # noqa: E501 + collection_formats = {} + + path_params = {} + + query_params = [] + if 'issnl' in params: + query_params.append(('issnl', params['issnl'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/container/lookup', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='ContainerEntity', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def lookup_creator(self, orcid, **kwargs): # noqa: E501 + """lookup_creator # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.lookup_creator(orcid, async=True) + >>> result = thread.get() + + :param async bool + :param str orcid: (required) + :return: CreatorEntity + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.lookup_creator_with_http_info(orcid, **kwargs) # noqa: E501 + else: + (data) = self.lookup_creator_with_http_info(orcid, **kwargs) # noqa: E501 + return data + + def lookup_creator_with_http_info(self, orcid, **kwargs): # noqa: E501 + """lookup_creator # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.lookup_creator_with_http_info(orcid, async=True) + >>> result = thread.get() + + :param async bool + :param str orcid: (required) + :return: CreatorEntity + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['orcid'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method lookup_creator" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'orcid' is set + if ('orcid' not in params or + params['orcid'] is None): + raise ValueError("Missing the required parameter `orcid` when calling `lookup_creator`") # noqa: E501 + + if ('orcid' in params and + len(params['orcid']) > 19): + raise ValueError("Invalid value for parameter `orcid` when calling `lookup_creator`, length must be less than or equal to `19`") # noqa: E501 + if ('orcid' in params and + len(params['orcid']) < 19): + raise ValueError("Invalid value for parameter `orcid` when calling `lookup_creator`, length must be greater than or equal to `19`") # noqa: E501 + if 'orcid' in params and not re.search('\\d{4}-\\d{4}-\\d{4}-\\d{3}[\\dX]', params['orcid']): # noqa: E501 + raise ValueError("Invalid value for parameter `orcid` when calling `lookup_creator`, must conform to the pattern `/\\d{4}-\\d{4}-\\d{4}-\\d{3}[\\dX]/`") # noqa: E501 + collection_formats = {} + + path_params = {} + + query_params = [] + if 'orcid' in params: + query_params.append(('orcid', params['orcid'])) # noqa: E501 + + header_params = {} + + form_params = [] + local_var_files = {} + + body_params = None + # HTTP header `Accept` + header_params['Accept'] = self.api_client.select_header_accept( + ['application/json']) # noqa: E501 + + # HTTP header `Content-Type` + header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 + ['application/json']) # noqa: E501 + + # Authentication setting + auth_settings = [] # noqa: E501 + + return self.api_client.call_api( + '/creator/lookup', 'GET', + path_params, + query_params, + header_params, + body=body_params, + post_params=form_params, + files=local_var_files, + response_type='CreatorEntity', # noqa: E501 + auth_settings=auth_settings, + async=params.get('async'), + _return_http_data_only=params.get('_return_http_data_only'), + _preload_content=params.get('_preload_content', True), + _request_timeout=params.get('_request_timeout'), + collection_formats=collection_formats) + + def lookup_file(self, sha1, **kwargs): # noqa: E501 + """lookup_file # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.lookup_file(sha1, async=True) + >>> result = thread.get() + + :param async bool + :param str sha1: (required) + :return: FileEntity + If the method is called asynchronously, + returns the request thread. + """ + kwargs['_return_http_data_only'] = True + if kwargs.get('async'): + return self.lookup_file_with_http_info(sha1, **kwargs) # noqa: E501 + else: + (data) = self.lookup_file_with_http_info(sha1, **kwargs) # noqa: E501 + return data + + def lookup_file_with_http_info(self, sha1, **kwargs): # noqa: E501 + """lookup_file # noqa: E501 + + This method makes a synchronous HTTP request by default. To make an + asynchronous HTTP request, please pass async=True + >>> thread = api.lookup_file_with_http_info(sha1, async=True) + >>> result = thread.get() + + :param async bool + :param str sha1: (required) + :return: FileEntity + If the method is called asynchronously, + returns the request thread. + """ + + all_params = ['sha1'] # noqa: E501 + all_params.append('async') + all_params.append('_return_http_data_only') + all_params.append('_preload_content') + all_params.append('_request_timeout') + + params = locals() + for key, val in six.iteritems(params['kwargs']): + if key not in all_params: + raise TypeError( + "Got an unexpected keyword argument '%s'" + " to method lookup_file" % key + ) + params[key] = val + del params['kwargs'] + # verify the required parameter 'sha1' is set + if ('sha1' not in params or + params['sha1'] is None): + raise ValueError("Missing the required parameter `sha1` when calling `lookup_file`") # noqa: E501 collection_formats = {} path_params = {} - if 'id' in params: - path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'expand' in params: - query_params.append(('expand', params['expand'])) # noqa: E501 + if 'sha1' in params: + query_params.append(('sha1', params['sha1'])) # noqa: E501 header_params = {} @@ -2915,14 +3925,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/work/{id}', 'GET', + '/file/lookup', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='WorkEntity', # noqa: E501 + response_type='FileEntity', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -2930,45 +3940,43 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_work_history(self, id, **kwargs): # noqa: E501 - """get_work_history # noqa: E501 + def lookup_release(self, doi, **kwargs): # noqa: E501 + """lookup_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_work_history(id, async=True) + >>> thread = api.lookup_release(doi, async=True) >>> result = thread.get() :param async bool - :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :param str doi: (required) + :return: ReleaseEntity If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_work_history_with_http_info(id, **kwargs) # noqa: E501 + return self.lookup_release_with_http_info(doi, **kwargs) # noqa: E501 else: - (data) = self.get_work_history_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.lookup_release_with_http_info(doi, **kwargs) # noqa: E501 return data - def get_work_history_with_http_info(self, id, **kwargs): # noqa: E501 - """get_work_history # noqa: E501 + def lookup_release_with_http_info(self, doi, **kwargs): # noqa: E501 + """lookup_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_work_history_with_http_info(id, async=True) + >>> thread = api.lookup_release_with_http_info(doi, async=True) >>> result = thread.get() :param async bool - :param str id: (required) - :param int limit: - :return: list[EntityHistoryEntry] + :param str doi: (required) + :return: ReleaseEntity If the method is called asynchronously, returns the request thread. """ - all_params = ['id', 'limit'] # noqa: E501 + all_params = ['doi'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -2979,24 +3987,22 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_work_history" % key + " to method lookup_release" % key ) params[key] = val del params['kwargs'] - # verify the required parameter 'id' is set - if ('id' not in params or - params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_work_history`") # noqa: E501 + # verify the required parameter 'doi' is set + if ('doi' not in params or + params['doi'] is None): + raise ValueError("Missing the required parameter `doi` when calling `lookup_release`") # noqa: E501 collection_formats = {} path_params = {} - if 'id' in params: - path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'limit' in params: - query_params.append(('limit', params['limit'])) # noqa: E501 + if 'doi' in params: + query_params.append(('doi', params['doi'])) # noqa: E501 header_params = {} @@ -3016,14 +4022,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/work/{id}/history', 'GET', + '/release/lookup', 'GET', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[EntityHistoryEntry]', # noqa: E501 + response_type='ReleaseEntity', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -3031,43 +4037,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def get_work_releases(self, id, **kwargs): # noqa: E501 - """get_work_releases # noqa: E501 + def update_container(self, id, entity, **kwargs): # noqa: E501 + """update_container # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_work_releases(id, async=True) + >>> thread = api.update_container(id, entity, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: list[ReleaseEntity] + :param ContainerEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.get_work_releases_with_http_info(id, **kwargs) # noqa: E501 + return self.update_container_with_http_info(id, entity, **kwargs) # noqa: E501 else: - (data) = self.get_work_releases_with_http_info(id, **kwargs) # noqa: E501 + (data) = self.update_container_with_http_info(id, entity, **kwargs) # noqa: E501 return data - def get_work_releases_with_http_info(self, id, **kwargs): # noqa: E501 - """get_work_releases # noqa: E501 + def update_container_with_http_info(self, id, entity, **kwargs): # noqa: E501 + """update_container # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.get_work_releases_with_http_info(id, async=True) + >>> thread = api.update_container_with_http_info(id, entity, async=True) >>> result = thread.get() :param async bool :param str id: (required) - :return: list[ReleaseEntity] + :param ContainerEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['id'] # noqa: E501 + all_params = ['id', 'entity'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -3078,14 +4086,18 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method get_work_releases" % key + " to method update_container" % key ) params[key] = val del params['kwargs'] # verify the required parameter 'id' is set if ('id' not in params or params['id'] is None): - raise ValueError("Missing the required parameter `id` when calling `get_work_releases`") # noqa: E501 + raise ValueError("Missing the required parameter `id` when calling `update_container`") # noqa: E501 + # verify the required parameter 'entity' is set + if ('entity' not in params or + params['entity'] is None): + raise ValueError("Missing the required parameter `entity` when calling `update_container`") # noqa: E501 collection_formats = {} @@ -3101,6 +4113,8 @@ class DefaultApi(object): local_var_files = {} body_params = None + if 'entity' in params: + body_params = params['entity'] # HTTP header `Accept` header_params['Accept'] = self.api_client.select_header_accept( ['application/json']) # noqa: E501 @@ -3113,14 +4127,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/work/{id}/releases', 'GET', + '/container/{id}', 'PUT', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='list[ReleaseEntity]', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -3128,43 +4142,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def lookup_container(self, issnl, **kwargs): # noqa: E501 - """lookup_container # noqa: E501 + def update_creator(self, id, entity, **kwargs): # noqa: E501 + """update_creator # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_container(issnl, async=True) + >>> thread = api.update_creator(id, entity, async=True) >>> result = thread.get() :param async bool - :param str issnl: (required) - :return: ContainerEntity + :param str id: (required) + :param CreatorEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.lookup_container_with_http_info(issnl, **kwargs) # noqa: E501 + return self.update_creator_with_http_info(id, entity, **kwargs) # noqa: E501 else: - (data) = self.lookup_container_with_http_info(issnl, **kwargs) # noqa: E501 + (data) = self.update_creator_with_http_info(id, entity, **kwargs) # noqa: E501 return data - def lookup_container_with_http_info(self, issnl, **kwargs): # noqa: E501 - """lookup_container # noqa: E501 + def update_creator_with_http_info(self, id, entity, **kwargs): # noqa: E501 + """update_creator # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_container_with_http_info(issnl, async=True) + >>> thread = api.update_creator_with_http_info(id, entity, async=True) >>> result = thread.get() :param async bool - :param str issnl: (required) - :return: ContainerEntity + :param str id: (required) + :param CreatorEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['issnl'] # noqa: E501 + all_params = ['id', 'entity'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -3175,30 +4191,26 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method lookup_container" % key + " to method update_creator" % key ) params[key] = val del params['kwargs'] - # verify the required parameter 'issnl' is set - if ('issnl' not in params or - params['issnl'] is None): - raise ValueError("Missing the required parameter `issnl` when calling `lookup_container`") # noqa: E501 + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `update_creator`") # noqa: E501 + # verify the required parameter 'entity' is set + if ('entity' not in params or + params['entity'] is None): + raise ValueError("Missing the required parameter `entity` when calling `update_creator`") # noqa: E501 - if ('issnl' in params and - len(params['issnl']) > 9): - raise ValueError("Invalid value for parameter `issnl` when calling `lookup_container`, length must be less than or equal to `9`") # noqa: E501 - if ('issnl' in params and - len(params['issnl']) < 9): - raise ValueError("Invalid value for parameter `issnl` when calling `lookup_container`, length must be greater than or equal to `9`") # noqa: E501 - if 'issnl' in params and not re.search('\\d{4}-\\d{3}[0-9X]', params['issnl']): # noqa: E501 - raise ValueError("Invalid value for parameter `issnl` when calling `lookup_container`, must conform to the pattern `/\\d{4}-\\d{3}[0-9X]/`") # noqa: E501 collection_formats = {} path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'issnl' in params: - query_params.append(('issnl', params['issnl'])) # noqa: E501 header_params = {} @@ -3206,6 +4218,8 @@ class DefaultApi(object): local_var_files = {} body_params = None + if 'entity' in params: + body_params = params['entity'] # HTTP header `Accept` header_params['Accept'] = self.api_client.select_header_accept( ['application/json']) # noqa: E501 @@ -3218,14 +4232,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/container/lookup', 'GET', + '/creator/{id}', 'PUT', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='ContainerEntity', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -3233,43 +4247,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def lookup_creator(self, orcid, **kwargs): # noqa: E501 - """lookup_creator # noqa: E501 + def update_file(self, id, entity, **kwargs): # noqa: E501 + """update_file # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_creator(orcid, async=True) + >>> thread = api.update_file(id, entity, async=True) >>> result = thread.get() :param async bool - :param str orcid: (required) - :return: CreatorEntity + :param str id: (required) + :param FileEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.lookup_creator_with_http_info(orcid, **kwargs) # noqa: E501 + return self.update_file_with_http_info(id, entity, **kwargs) # noqa: E501 else: - (data) = self.lookup_creator_with_http_info(orcid, **kwargs) # noqa: E501 + (data) = self.update_file_with_http_info(id, entity, **kwargs) # noqa: E501 return data - def lookup_creator_with_http_info(self, orcid, **kwargs): # noqa: E501 - """lookup_creator # noqa: E501 + def update_file_with_http_info(self, id, entity, **kwargs): # noqa: E501 + """update_file # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_creator_with_http_info(orcid, async=True) + >>> thread = api.update_file_with_http_info(id, entity, async=True) >>> result = thread.get() :param async bool - :param str orcid: (required) - :return: CreatorEntity + :param str id: (required) + :param FileEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['orcid'] # noqa: E501 + all_params = ['id', 'entity'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -3280,30 +4296,26 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method lookup_creator" % key + " to method update_file" % key ) params[key] = val del params['kwargs'] - # verify the required parameter 'orcid' is set - if ('orcid' not in params or - params['orcid'] is None): - raise ValueError("Missing the required parameter `orcid` when calling `lookup_creator`") # noqa: E501 + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `update_file`") # noqa: E501 + # verify the required parameter 'entity' is set + if ('entity' not in params or + params['entity'] is None): + raise ValueError("Missing the required parameter `entity` when calling `update_file`") # noqa: E501 - if ('orcid' in params and - len(params['orcid']) > 19): - raise ValueError("Invalid value for parameter `orcid` when calling `lookup_creator`, length must be less than or equal to `19`") # noqa: E501 - if ('orcid' in params and - len(params['orcid']) < 19): - raise ValueError("Invalid value for parameter `orcid` when calling `lookup_creator`, length must be greater than or equal to `19`") # noqa: E501 - if 'orcid' in params and not re.search('\\d{4}-\\d{4}-\\d{4}-\\d{3}[\\dX]', params['orcid']): # noqa: E501 - raise ValueError("Invalid value for parameter `orcid` when calling `lookup_creator`, must conform to the pattern `/\\d{4}-\\d{4}-\\d{4}-\\d{3}[\\dX]/`") # noqa: E501 collection_formats = {} path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'orcid' in params: - query_params.append(('orcid', params['orcid'])) # noqa: E501 header_params = {} @@ -3311,6 +4323,8 @@ class DefaultApi(object): local_var_files = {} body_params = None + if 'entity' in params: + body_params = params['entity'] # HTTP header `Accept` header_params['Accept'] = self.api_client.select_header_accept( ['application/json']) # noqa: E501 @@ -3323,14 +4337,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/creator/lookup', 'GET', + '/file/{id}', 'PUT', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='CreatorEntity', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -3338,43 +4352,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def lookup_file(self, sha1, **kwargs): # noqa: E501 - """lookup_file # noqa: E501 + def update_release(self, id, entity, **kwargs): # noqa: E501 + """update_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_file(sha1, async=True) + >>> thread = api.update_release(id, entity, async=True) >>> result = thread.get() :param async bool - :param str sha1: (required) - :return: FileEntity + :param str id: (required) + :param ReleaseEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.lookup_file_with_http_info(sha1, **kwargs) # noqa: E501 + return self.update_release_with_http_info(id, entity, **kwargs) # noqa: E501 else: - (data) = self.lookup_file_with_http_info(sha1, **kwargs) # noqa: E501 + (data) = self.update_release_with_http_info(id, entity, **kwargs) # noqa: E501 return data - def lookup_file_with_http_info(self, sha1, **kwargs): # noqa: E501 - """lookup_file # noqa: E501 + def update_release_with_http_info(self, id, entity, **kwargs): # noqa: E501 + """update_release # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_file_with_http_info(sha1, async=True) + >>> thread = api.update_release_with_http_info(id, entity, async=True) >>> result = thread.get() :param async bool - :param str sha1: (required) - :return: FileEntity + :param str id: (required) + :param ReleaseEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['sha1'] # noqa: E501 + all_params = ['id', 'entity'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -3385,22 +4401,26 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method lookup_file" % key + " to method update_release" % key ) params[key] = val del params['kwargs'] - # verify the required parameter 'sha1' is set - if ('sha1' not in params or - params['sha1'] is None): - raise ValueError("Missing the required parameter `sha1` when calling `lookup_file`") # noqa: E501 + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `update_release`") # noqa: E501 + # verify the required parameter 'entity' is set + if ('entity' not in params or + params['entity'] is None): + raise ValueError("Missing the required parameter `entity` when calling `update_release`") # noqa: E501 collection_formats = {} path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'sha1' in params: - query_params.append(('sha1', params['sha1'])) # noqa: E501 header_params = {} @@ -3408,6 +4428,8 @@ class DefaultApi(object): local_var_files = {} body_params = None + if 'entity' in params: + body_params = params['entity'] # HTTP header `Accept` header_params['Accept'] = self.api_client.select_header_accept( ['application/json']) # noqa: E501 @@ -3420,14 +4442,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/file/lookup', 'GET', + '/release/{id}', 'PUT', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='FileEntity', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), @@ -3435,43 +4457,45 @@ class DefaultApi(object): _request_timeout=params.get('_request_timeout'), collection_formats=collection_formats) - def lookup_release(self, doi, **kwargs): # noqa: E501 - """lookup_release # noqa: E501 + def update_work(self, id, entity, **kwargs): # noqa: E501 + """update_work # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_release(doi, async=True) + >>> thread = api.update_work(id, entity, async=True) >>> result = thread.get() :param async bool - :param str doi: (required) - :return: ReleaseEntity + :param str id: (required) + :param WorkEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True if kwargs.get('async'): - return self.lookup_release_with_http_info(doi, **kwargs) # noqa: E501 + return self.update_work_with_http_info(id, entity, **kwargs) # noqa: E501 else: - (data) = self.lookup_release_with_http_info(doi, **kwargs) # noqa: E501 + (data) = self.update_work_with_http_info(id, entity, **kwargs) # noqa: E501 return data - def lookup_release_with_http_info(self, doi, **kwargs): # noqa: E501 - """lookup_release # noqa: E501 + def update_work_with_http_info(self, id, entity, **kwargs): # noqa: E501 + """update_work # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async=True - >>> thread = api.lookup_release_with_http_info(doi, async=True) + >>> thread = api.update_work_with_http_info(id, entity, async=True) >>> result = thread.get() :param async bool - :param str doi: (required) - :return: ReleaseEntity + :param str id: (required) + :param WorkEntity entity: (required) + :return: EntityEdit If the method is called asynchronously, returns the request thread. """ - all_params = ['doi'] # noqa: E501 + all_params = ['id', 'entity'] # noqa: E501 all_params.append('async') all_params.append('_return_http_data_only') all_params.append('_preload_content') @@ -3482,22 +4506,26 @@ class DefaultApi(object): if key not in all_params: raise TypeError( "Got an unexpected keyword argument '%s'" - " to method lookup_release" % key + " to method update_work" % key ) params[key] = val del params['kwargs'] - # verify the required parameter 'doi' is set - if ('doi' not in params or - params['doi'] is None): - raise ValueError("Missing the required parameter `doi` when calling `lookup_release`") # noqa: E501 + # verify the required parameter 'id' is set + if ('id' not in params or + params['id'] is None): + raise ValueError("Missing the required parameter `id` when calling `update_work`") # noqa: E501 + # verify the required parameter 'entity' is set + if ('entity' not in params or + params['entity'] is None): + raise ValueError("Missing the required parameter `entity` when calling `update_work`") # noqa: E501 collection_formats = {} path_params = {} + if 'id' in params: + path_params['id'] = params['id'] # noqa: E501 query_params = [] - if 'doi' in params: - query_params.append(('doi', params['doi'])) # noqa: E501 header_params = {} @@ -3505,6 +4533,8 @@ class DefaultApi(object): local_var_files = {} body_params = None + if 'entity' in params: + body_params = params['entity'] # HTTP header `Accept` header_params['Accept'] = self.api_client.select_header_accept( ['application/json']) # noqa: E501 @@ -3517,14 +4547,14 @@ class DefaultApi(object): auth_settings = [] # noqa: E501 return self.api_client.call_api( - '/release/lookup', 'GET', + '/work/{id}', 'PUT', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, - response_type='ReleaseEntity', # noqa: E501 + response_type='EntityEdit', # noqa: E501 auth_settings=auth_settings, async=params.get('async'), _return_http_data_only=params.get('_return_http_data_only'), diff --git a/python/tests/codegen_tests/test_default_api.py b/python/tests/codegen_tests/test_default_api.py index e3008898..ec36887b 100644 --- a/python/tests/codegen_tests/test_default_api.py +++ b/python/tests/codegen_tests/test_default_api.py @@ -101,6 +101,36 @@ class TestDefaultApi(unittest.TestCase): """ pass + def test_delete_container(self): + """Test case for delete_container + + """ + pass + + def test_delete_creator(self): + """Test case for delete_creator + + """ + pass + + def test_delete_file(self): + """Test case for delete_file + + """ + pass + + def test_delete_release(self): + """Test case for delete_release + + """ + pass + + def test_delete_work(self): + """Test case for delete_work + + """ + pass + def test_get_changelog(self): """Test case for get_changelog @@ -239,6 +269,36 @@ class TestDefaultApi(unittest.TestCase): """ pass + def test_update_container(self): + """Test case for update_container + + """ + pass + + def test_update_creator(self): + """Test case for update_creator + + """ + pass + + def test_update_file(self): + """Test case for update_file + + """ + pass + + def test_update_release(self): + """Test case for update_release + + """ + pass + + def test_update_work(self): + """Test case for update_work + + """ + pass + if __name__ == '__main__': unittest.main() diff --git a/rust/TODO b/rust/TODO index ed38b915..05ad76d8 100644 --- a/rust/TODO +++ b/rust/TODO @@ -1,4 +1,6 @@ +- database_entity_crud -> api_entity_crud +- merge autoaccept branch in with http-verbs branch - fatcat_api -> fatcat_api_schema (or spec? models? types?) - fatcat -> fatcat-api-server - refactor rev creation (from an entity) into it's own function, across the board diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 56fbca19..31b71395 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -39,7 +39,6 @@ macro_rules! entity_batch_handler { if autoaccept { let _clr: ChangelogRow = diesel::insert_into(changelog::table) - // if autoaccept, eg_id is always Some .values((changelog::editgroup_id.eq(edit_context.editgroup_id.to_uuid()),)) .get_result(conn)?; } -- cgit v1.2.3 From 109f82169ef2a141b5d859e23d3ba052cec99fcb Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Sun, 9 Sep 2018 10:05:20 -0700 Subject: parallelize abstracts insertion --- rust/src/database_entity_crud.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'rust/src') diff --git a/rust/src/database_entity_crud.rs b/rust/src/database_entity_crud.rs index 9a47c2de..88c89e84 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/database_entity_crud.rs @@ -40,7 +40,8 @@ pub trait EntityCrud where Self: Sized, { - // TODO: could these be generic structs? Or do they need to be bound to a specific table? + // TODO: could EditRow and IdentRow be generic structs? Or do they need to be bound to a + // specific table? type EditRow; // EntityEditRow type EditNewRow; type IdentRow; // EntityIdentRow @@ -85,7 +86,7 @@ where fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result>; } -// TODO: this could be a separate trait on all entities? +// TODO: this could be a separate trait on all entities macro_rules! generic_parse_editgroup_id{ () => { fn parse_editgroup_id(&self) -> Result> { @@ -832,6 +833,8 @@ impl EntityCrud for ReleaseEntity { let mut release_ref_rows: Vec = vec![]; let mut release_contrib_rows: Vec = vec![]; + let mut abstract_rows: Vec = vec![]; + let mut release_abstract_rows: Vec = vec![]; for (model, rev_id) in models.iter().zip(rev_ids.iter()) { match &model.refs { @@ -883,7 +886,6 @@ impl EntityCrud for ReleaseEntity { } }; - // TODO: this part still isn't parallelized if let Some(abstract_list) = &model.abstracts { // For rows that specify content, we need to insert the abstract if it doesn't exist // already @@ -895,15 +897,8 @@ impl EntityCrud for ReleaseEntity { content: c.content.clone().unwrap(), }) .collect(); - if !new_abstracts.is_empty() { - // Sort of an "upsert"; only inserts new abstract rows if they don't already exist - insert_into(abstracts::table) - .values(&new_abstracts) - .on_conflict(abstracts::sha1) - .do_nothing() - .execute(conn)?; - } - let release_abstract_rows: Vec = abstract_list + abstract_rows.extend(new_abstracts); + let new_release_abstract_rows: Vec = abstract_list .into_iter() .map(|c| { Ok(ReleaseRevAbstractNewRow { @@ -920,9 +915,7 @@ impl EntityCrud for ReleaseEntity { }) }) .collect::>>()?; - insert_into(release_rev_abstract::table) - .values(release_abstract_rows) - .execute(conn)?; + release_abstract_rows.extend(new_release_abstract_rows); } } @@ -938,6 +931,18 @@ impl EntityCrud for ReleaseEntity { .execute(conn)?; } + if !abstract_rows.is_empty() { + // Sort of an "upsert"; only inserts new abstract rows if they don't already exist + insert_into(abstracts::table) + .values(&abstract_rows) + .on_conflict(abstracts::sha1) + .do_nothing() + .execute(conn)?; + insert_into(release_rev_abstract::table) + .values(release_abstract_rows) + .execute(conn)?; + } + Ok(rev_ids) } } -- cgit v1.2.3