diff options
author | Bryan Newbold <bnewbold@robocracy.org> | 2018-09-10 19:37:36 -0700 |
---|---|---|
committer | Bryan Newbold <bnewbold@robocracy.org> | 2018-09-10 19:37:36 -0700 |
commit | dd0dbc45a0c0aad0819203431d374cbd93cce58f (patch) | |
tree | f9a8fb6e8eea82d54f561fb89472d16a4f287505 /rust | |
parent | 1aa49b2c613178f17a2cb6e85a22f183a1c81947 (diff) | |
parent | 60b070103e80a83e062a57cefd0ba0a84fc3a4c0 (diff) | |
download | fatcat-x-attic-cockroach.tar.gz fatcat-x-attic-cockroach.zip |
Merge branch 'master' into cockroachx-attic-cockroachcockroach
Manually resolve conflicts in:
rust/migrations/2018-05-12-001226_init/up.sql
Diffstat (limited to 'rust')
-rw-r--r-- | rust/HACKING.md | 4 | ||||
-rw-r--r-- | rust/TODO | 10 | ||||
-rw-r--r-- | rust/fatcat-api/README.md | 2 | ||||
-rw-r--r-- | rust/fatcat-api/api.yaml | 20 | ||||
-rw-r--r-- | rust/fatcat-api/api/swagger.yaml | 54 | ||||
-rw-r--r-- | rust/fatcat-api/src/client.rs | 26 | ||||
-rw-r--r-- | rust/fatcat-api/src/lib.rs | 28 | ||||
-rw-r--r-- | rust/fatcat-api/src/mimetypes.rs | 20 | ||||
-rw-r--r-- | rust/fatcat-api/src/server.rs | 44 | ||||
-rw-r--r-- | rust/migrations/2018-05-12-001226_init/up.sql | 28 | ||||
-rw-r--r-- | rust/src/api_entity_crud.rs (renamed from rust/src/database_entity_crud.rs) | 92 | ||||
-rw-r--r-- | rust/src/api_helpers.rs | 58 | ||||
-rw-r--r-- | rust/src/api_server.rs | 309 | ||||
-rw-r--r-- | rust/src/api_wrappers.rs | 206 | ||||
-rw-r--r-- | rust/src/lib.rs | 2 |
15 files changed, 355 insertions, 548 deletions
diff --git a/rust/HACKING.md b/rust/HACKING.md index 622a4b5a..0dde5058 100644 --- a/rust/HACKING.md +++ b/rust/HACKING.md @@ -8,11 +8,11 @@ swagger API spec on one side and the SQL schema on the other. - `./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 +- `./src/api_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`. + arguments and return types. mostly calls in to `api_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). @@ -1,16 +1,10 @@ -finish refactor: -- database_entity_crud -> api_entity_crud -x merge autoaccept branch in with http-verbs branch -- direct CRUD calls from api_wrappers (except, maybe, batch?) - => generally, standardize "edit" actions -- FatCatId and edit context between wrappers and handlers -- review editgroup accept code - verbs: - enforce "previous_rev" required in updates +- review editgroup accept code (?) - fatcat_api -> fatcat_api_schema (or spec? models? types?) +- generally, standardize "edit" actions - fatcat -> fatcat-api-server - editgroup param to update => also for creation? for consistency diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md index c971b88c..1b566766 100644 --- a/rust/fatcat-api/README.md +++ b/rust/fatcat-api/README.md @@ -13,7 +13,7 @@ To see how to make this your own, look here: [README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) - API version: 0.1.0 -- Build date: 2018-09-08T04:52:59.479Z +- Build date: 2018-09-11T02:27:08.863Z This autogenerated project defines an API crate `fatcat` which contains: * An `Api` trait defining the API in Rust. diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml index a8919216..2b0615d2 100644 --- a/rust/fatcat-api/api.yaml +++ b/rust/fatcat-api/api.yaml @@ -671,7 +671,7 @@ paths: operationId: "get_creator_releases" responses: 200: - description: Found Entity + description: Found schema: type: array items: @@ -939,7 +939,7 @@ paths: operationId: "get_release_files" responses: 200: - description: Found Entity + description: Found schema: type: array items: @@ -1081,7 +1081,7 @@ paths: operationId: "get_work_releases" responses: 200: - description: Found Entity + description: Found schema: type: array items: @@ -1097,9 +1097,13 @@ paths: operationId: "get_editor" responses: 200: - description: Found Editor + description: Found schema: $ref: "#/definitions/editor" + 400: + description: Bad Request + schema: + $ref: "#/definitions/error_response" 404: description: Not Found schema: @@ -1118,11 +1122,15 @@ paths: operationId: "get_editor_changelog" responses: 200: - description: Found Merged Changes + description: Found schema: type: array items: $ref: "#/definitions/changelog_entry" + 400: + description: Bad Request + schema: + $ref: "#/definitions/error_response" 404: description: Not Found schema: @@ -1163,7 +1171,7 @@ paths: operationId: "get_editgroup" responses: 200: - description: Found Entity + description: Found schema: $ref: "#/definitions/editgroup" 400: diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml index 0b1ca88a..9bc84351 100644 --- a/rust/fatcat-api/api/swagger.yaml +++ b/rust/fatcat-api/api/swagger.yaml @@ -834,13 +834,13 @@ paths: example: "\"id_example\".to_string()" responses: 200: - description: "Found Entity" + description: "Found" schema: type: "array" items: $ref: "#/definitions/release_entity" - x-responseId: "FoundEntity" - x-uppercaseResponseId: "FOUND_ENTITY" + x-responseId: "Found" + x-uppercaseResponseId: "FOUND" uppercase_operation_id: "GET_CREATOR_RELEASES" uppercase_data_type: "VEC<RELEASEENTITY>" producesJson: true @@ -1749,13 +1749,13 @@ paths: example: "\"id_example\".to_string()" responses: 200: - description: "Found Entity" + description: "Found" schema: type: "array" items: $ref: "#/definitions/file_entity" - x-responseId: "FoundEntity" - x-uppercaseResponseId: "FOUND_ENTITY" + x-responseId: "Found" + x-uppercaseResponseId: "FOUND" uppercase_operation_id: "GET_RELEASE_FILES" uppercase_data_type: "VEC<FILEENTITY>" producesJson: true @@ -2232,13 +2232,13 @@ paths: example: "\"id_example\".to_string()" responses: 200: - description: "Found Entity" + description: "Found" schema: type: "array" items: $ref: "#/definitions/release_entity" - x-responseId: "FoundEntity" - x-uppercaseResponseId: "FOUND_ENTITY" + x-responseId: "Found" + x-uppercaseResponseId: "FOUND" uppercase_operation_id: "GET_WORK_RELEASES" uppercase_data_type: "VEC<RELEASEENTITY>" producesJson: true @@ -2286,14 +2286,23 @@ paths: example: "\"id_example\".to_string()" responses: 200: - description: "Found Editor" + description: "Found" schema: $ref: "#/definitions/editor" - x-responseId: "FoundEditor" - x-uppercaseResponseId: "FOUND_EDITOR" + x-responseId: "Found" + x-uppercaseResponseId: "FOUND" uppercase_operation_id: "GET_EDITOR" uppercase_data_type: "EDITOR" producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "GET_EDITOR" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true 404: description: "Not Found" schema: @@ -2329,16 +2338,25 @@ paths: example: "\"id_example\".to_string()" responses: 200: - description: "Found Merged Changes" + description: "Found" schema: type: "array" items: $ref: "#/definitions/changelog_entry" - x-responseId: "FoundMergedChanges" - x-uppercaseResponseId: "FOUND_MERGED_CHANGES" + x-responseId: "Found" + x-uppercaseResponseId: "FOUND" uppercase_operation_id: "GET_EDITOR_CHANGELOG" uppercase_data_type: "VEC<CHANGELOGENTRY>" producesJson: true + 400: + description: "Bad Request" + schema: + $ref: "#/definitions/error_response" + x-responseId: "BadRequest" + x-uppercaseResponseId: "BAD_REQUEST" + uppercase_operation_id: "GET_EDITOR_CHANGELOG" + uppercase_data_type: "ERRORRESPONSE" + producesJson: true 404: description: "Not Found" schema: @@ -2428,11 +2446,11 @@ paths: example: "\"id_example\".to_string()" responses: 200: - description: "Found Entity" + description: "Found" schema: $ref: "#/definitions/editgroup" - x-responseId: "FoundEntity" - x-uppercaseResponseId: "FOUND_ENTITY" + x-responseId: "Found" + x-uppercaseResponseId: "FOUND" uppercase_operation_id: "GET_EDITGROUP" uppercase_data_type: "EDITGROUP" producesJson: true diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs index 6f61f773..a08e3cfe 100644 --- a/rust/fatcat-api/src/client.rs +++ b/rust/fatcat-api/src/client.rs @@ -1749,7 +1749,7 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::<Vec<models::ReleaseEntity>>(&buf)?; - Ok(GetCreatorReleasesResponse::FoundEntity(body)) + Ok(GetCreatorReleasesResponse::Found(body)) } 400 => { let mut buf = String::new(); @@ -1809,7 +1809,7 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::<models::Editgroup>(&buf)?; - Ok(GetEditgroupResponse::FoundEntity(body)) + Ok(GetEditgroupResponse::Found(body)) } 400 => { let mut buf = String::new(); @@ -1869,7 +1869,14 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::<models::Editor>(&buf)?; - Ok(GetEditorResponse::FoundEditor(body)) + Ok(GetEditorResponse::Found(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(GetEditorResponse::BadRequest(body)) } 404 => { let mut buf = String::new(); @@ -1922,7 +1929,14 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::<Vec<models::ChangelogEntry>>(&buf)?; - Ok(GetEditorChangelogResponse::FoundMergedChanges(body)) + Ok(GetEditorChangelogResponse::Found(body)) + } + 400 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::<models::ErrorResponse>(&buf)?; + + Ok(GetEditorChangelogResponse::BadRequest(body)) } 404 => { let mut buf = String::new(); @@ -2179,7 +2193,7 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::<Vec<models::FileEntity>>(&buf)?; - Ok(GetReleaseFilesResponse::FoundEntity(body)) + Ok(GetReleaseFilesResponse::Found(body)) } 400 => { let mut buf = String::new(); @@ -2492,7 +2506,7 @@ impl Api for Client { response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; let body = serde_json::from_str::<Vec<models::ReleaseEntity>>(&buf)?; - Ok(GetWorkReleasesResponse::FoundEntity(body)) + Ok(GetWorkReleasesResponse::Found(body)) } 400 => { let mut buf = String::new(); diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs index fc1ae2a1..a08c6e04 100644 --- a/rust/fatcat-api/src/lib.rs +++ b/rust/fatcat-api/src/lib.rs @@ -304,8 +304,8 @@ pub enum GetCreatorHistoryResponse { #[derive(Debug, PartialEq)] pub enum GetCreatorReleasesResponse { - /// Found Entity - FoundEntity(Vec<models::ReleaseEntity>), + /// Found + Found(Vec<models::ReleaseEntity>), /// Bad Request BadRequest(models::ErrorResponse), /// Not Found @@ -316,8 +316,8 @@ pub enum GetCreatorReleasesResponse { #[derive(Debug, PartialEq)] pub enum GetEditgroupResponse { - /// Found Entity - FoundEntity(models::Editgroup), + /// Found + Found(models::Editgroup), /// Bad Request BadRequest(models::ErrorResponse), /// Not Found @@ -328,8 +328,10 @@ pub enum GetEditgroupResponse { #[derive(Debug, PartialEq)] pub enum GetEditorResponse { - /// Found Editor - FoundEditor(models::Editor), + /// Found + Found(models::Editor), + /// Bad Request + BadRequest(models::ErrorResponse), /// Not Found NotFound(models::ErrorResponse), /// Generic Error @@ -338,8 +340,10 @@ pub enum GetEditorResponse { #[derive(Debug, PartialEq)] pub enum GetEditorChangelogResponse { - /// Found Merged Changes - FoundMergedChanges(Vec<models::ChangelogEntry>), + /// Found + Found(Vec<models::ChangelogEntry>), + /// Bad Request + BadRequest(models::ErrorResponse), /// Not Found NotFound(models::ErrorResponse), /// Generic Error @@ -384,8 +388,8 @@ pub enum GetReleaseResponse { #[derive(Debug, PartialEq)] pub enum GetReleaseFilesResponse { - /// Found Entity - FoundEntity(Vec<models::FileEntity>), + /// Found + Found(Vec<models::FileEntity>), /// Bad Request BadRequest(models::ErrorResponse), /// Not Found @@ -440,8 +444,8 @@ pub enum GetWorkHistoryResponse { #[derive(Debug, PartialEq)] pub enum GetWorkReleasesResponse { - /// Found Entity - FoundEntity(Vec<models::ReleaseEntity>), + /// Found + Found(Vec<models::ReleaseEntity>), /// Bad Request BadRequest(models::ErrorResponse), /// Not Found diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs index 2c54a313..ff2c12ce 100644 --- a/rust/fatcat-api/src/mimetypes.rs +++ b/rust/fatcat-api/src/mimetypes.rs @@ -362,7 +362,7 @@ pub mod responses { } /// Create Mime objects for the response content types for GetCreatorReleases lazy_static! { - pub static ref GET_CREATOR_RELEASES_FOUND_ENTITY: Mime = mime!(Application / Json); + pub static ref GET_CREATOR_RELEASES_FOUND: Mime = mime!(Application / Json); } /// Create Mime objects for the response content types for GetCreatorReleases lazy_static! { @@ -378,7 +378,7 @@ pub mod responses { } /// Create Mime objects for the response content types for GetEditgroup lazy_static! { - pub static ref GET_EDITGROUP_FOUND_ENTITY: Mime = mime!(Application / Json); + pub static ref GET_EDITGROUP_FOUND: Mime = mime!(Application / Json); } /// Create Mime objects for the response content types for GetEditgroup lazy_static! { @@ -394,7 +394,11 @@ pub mod responses { } /// Create Mime objects for the response content types for GetEditor lazy_static! { - pub static ref GET_EDITOR_FOUND_EDITOR: Mime = mime!(Application / Json); + pub static ref GET_EDITOR_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for GetEditor + lazy_static! { + pub static ref GET_EDITOR_BAD_REQUEST: Mime = mime!(Application / Json); } /// Create Mime objects for the response content types for GetEditor lazy_static! { @@ -406,7 +410,11 @@ pub mod responses { } /// Create Mime objects for the response content types for GetEditorChangelog lazy_static! { - pub static ref GET_EDITOR_CHANGELOG_FOUND_MERGED_CHANGES: Mime = mime!(Application / Json); + pub static ref GET_EDITOR_CHANGELOG_FOUND: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for GetEditorChangelog + lazy_static! { + pub static ref GET_EDITOR_CHANGELOG_BAD_REQUEST: Mime = mime!(Application / Json); } /// Create Mime objects for the response content types for GetEditorChangelog lazy_static! { @@ -466,7 +474,7 @@ pub mod responses { } /// Create Mime objects for the response content types for GetReleaseFiles lazy_static! { - pub static ref GET_RELEASE_FILES_FOUND_ENTITY: Mime = mime!(Application / Json); + pub static ref GET_RELEASE_FILES_FOUND: Mime = mime!(Application / Json); } /// Create Mime objects for the response content types for GetReleaseFiles lazy_static! { @@ -538,7 +546,7 @@ pub mod responses { } /// Create Mime objects for the response content types for GetWorkReleases lazy_static! { - pub static ref GET_WORK_RELEASES_FOUND_ENTITY: Mime = mime!(Application / Json); + pub static ref GET_WORK_RELEASES_FOUND: Mime = mime!(Application / Json); } /// Create Mime objects for the response content types for GetWorkReleases lazy_static! { diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs index 04d10e14..dfc94a81 100644 --- a/rust/fatcat-api/src/server.rs +++ b/rust/fatcat-api/src/server.rs @@ -2361,11 +2361,11 @@ where match api.get_creator_releases(param_id, context).wait() { Ok(rsp) => match rsp { - GetCreatorReleasesResponse::FoundEntity(body) => { + GetCreatorReleasesResponse::Found(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); let mut response = Response::with((status::Status::from_u16(200), body_string)); - response.headers.set(ContentType(mimetypes::responses::GET_CREATOR_RELEASES_FOUND_ENTITY.clone())); + response.headers.set(ContentType(mimetypes::responses::GET_CREATOR_RELEASES_FOUND.clone())); context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); @@ -2449,11 +2449,11 @@ where match api.get_editgroup(param_id, context).wait() { Ok(rsp) => match rsp { - GetEditgroupResponse::FoundEntity(body) => { + GetEditgroupResponse::Found(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); let mut response = Response::with((status::Status::from_u16(200), body_string)); - response.headers.set(ContentType(mimetypes::responses::GET_EDITGROUP_FOUND_ENTITY.clone())); + response.headers.set(ContentType(mimetypes::responses::GET_EDITGROUP_FOUND.clone())); context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); @@ -2537,11 +2537,21 @@ where match api.get_editor(param_id, context).wait() { Ok(rsp) => match rsp { - GetEditorResponse::FoundEditor(body) => { + GetEditorResponse::Found(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); let mut response = Response::with((status::Status::from_u16(200), body_string)); - response.headers.set(ContentType(mimetypes::responses::GET_EDITOR_FOUND_EDITOR.clone())); + response.headers.set(ContentType(mimetypes::responses::GET_EDITOR_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + + Ok(response) + } + GetEditorResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::GET_EDITOR_BAD_REQUEST.clone())); context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); @@ -2615,11 +2625,21 @@ where match api.get_editor_changelog(param_id, context).wait() { Ok(rsp) => match rsp { - GetEditorChangelogResponse::FoundMergedChanges(body) => { + GetEditorChangelogResponse::Found(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); let mut response = Response::with((status::Status::from_u16(200), body_string)); - response.headers.set(ContentType(mimetypes::responses::GET_EDITOR_CHANGELOG_FOUND_MERGED_CHANGES.clone())); + response.headers.set(ContentType(mimetypes::responses::GET_EDITOR_CHANGELOG_FOUND.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + + Ok(response) + } + GetEditorChangelogResponse::BadRequest(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(400), body_string)); + response.headers.set(ContentType(mimetypes::responses::GET_EDITOR_CHANGELOG_BAD_REQUEST.clone())); context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); @@ -2969,11 +2989,11 @@ where match api.get_release_files(param_id, context).wait() { Ok(rsp) => match rsp { - GetReleaseFilesResponse::FoundEntity(body) => { + GetReleaseFilesResponse::Found(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); let mut response = Response::with((status::Status::from_u16(200), body_string)); - response.headers.set(ContentType(mimetypes::responses::GET_RELEASE_FILES_FOUND_ENTITY.clone())); + response.headers.set(ContentType(mimetypes::responses::GET_RELEASE_FILES_FOUND.clone())); context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); @@ -3391,11 +3411,11 @@ where match api.get_work_releases(param_id, context).wait() { Ok(rsp) => match rsp { - GetWorkReleasesResponse::FoundEntity(body) => { + GetWorkReleasesResponse::Found(body) => { let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); let mut response = Response::with((status::Status::from_u16(200), body_string)); - response.headers.set(ContentType(mimetypes::responses::GET_WORK_RELEASES_FOUND_ENTITY.clone())); + response.headers.set(ContentType(mimetypes::responses::GET_WORK_RELEASES_FOUND.clone())); context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); diff --git a/rust/migrations/2018-05-12-001226_init/up.sql b/rust/migrations/2018-05-12-001226_init/up.sql index 3ca954ca..bb62ba75 100644 --- a/rust/migrations/2018-05-12-001226_init/up.sql +++ b/rust/migrations/2018-05-12-001226_init/up.sql @@ -63,8 +63,8 @@ CREATE TABLE creator_rev ( -- Could denormalize a "is_live" flag into revision tables, to make indices -- more efficient -CREATE INDEX creator_rev_orcid_idx ON creator_rev(orcid); -- WHERE orcid IS NOT NULL; -CREATE INDEX creator_rev_wikidata_idx ON creator_rev(wikidata_qid); -- WHERE wikidata_qid IS NOT NULL; +CREATE INDEX creator_rev_orcid_idx ON creator_rev(orcid); +CREATE INDEX creator_rev_wikidata_idx ON creator_rev(wikidata_qid); CREATE TABLE creator_ident ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), @@ -101,8 +101,8 @@ CREATE TABLE container_rev ( coden TEXT ); -CREATE INDEX container_rev_issnl_idx ON container_rev(issnl); -- WHERE issnl IS NOT NULL; -CREATE INDEX container_rev_wikidata_idx ON container_rev(wikidata_qid); -- WHERE wikidata_qid IS NOT NULL; +CREATE INDEX container_rev_issnl_idx ON container_rev(issnl); +CREATE INDEX container_rev_wikidata_idx ON container_rev(wikidata_qid); CREATE TABLE container_ident ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), @@ -138,9 +138,9 @@ CREATE TABLE file_rev ( mimetype TEXT ); -CREATE INDEX file_rev_sha1_idx ON file_rev(sha1); -- WHERE sha1 IS NOT NULL; -CREATE INDEX file_rev_md5_idx ON file_rev(md5); -- WHERE md5 IS NOT NULL; -CREATE INDEX file_rev_sha256_idx ON file_rev(sha256); -- WHERE sha256 IS NOT NULL; +CREATE INDEX file_rev_sha1_idx ON file_rev(sha1); +CREATE INDEX file_rev_md5_idx ON file_rev(md5); +CREATE INDEX file_rev_sha256_idx ON file_rev(sha256); CREATE TABLE file_rev_url ( id BIGSERIAL PRIMARY KEY, @@ -199,13 +199,13 @@ CREATE TABLE release_rev ( -- TODO: identifier table? ); -CREATE INDEX release_rev_doi_idx ON release_rev(doi); -- WHERE doi IS NOT NULL; -CREATE INDEX release_rev_pmid_idx ON release_rev(pmid); -- WHERE pmid IS NOT NULL; -CREATE INDEX release_rev_pmcid_idx ON release_rev(pmcid); -- WHERE pmcid IS NOT NULL; -CREATE INDEX release_rev_wikidata_idx ON release_rev(wikidata_qid); -- WHERE wikidata_qid IS NOT NULL; -CREATE INDEX release_rev_isbn13_idx ON release_rev(isbn13); -- WHERE isbn13 IS NOT NULL; -CREATE INDEX release_rev_core_idx ON release_rev(core_id); -- WHERE core_id IS NOT NULL; -CREATE INDEX release_rev_work_idx ON release_rev(work_ident_id); -- WHERE work_ident_id IS NOT NULL; +CREATE INDEX release_rev_doi_idx ON release_rev(doi); +CREATE INDEX release_rev_pmid_idx ON release_rev(pmid); +CREATE INDEX release_rev_pmcid_idx ON release_rev(pmcid); +CREATE INDEX release_rev_wikidata_idx ON release_rev(wikidata_qid); +CREATE INDEX release_rev_isbn13_idx ON release_rev(isbn13); +CREATE INDEX release_rev_core_idx ON release_rev(core_id); +CREATE INDEX release_rev_work_idx ON release_rev(work_ident_id); CREATE TABLE release_rev_abstract ( id BIGSERIAL PRIMARY KEY, diff --git a/rust/src/database_entity_crud.rs b/rust/src/api_entity_crud.rs index 8b29ff28..2d5ea93d 100644 --- a/rust/src/database_entity_crud.rs +++ b/rust/src/api_entity_crud.rs @@ -6,19 +6,11 @@ use diesel::prelude::*; use diesel::{self, insert_into}; use errors::*; use fatcat_api::models::*; -use serde_json; use sha1::Sha1; use std::marker::Sized; use std::str::FromStr; use uuid::Uuid; -pub struct EditContext { - pub editor_id: FatCatId, - pub editgroup_id: FatCatId, - pub extra_json: Option<serde_json::Value>, - pub autoaccept: bool, -} - /* One goal here is to abstract the non-entity-specific bits into generic traits or functions, * instead of macros. * @@ -51,19 +43,9 @@ where fn parse_editgroup_id(&self) -> Result<Option<FatCatId>>; // Generic Methods - fn db_get( - conn: &DbConn, - ident: FatCatId - ) -> Result<Self>; - fn db_get_rev( - conn: &DbConn, - rev_id: Uuid - ) -> Result<Self>; - fn db_create( - &self, - conn: &DbConn, - edit_context: &EditContext - ) -> Result<Self::EditRow>; + fn db_get(conn: &DbConn, ident: FatCatId) -> Result<Self>; + fn db_get_rev(conn: &DbConn, rev_id: Uuid) -> Result<Self>; + fn db_create(&self, conn: &DbConn, edit_context: &EditContext) -> Result<Self::EditRow>; fn db_create_batch( conn: &DbConn, edit_context: &EditContext, @@ -85,10 +67,7 @@ where ident: FatCatId, limit: Option<i64>, ) -> Result<Vec<EntityHistoryEntry>>; - fn db_accept_edits( - conn: &DbConn, - editgroup_id: FatCatId - ) -> Result<u64>; + fn db_accept_edits(conn: &DbConn, editgroup_id: FatCatId) -> Result<u64>; // Entity-specific Methods fn db_from_row( @@ -96,14 +75,8 @@ where rev_row: Self::RevRow, ident_row: Option<Self::IdentRow>, ) -> Result<Self>; - fn db_insert_rev( - &self, - conn: &DbConn - ) -> Result<Uuid>; - fn db_insert_revs( - conn: &DbConn, - models: &[&Self] - ) -> Result<Vec<Uuid>>; + fn db_insert_rev(&self, conn: &DbConn) -> Result<Uuid>; + fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result<Vec<Uuid>>; } // TODO: this could be a separate trait on all entities @@ -333,13 +306,10 @@ macro_rules! generic_db_get_history { // UPDATE FROM version: single query for many rows // Works with Postgres, not Cockroach +#[allow(unused_macros)] macro_rules! generic_db_accept_edits_batch { ($entity_name_str:expr) => { - fn db_accept_edits( - conn: &DbConn, - editgroup_id: FatCatId, - ) -> Result<u64> { - + fn db_accept_edits(conn: &DbConn, editgroup_id: FatCatId) -> Result<u64> { let count = diesel::sql_query(format!( " UPDATE {entity}_ident @@ -351,23 +321,20 @@ macro_rules! generic_db_accept_edits_batch { WHERE {entity}_ident.id = {entity}_edit.ident_id AND {entity}_edit.editgroup_id = $1", - entity = $entity_name_str + entity = $entity_name_str )).bind::<diesel::sql_types::Uuid, _>(editgroup_id.to_uuid()) .execute(conn)?; Ok(count as u64) } - } + }; } // UPDATE ROW version: single query per row // CockroachDB version (slow, single query per row) +#[allow(unused_macros)] macro_rules! generic_db_accept_edits_each { ($ident_table:ident, $edit_table:ident) => { - fn db_accept_edits( - conn: &DbConn, - editgroup_id: FatCatId, - ) -> Result<u64> { - + fn db_accept_edits(conn: &DbConn, editgroup_id: FatCatId) -> Result<u64> { // 1. select edit rows (in sql) let edit_rows: Vec<Self::EditRow> = $edit_table::table .filter($edit_table::editgroup_id.eq(&editgroup_id.to_uuid())) @@ -375,31 +342,26 @@ macro_rules! generic_db_accept_edits_each { // 2. create ident rows (in rust) let ident_rows: Vec<Self::IdentRow> = edit_rows .iter() - .map(|edit| - Self::IdentRow { - id: edit.ident_id, - is_live: true, - rev_id: edit.rev_id, - redirect_id: edit.redirect_id, - - } - ) + .map(|edit| Self::IdentRow { + id: edit.ident_id, + is_live: true, + rev_id: edit.rev_id, + redirect_id: edit.redirect_id, + }) .collect(); /* - // 3. upsert ident rows (in sql) - let count: u64 = diesel::insert_into($ident_table::table) - .values(ident_rows) - .on_conflict() - .do_update() - .set(ident_rows) - .execute(conn)?; - */ + // 3. upsert ident rows (in sql) + let count: u64 = diesel::insert_into($ident_table::table) + .values(ident_rows) + .on_conflict() + .do_update() + .set(ident_rows) + .execute(conn)?; + */ // 3. update every row individually let count = ident_rows.len() as u64; for row in ident_rows { - diesel::update(&row) - .set(&row) - .execute(conn)?; + diesel::update(&row).set(&row).execute(conn)?; } Ok(count) } diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 8ab9dcb3..6c214223 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -1,18 +1,51 @@ +use api_entity_crud::EntityCrud; use data_encoding::BASE32_NOPAD; use database_models::*; use database_schema::*; -use fatcat_api::models::*; use diesel; use diesel::prelude::*; use errors::*; +use fatcat_api::models::*; use regex::Regex; +use serde_json; use std::str::FromStr; use uuid::Uuid; -use database_entity_crud::EntityCrud; pub type DbConn = diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>; +pub struct EditContext { + pub editor_id: FatCatId, + pub editgroup_id: FatCatId, + pub extra_json: Option<serde_json::Value>, + pub autoaccept: bool, +} + +pub fn make_edit_context( + conn: &DbConn, + editgroup_id: Option<FatCatId>, + autoaccept: bool, +) -> Result<EditContext> { + let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth + 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, + autoaccept: autoaccept, + }) +} + /// This function should always be run within a transaction pub fn get_or_create_editgroup(editor_id: Uuid, conn: &DbConn) -> Result<Uuid> { // check for current active @@ -32,34 +65,33 @@ pub fn get_or_create_editgroup(editor_id: Uuid, conn: &DbConn) -> Result<Uuid> { } /// This function should always be run within a transaction -pub fn accept_editgroup(editgroup_id: Uuid, conn: &DbConn) -> Result<ChangelogRow> { +pub fn accept_editgroup(editgroup_id: FatCatId, conn: &DbConn) -> Result<ChangelogRow> { // check that we haven't accepted already (in changelog) // NB: could leave this to a UNIQUE constraint let count: i64 = changelog::table - .filter(changelog::editgroup_id.eq(editgroup_id)) + .filter(changelog::editgroup_id.eq(editgroup_id.to_uuid())) .count() .get_result(conn)?; if count > 0 { - return Err(ErrorKind::EditgroupAlreadyAccepted(uuid2fcid(&editgroup_id)).into()); + return Err(ErrorKind::EditgroupAlreadyAccepted(editgroup_id.to_string()).into()); } // copy edit columns to ident table - let eg_id = FatCatId::from_uuid(&editgroup_id); - ContainerEntity::db_accept_edits(conn, eg_id)?; - CreatorEntity::db_accept_edits(conn, eg_id)?; - FileEntity::db_accept_edits(conn, eg_id)?; - ReleaseEntity::db_accept_edits(conn, eg_id)?; - WorkEntity::db_accept_edits(conn, eg_id)?; + ContainerEntity::db_accept_edits(conn, editgroup_id)?; + CreatorEntity::db_accept_edits(conn, editgroup_id)?; + FileEntity::db_accept_edits(conn, editgroup_id)?; + ReleaseEntity::db_accept_edits(conn, editgroup_id)?; + WorkEntity::db_accept_edits(conn, editgroup_id)?; // append log/changelog row let entry: ChangelogRow = diesel::insert_into(changelog::table) - .values((changelog::editgroup_id.eq(editgroup_id),)) + .values((changelog::editgroup_id.eq(editgroup_id.to_uuid()),)) .get_result(conn)?; // update any editor's active editgroup let no_active: Option<Uuid> = None; diesel::update(editor::table) - .filter(editor::active_editgroup_id.eq(editgroup_id)) + .filter(editor::active_editgroup_id.eq(editgroup_id.to_uuid())) .set(editor::active_editgroup_id.eq(no_active)) .execute(conn)?; Ok(entry) diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 5fce8e64..076bc085 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -1,8 +1,8 @@ //! API endpoint handlers +use api_entity_crud::EntityCrud; use api_helpers::*; use chrono; -use database_entity_crud::{EditContext, EntityCrud}; use database_models::*; use database_schema::*; use diesel::prelude::*; @@ -11,7 +11,6 @@ use errors::*; use fatcat_api::models; use fatcat_api::models::*; use std::str::FromStr; -use uuid::Uuid; use ConnectionPool; macro_rules! entity_batch_handler { @@ -53,27 +52,6 @@ macro_rules! count_entity { }}; } -fn make_edit_context(conn: &DbConn, editgroup_id: Option<FatCatId>, autoaccept: bool) -> Result<EditContext> { - let editor_id = Uuid::parse_str("00000000-0000-0000-AAAA-000000000001")?; // TODO: auth - 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, - autoaccept: autoaccept, - }) -} - #[derive(Clone)] pub struct Server { pub db_pool: ConnectionPool, @@ -82,11 +60,11 @@ pub struct Server { impl Server { pub fn get_container_handler( &self, - id: &Uuid, + id: FatCatId, _expand: Option<String>, conn: &DbConn, ) -> Result<ContainerEntity> { - ContainerEntity::db_get(conn, FatCatId::from_uuid(id)) + ContainerEntity::db_get(conn, id) } pub fn lookup_container_handler(&self, issnl: &str, conn: &DbConn) -> Result<ContainerEntity> { @@ -106,11 +84,11 @@ impl Server { pub fn get_creator_handler( &self, - id: &Uuid, + id: FatCatId, _expand: Option<String>, conn: &DbConn, ) -> Result<CreatorEntity> { - CreatorEntity::db_get(conn, FatCatId::from_uuid(id)) + CreatorEntity::db_get(conn, id) } pub fn lookup_creator_handler(&self, orcid: &str, conn: &DbConn) -> Result<CreatorEntity> { @@ -130,16 +108,14 @@ impl Server { pub fn get_creator_releases_handler( &self, - id: &str, + id: FatCatId, conn: &DbConn, ) -> Result<Vec<ReleaseEntity>> { - let id = fcid2uuid(&id)?; - // TODO: some kind of unique or group-by? let rows: Vec<(ReleaseRevRow, ReleaseIdentRow, ReleaseContribRow)> = release_rev::table .inner_join(release_ident::table) .inner_join(release_contrib::table) - .filter(release_contrib::creator_ident_id.eq(&id)) + .filter(release_contrib::creator_ident_id.eq(&id.to_uuid())) .filter(release_ident::is_live.eq(true)) .filter(release_ident::redirect_id.is_null()) .load(conn)?; @@ -152,11 +128,11 @@ impl Server { pub fn get_file_handler( &self, - id: &Uuid, + id: FatCatId, _expand: Option<String>, conn: &DbConn, ) -> Result<FileEntity> { - FileEntity::db_get(conn, FatCatId::from_uuid(id)) + FileEntity::db_get(conn, id) } pub fn lookup_file_handler(&self, sha1: &str, conn: &DbConn) -> Result<FileEntity> { @@ -175,19 +151,18 @@ impl Server { pub fn get_release_handler( &self, - id: &Uuid, + id: FatCatId, expand: Option<String>, conn: &DbConn, ) -> Result<ReleaseEntity> { - let mut release = ReleaseEntity::db_get(conn, FatCatId::from_uuid(id))?; + let mut release = ReleaseEntity::db_get(conn, id)?; // For now, if there is any expand param we do them all if expand.is_some() { - release.files = - Some(self.get_release_files_handler(&release.ident.clone().unwrap(), conn)?); + release.files = Some(self.get_release_files_handler(id, conn)?); if let Some(ref cid) = release.container_id { release.container = - Some(self.get_container_handler(&fcid2uuid(&cid)?, None, conn)?); + Some(self.get_container_handler(FatCatId::from_str(&cid)?, None, conn)?); } } Ok(release) @@ -208,13 +183,15 @@ impl Server { ReleaseEntity::db_from_row(conn, rev, Some(ident)) } - pub fn get_release_files_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<FileEntity>> { - let ident = FatCatId::from_str(id)?; - + pub fn get_release_files_handler( + &self, + id: FatCatId, + conn: &DbConn, + ) -> Result<Vec<FileEntity>> { 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(&ident.to_uuid())) + .filter(file_release::target_release_ident_id.eq(&id.to_uuid())) .filter(file_ident::is_live.eq(true)) .filter(file_ident::redirect_id.is_null()) .load(conn)?; @@ -226,19 +203,21 @@ impl Server { pub fn get_work_handler( &self, - id: &Uuid, + id: FatCatId, _expand: Option<String>, conn: &DbConn, ) -> Result<WorkEntity> { - WorkEntity::db_get(conn, FatCatId::from_uuid(id)) + WorkEntity::db_get(conn, id) } - pub fn get_work_releases_handler(&self, id: &str, conn: &DbConn) -> Result<Vec<ReleaseEntity>> { - let id = fcid2uuid(&id)?; - + pub fn get_work_releases_handler( + &self, + id: FatCatId, + conn: &DbConn, + ) -> Result<Vec<ReleaseEntity>> { let rows: Vec<(ReleaseRevRow, ReleaseIdentRow)> = release_rev::table .inner_join(release_ident::table) - .filter(release_rev::work_ident_id.eq(&id)) + .filter(release_rev::work_ident_id.eq(&id.to_uuid())) .filter(release_ident::is_live.eq(true)) .filter(release_ident::redirect_id.is_null()) .load(conn)?; @@ -248,165 +227,8 @@ impl Server { .collect() } - pub fn create_container_handler( - &self, - entity: models::ContainerEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; - let edit = entity.db_create(conn, &edit_context)?; - edit.into_model() - } - - pub fn update_container_handler( - &self, - id: &Uuid, - entity: models::ContainerEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - pub fn delete_container_handler( - &self, - id: &Uuid, - editgroup_id: Option<Uuid>, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - - pub fn create_creator_handler( - &self, - entity: models::CreatorEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; - let edit = entity.db_create(conn, &edit_context)?; - edit.into_model() - } - - pub fn update_creator_handler( - &self, - id: &Uuid, - entity: models::CreatorEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - pub fn delete_creator_handler( - &self, - id: &Uuid, - editgroup_id: Option<Uuid>, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - - pub fn create_file_handler( - &self, - entity: models::FileEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; - let edit = entity.db_create(conn, &edit_context)?; - edit.into_model() - } - - pub fn update_file_handler( - &self, - id: &Uuid, - entity: models::FileEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - pub fn delete_file_handler( - &self, - id: &Uuid, - editgroup_id: Option<Uuid>, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - - pub fn create_release_handler( - &self, - entity: models::ReleaseEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; - let edit = entity.db_create(conn, &edit_context)?; - edit.into_model() - } - - pub fn update_release_handler( - &self, - id: &Uuid, - entity: models::ReleaseEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - pub fn delete_release_handler( - &self, - id: &Uuid, - editgroup_id: Option<Uuid>, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - - pub fn create_work_handler( - &self, - entity: models::WorkEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - let edit_context = make_edit_context(conn, entity.parse_editgroup_id()?, false)?; - let edit = entity.db_create(conn, &edit_context)?; - edit.into_model() - } - - pub fn update_work_handler( - &self, - id: &Uuid, - entity: models::WorkEntity, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - - pub fn delete_work_handler( - &self, - id: &Uuid, - editgroup_id: Option<Uuid>, - conn: &DbConn, - ) -> Result<EntityEdit> { - 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() - } - - pub fn accept_editgroup_handler(&self, id: &str, conn: &DbConn) -> Result<()> { - accept_editgroup(fcid2uuid(id)?, conn)?; + pub fn accept_editgroup_handler(&self, id: FatCatId, conn: &DbConn) -> Result<()> { + accept_editgroup(id, conn)?; Ok(()) } @@ -417,7 +239,7 @@ impl Server { ) -> Result<Editgroup> { let row: EditgroupRow = insert_into(editgroup::table) .values(( - editgroup::editor_id.eq(fcid2uuid(&entity.editor_id)?), + editgroup::editor_id.eq(FatCatId::from_str(&entity.editor_id)?.to_uuid()), editgroup::description.eq(entity.description), editgroup::extra_json.eq(entity.extra), )) @@ -432,14 +254,13 @@ impl Server { }) } - pub fn get_editgroup_handler(&self, id: &str, conn: &DbConn) -> Result<Editgroup> { - let id = fcid2uuid(id)?; - let row: EditgroupRow = editgroup::table.find(id).first(conn)?; + pub fn get_editgroup_handler(&self, id: FatCatId, conn: &DbConn) -> Result<Editgroup> { + let row: EditgroupRow = editgroup::table.find(id.to_uuid()).first(conn)?; let edits = EditgroupEdits { containers: Some( container_edit::table - .filter(container_edit::editgroup_id.eq(id)) + .filter(container_edit::editgroup_id.eq(id.to_uuid())) .get_results(conn)? .into_iter() .map(|e: ContainerEditRow| e.into_model().unwrap()) @@ -447,7 +268,7 @@ impl Server { ), creators: Some( creator_edit::table - .filter(creator_edit::editgroup_id.eq(id)) + .filter(creator_edit::editgroup_id.eq(id.to_uuid())) .get_results(conn)? .into_iter() .map(|e: CreatorEditRow| e.into_model().unwrap()) @@ -455,7 +276,7 @@ impl Server { ), files: Some( file_edit::table - .filter(file_edit::editgroup_id.eq(id)) + .filter(file_edit::editgroup_id.eq(id.to_uuid())) .get_results(conn)? .into_iter() .map(|e: FileEditRow| e.into_model().unwrap()) @@ -463,7 +284,7 @@ impl Server { ), releases: Some( release_edit::table - .filter(release_edit::editgroup_id.eq(id)) + .filter(release_edit::editgroup_id.eq(id.to_uuid())) .get_results(conn)? .into_iter() .map(|e: ReleaseEditRow| e.into_model().unwrap()) @@ -471,7 +292,7 @@ impl Server { ), works: Some( work_edit::table - .filter(work_edit::editgroup_id.eq(id)) + .filter(work_edit::editgroup_id.eq(id.to_uuid())) .get_results(conn)? .into_iter() .map(|e: WorkEditRow| e.into_model().unwrap()) @@ -489,9 +310,8 @@ impl Server { Ok(eg) } - pub fn get_editor_handler(&self, id: &str, conn: &DbConn) -> Result<Editor> { - let id = fcid2uuid(id)?; - let row: EditorRow = editor::table.find(id).first(conn)?; + pub fn get_editor_handler(&self, id: FatCatId, conn: &DbConn) -> Result<Editor> { + let row: EditorRow = editor::table.find(id.to_uuid()).first(conn)?; let ed = Editor { id: Some(uuid2fcid(&row.id)), @@ -500,14 +320,13 @@ impl Server { Ok(ed) } - pub fn editor_changelog_get_handler( + pub fn get_editor_changelog_handler( &self, - id: &str, + id: FatCatId, conn: &DbConn, ) -> Result<Vec<ChangelogEntry>> { - let id = fcid2uuid(id)?; // TODO: single query - let editor: EditorRow = editor::table.find(id).first(conn)?; + let editor: EditorRow = editor::table.find(id.to_uuid()).first(conn)?; let changes: Vec<(ChangelogRow, EditgroupRow)> = changelog::table .inner_join(editgroup::table) .filter(editgroup::editor_id.eq(editor.id)) @@ -552,7 +371,8 @@ impl Server { pub fn get_changelog_entry_handler(&self, id: i64, conn: &DbConn) -> Result<ChangelogEntry> { let cl_row: ChangelogRow = changelog::table.find(id).first(conn)?; - let editgroup = self.get_editgroup_handler(&uuid2fcid(&cl_row.editgroup_id), conn)?; + let editgroup = + self.get_editgroup_handler(FatCatId::from_uuid(&cl_row.editgroup_id), conn)?; let mut entry = cl_row.into_model(); entry.editgroup = Some(editgroup); @@ -638,45 +458,4 @@ impl Server { entity_batch_handler!(create_file_batch_handler, FileEntity); 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<i64>, - conn: &DbConn, - ) -> Result<Vec<EntityHistoryEntry>> { - ContainerEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) - } - pub fn get_creator_history_handler( - &self, - id: &Uuid, - limit: Option<i64>, - conn: &DbConn, - ) -> Result<Vec<EntityHistoryEntry>> { - CreatorEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) - } - pub fn get_file_history_handler( - &self, - id: &Uuid, - limit: Option<i64>, - conn: &DbConn, - ) -> Result<Vec<EntityHistoryEntry>> { - FileEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) - } - pub fn get_release_history_handler( - &self, - id: &Uuid, - limit: Option<i64>, - conn: &DbConn, - ) -> Result<Vec<EntityHistoryEntry>> { - ReleaseEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) - } - pub fn get_work_history_handler( - &self, - id: &Uuid, - limit: Option<i64>, - conn: &DbConn, - ) -> Result<Vec<EntityHistoryEntry>> { - WorkEntity::db_get_history(conn, FatCatId::from_uuid(id), limit) - } } diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index faafe984..f6425c8c 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -1,13 +1,16 @@ //! API endpoint handlers -use api_helpers::fcid2uuid; +use api_entity_crud::EntityCrud; +use api_helpers::*; use api_server::Server; +use database_models::EntityEditRow; use diesel::Connection; use errors::*; use fatcat_api::models; use fatcat_api::models::*; use fatcat_api::*; use futures::{self, Future}; +use std::str::FromStr; /// Helper for generating wrappers (which return "Box::new(futures::done(Ok(BLAH)))" like the /// codegen fatcat-api code wants) that call through to actual helpers (which have simple Result<> @@ -17,11 +20,11 @@ macro_rules! wrap_entity_handlers { // stable doesn't have a mechanism to "concat" or generate new identifiers in macros, at least // in the context of defining new functions. // The only stable approach I know of would be: https://github.com/dtolnay/mashup - ($get_fn:ident, $get_handler:ident, $get_resp:ident, $post_fn:ident, $post_handler:ident, + ($get_fn:ident, $get_handler:ident, $get_resp:ident, $post_fn:ident, $post_resp:ident, $post_batch_fn:ident, $post_batch_handler: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) => { + $post_batch_resp:ident, $update_fn:ident, $update_resp:ident, + $delete_fn:ident, $delete_resp:ident, $get_history_fn:ident, + $get_history_resp:ident, $model:ident) => { fn $get_fn( &self, @@ -29,13 +32,12 @@ macro_rules! wrap_entity_handlers { expand: Option<String>, _context: &Context, ) -> Box<Future<Item = $get_resp, Error = ApiError> + Send> { - let id = if let Ok(parsed) = fcid2uuid(&id) { parsed } else { - return Box::new(futures::done(Ok($get_resp::BadRequest(ErrorResponse { - message: ErrorKind::InvalidFatcatId(id).to_string() })))); - }; let conn = self.db_pool.get().expect("db_pool error"); // No transaction for GET - let ret = match self.$get_handler(&id, expand, &conn) { + let ret = match conn.transaction(|| { + let entity_id = FatCatId::from_str(&id)?; + self.$get_handler(entity_id, expand, &conn) + }) { Ok(entity) => $get_resp::FoundEntity(entity), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -61,7 +63,10 @@ macro_rules! wrap_entity_handlers { _context: &Context, ) -> Box<Future<Item = $post_resp, Error = ApiError> + 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(|| { + let edit_context = make_edit_context(&conn, entity.parse_editgroup_id()?, false)?; + Ok(entity.db_create(&conn, &edit_context)?.into_model()?) + }) { Ok(edit) => $post_resp::CreatedEntity(edit), Err(Error(ErrorKind::Diesel(e), _)) => @@ -115,12 +120,12 @@ macro_rules! wrap_entity_handlers { entity: models::$model, _context: &Context, ) -> Box<Future<Item = $update_resp, Error = ApiError> + 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)) { + let ret = match conn.transaction(|| { + let entity_id = FatCatId::from_str(&id)?; + let edit_context = make_edit_context(&conn, entity.parse_editgroup_id()?, false)?; + Ok(entity.db_update(&conn, &edit_context, entity_id)?.into_model()?) + }) { Ok(edit) => $update_resp::UpdatedEntity(edit), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -148,19 +153,16 @@ macro_rules! wrap_entity_handlers { editgroup_id: Option<String>, _context: &Context, ) -> Box<Future<Item = $delete_resp, Error = ApiError> + 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)) { + let ret = match conn.transaction(|| { + let entity_id = FatCatId::from_str(&id)?; + let editgroup_id: Option<FatCatId> = match editgroup_id { + Some(s) => Some(FatCatId::from_str(&s)?), + None => None, + }; + let edit_context = make_edit_context(&conn, editgroup_id, false)?; + Ok($model::db_delete(&conn, &edit_context, entity_id)?.into_model()?) + }) { Ok(edit) => $delete_resp::DeletedEntity(edit), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -188,13 +190,12 @@ macro_rules! wrap_entity_handlers { limit: Option<i64>, _context: &Context, ) -> Box<Future<Item = $get_history_resp, Error = ApiError> + Send> { - let id = if let Ok(parsed) = fcid2uuid(&id) { parsed } else { - return Box::new(futures::done(Ok($get_history_resp::BadRequest(ErrorResponse { - message: ErrorKind::InvalidFatcatId(id).to_string() })))); - }; let conn = self.db_pool.get().expect("db_pool error"); // No transaction for GET - let ret = match self.$get_history_handler(&id, limit, &conn) { + let ret = match conn.transaction(|| { + let entity_id = FatCatId::from_str(&id)?; + Ok($model::db_get_history(&conn, entity_id, limit)?) + }) { Ok(history) => $get_history_resp::FoundEntityHistory(history), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => @@ -240,25 +241,50 @@ macro_rules! wrap_lookup_handler { } } +macro_rules! wrap_fcid_handler { + ($get_fn:ident, $get_handler:ident, $get_resp:ident) => { + fn $get_fn( + &self, + id: String, + _context: &Context, + ) -> Box<Future<Item = $get_resp, Error = ApiError> + Send> { + let conn = self.db_pool.get().expect("db_pool error"); + // No transaction for GET + let ret = match (|| { + let fcid = FatCatId::from_str(&id)?; + self.$get_handler(fcid, &conn) + })() { + Ok(entity) => + $get_resp::Found(entity), + Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => + $get_resp::NotFound(ErrorResponse { message: format!("Not found: {}", id) }), + Err(Error(ErrorKind::MalformedExternalId(e), _)) => + $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), + Err(e) => { + error!("{}", e); + $get_resp::BadRequest(ErrorResponse { message: e.to_string() }) + }, + }; + Box::new(futures::done(Ok(ret))) + } + } +} + impl Api for Server { wrap_entity_handlers!( get_container, get_container_handler, GetContainerResponse, create_container, - create_container_handler, CreateContainerResponse, 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, ContainerEntity ); @@ -268,19 +294,15 @@ impl Api for Server { get_creator_handler, GetCreatorResponse, create_creator, - create_creator_handler, CreateCreatorResponse, 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, CreatorEntity ); @@ -289,19 +311,15 @@ impl Api for Server { get_file_handler, GetFileResponse, create_file, - create_file_handler, CreateFileResponse, 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, FileEntity ); @@ -310,19 +328,15 @@ impl Api for Server { get_release_handler, GetReleaseResponse, create_release, - create_release_handler, CreateReleaseResponse, 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, ReleaseEntity ); @@ -331,19 +345,15 @@ impl Api for Server { get_work_handler, GetWorkResponse, create_work, - create_work_handler, CreateWorkResponse, 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, WorkEntity ); @@ -377,26 +387,26 @@ impl Api for Server { String ); - wrap_lookup_handler!( + wrap_fcid_handler!( get_release_files, get_release_files_handler, - GetReleaseFilesResponse, - id, - String + GetReleaseFilesResponse ); - wrap_lookup_handler!( + wrap_fcid_handler!( get_work_releases, get_work_releases_handler, - GetWorkReleasesResponse, - id, - String + GetWorkReleasesResponse ); - wrap_lookup_handler!( + wrap_fcid_handler!( get_creator_releases, get_creator_releases_handler, - GetCreatorReleasesResponse, - id, - String + GetCreatorReleasesResponse + ); + wrap_fcid_handler!(get_editor, get_editor_handler, GetEditorResponse); + wrap_fcid_handler!( + get_editor_changelog, + get_editor_changelog_handler, + GetEditorChangelogResponse ); fn accept_editgroup( @@ -405,7 +415,10 @@ impl Api for Server { _context: &Context, ) -> Box<Future<Item = AcceptEditgroupResponse, Error = ApiError> + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.accept_editgroup_handler(&id, &conn)) { + let ret = match conn.transaction(|| { + let id = FatCatId::from_str(&id)?; + self.accept_editgroup_handler(id, &conn) + }) { Ok(()) => AcceptEditgroupResponse::MergedSuccessfully(Success { message: "horray!".to_string(), }), @@ -432,9 +445,12 @@ impl Api for Server { _context: &Context, ) -> Box<Future<Item = GetEditgroupResponse, Error = ApiError> + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.get_editgroup_handler(&id, &conn)) { + let ret = match conn.transaction(|| { + let id = FatCatId::from_str(&id)?; + self.get_editgroup_handler(id, &conn) + }) { Ok(entity) => - GetEditgroupResponse::FoundEntity(entity), + GetEditgroupResponse::Found(entity), Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => GetEditgroupResponse::NotFound( ErrorResponse { message: format!("No such editgroup: {}", id) }), @@ -452,7 +468,9 @@ impl Api for Server { _context: &Context, ) -> Box<Future<Item = CreateEditgroupResponse, Error = ApiError> + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let ret = match conn.transaction(|| self.create_editgroup_handler(entity, &conn)) { + let ret = match conn.transaction(|| + self.create_editgroup_handler(entity, &conn) + ) { Ok(eg) => CreateEditgroupResponse::SuccessfullyCreated(eg), Err(e) => @@ -463,56 +481,6 @@ impl Api for Server { Box::new(futures::done(Ok(ret))) } - fn get_editor_changelog( - &self, - username: String, - _context: &Context, - ) -> Box<Future<Item = GetEditorChangelogResponse, Error = ApiError> + Send> { - let conn = self.db_pool.get().expect("db_pool error"); - // No transaction for GET - let ret = match self.editor_changelog_get_handler(&username, &conn) { - Ok(entries) => GetEditorChangelogResponse::FoundMergedChanges(entries), - Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => { - GetEditorChangelogResponse::NotFound(ErrorResponse { - message: format!("No such editor: {}", username.clone()), - }) - } - Err(e) => { - // TODO: dig in to error type here - error!("{}", e); - GetEditorChangelogResponse::GenericError(ErrorResponse { - message: e.to_string(), - }) - } - }; - Box::new(futures::done(Ok(ret))) - } - - fn get_editor( - &self, - username: String, - _context: &Context, - ) -> Box<Future<Item = GetEditorResponse, Error = ApiError> + Send> { - let conn = self.db_pool.get().expect("db_pool error"); - // No transaction for GET - let ret = match self.get_editor_handler(&username, &conn) { - Ok(entity) => GetEditorResponse::FoundEditor(entity), - Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => { - GetEditorResponse::NotFound(ErrorResponse { - message: format!("No such editor: {}", username.clone()), - }) - } - Err(e) => { - // TODO: dig in to error type here - error!("{}", e); - GetEditorResponse::GenericError(ErrorResponse { - message: e.to_string(), - }) - } - }; - Box::new(futures::done(Ok(ret))) - } - fn get_changelog( &self, limit: Option<i64>, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 2236d602..b4b9e3c7 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -22,10 +22,10 @@ extern crate regex; extern crate lazy_static; extern crate sha1; +pub mod api_entity_crud; 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; |