diff options
Diffstat (limited to 'rust')
-rw-r--r-- | rust/README.md | 7 | ||||
-rw-r--r-- | rust/TODO | 19 | ||||
-rw-r--r-- | rust/fatcat-openapi2.yml | 1 | ||||
-rw-r--r-- | rust/src/api_server.rs | 209 |
4 files changed, 149 insertions, 87 deletions
diff --git a/rust/README.md b/rust/README.md index f539e4fb..794afee7 100644 --- a/rust/README.md +++ b/rust/README.md @@ -9,6 +9,13 @@ Things! diesel print-schema > src/database_schema.rs +Regenerate API schemas: + + cargo swagger fatcat-openapi2.yml fatcat-api --docker-tag=v2.3.1 + sudo chown `whoami`:`whoami` -R fatcat-api + # edit fatcat-api/Cargo.toml, set name to "fatcat-api" + cargo fmt + Debugging SQL errors: psql fatcat_rs < migrations/2018-05-12-001226_init/up.sql @@ -4,13 +4,22 @@ x re-generate OpenAPI => cargo swagger (docker) seems to only use latest x iron-slog x integrate API server example into a main.rs -- get and post for creators -- cleanup pooled database: https://github.com/diesel-rs/diesel/pull/1466 +x get and post for creators +x cleanup pooled database: https://github.com/diesel-rs/diesel/pull/1466 +- clean up blasse error handling a bit +- add 404s to gets +- wow. fix a bunch of api schema names ("FindASingleContainerByExternalIdentifer") +- creators, releases, works, files (?) +- one-to-many relationship (eg, works) +- many-to-many relationship (eg, creators) +- refactor handlers to have a proper Result<_,_> error-chain type, so I can use '?' +- copypasta a bunch of CRUD +- helper to calculate state of idents then: -- encode the remaining entities in SQL -- openapi for other entities (heavily templated) - +x encode the remaining entities in SQL +x openapi for other entities (heavily templated) +- figure out JSON(B) in both swagger and diesel later: - metrics, jwt, config, sentry, testing diff --git a/rust/fatcat-openapi2.yml b/rust/fatcat-openapi2.yml index abf09a10..955cef65 100644 --- a/rust/fatcat-openapi2.yml +++ b/rust/fatcat-openapi2.yml @@ -144,6 +144,7 @@ definitions: type: integer editor_id: type: integer + # TODO: work_edits array, etc. ["edits"]["work"] or ["work_edits"]? changelogentry: type: object required: diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index 8523f154..385a1eff 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -32,19 +32,38 @@ impl Api for Server { _context: &Context, ) -> Box<Future<Item = ContainerIdGetResponse, Error = ApiError> + Send> { let conn = self.db_pool.get().expect("db_pool error"); - let id = uuid::Uuid::parse_str(&id).unwrap(); + let id = match uuid::Uuid::parse_str(&id) { + Ok(some_uuid) => some_uuid, + Err(_) => { + return Box::new(futures::done(Ok( + ContainerIdGetResponse::BadRequest( + Error { message: "Failed to parse UUID".to_string() } + )))); + } + }; - let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table + let res: Result<(ContainerIdentRow, ContainerRevRow), _> = container_ident::table .find(id) .inner_join(container_rev::table) - .first(&conn) - .expect("error loading container"); + .first(&conn); + + let (ident, rev) = match res { + Ok(r) => r, + Err(_) => { + return Box::new(futures::done(Ok( + // TODO: UGH, need to add 404 responses everywhere, not 400 + //ContainerIdGetResponse::NotFound( + ContainerIdGetResponse::BadRequest( + Error { message: "No such container".to_string() } + )))); + } + }; let entity = ContainerEntity { issn: rev.issn, publisher: rev.publisher, - parent: None, // TODO - name: Some(rev.name), // TODO: not optional + parent: None, // TODO: + name: rev.name, state: None, // TODO: ident: Some(ident.id.to_string()), revision: ident.rev_id.map(|v| v as isize), @@ -59,31 +78,57 @@ impl Api for Server { fn container_lookup_get( &self, issn: String, - context: &Context, + _context: &Context, ) -> Box<Future<Item = ContainerLookupGetResponse, Error = ApiError> + Send> { - let context = context.clone(); - println!( - "container_lookup_get(\"{}\") - X-Span-ID: {:?}", - issn, - context.x_span_id.unwrap_or(String::from("<none>")).clone() - ); - Box::new(futures::failed("Generic failure".into())) + let conn = self.db_pool.get().expect("db_pool error"); + + let res: Result<(ContainerIdentRow, ContainerRevRow), _> = container_ident::table + .inner_join(container_rev::table) + .first(&conn); + // XXX: actually do a filter/lookup + + let (ident, rev) = match res { + Ok(r) => r, + Err(_) => { + return Box::new(futures::done(Ok( + // TODO: UGH, need to add 404 responses everywhere, not 400 + //ContainerIdGetResponse::NotFound( + ContainerLookupGetResponse::BadRequest( + Error { message: "No such container".to_string() } + )))); + } + }; + + let entity = ContainerEntity { + issn: rev.issn, + publisher: rev.publisher, + parent: None, // TODO: + name: rev.name, + state: None, // TODO: + ident: Some(ident.id.to_string()), + revision: ident.rev_id.map(|v| v as isize), + redirect: ident.redirect_id.map(|u| u.to_string()), + editgroup: None, + }; + Box::new(futures::done(Ok( + ContainerLookupGetResponse::FindASingleContainerByExternalIdentifer(entity), + ))) } fn container_post( &self, - body: Option<models::ContainerEntity>, + body: models::ContainerEntity, context: &Context, ) -> Box<Future<Item = ContainerPostResponse, Error = ApiError> + Send> { println!("{:?}", body); - let body = body.expect("missing body"); // TODO: required parameter - //let editgroup_id: i64 = body.editgroup.expect("need editgroup_id") as i64; // TODO: or find/create + //let editgroup_id: i64 = body.editgroup.expect("need editgroup_id") as i64; + // TODO: or find/create let editgroup_id = 1; let conn = self.db_pool.get().expect("db_pool error"); - let name = body.name.unwrap(); - let issn = body.issn.unwrap(); - println!("name={} issn={}", name, issn); + let name = body.name; + let issn = body.issn; + println!("name={} issn={:?}", name, issn); let edit: Vec<ContainerEditRow> = diesel::sql_query( "WITH rev AS ( INSERT INTO container_rev (name, issn) @@ -96,7 +141,7 @@ impl Api for Server { ($3, (SELECT ident.id FROM ident), (SELECT rev.id FROM rev)) RETURNING *", ).bind::<diesel::sql_types::Text, _>(name) - .bind::<diesel::sql_types::Text, _>(issn) + .bind::<diesel::sql_types::Nullable<diesel::sql_types::Text>, _>(issn) .bind::<diesel::sql_types::BigInt, _>(editgroup_id) .load(&conn) .unwrap(); @@ -106,7 +151,7 @@ impl Api for Server { editgroup_id: Some(edit.editgroup_id as isize), revision: Some(edit.rev_id.unwrap() as isize), ident: Some(edit.ident_id.to_string()), - id: Some(edit.id as isize), + edit_id: Some(edit.id as isize), }; Box::new(futures::done(Ok(ContainerPostResponse::Created( entity_edit, @@ -121,7 +166,7 @@ impl Api for Server { let conn = self.db_pool.get().expect("db_pool error"); let ce = CreatorEntity { orcid: None, - name: None, + name: "Dummy Name".into(), state: None, ident: None, revision: None, @@ -149,7 +194,7 @@ impl Api for Server { fn creator_post( &self, - body: Option<models::CreatorEntity>, + body: models::CreatorEntity, context: &Context, ) -> Box<Future<Item = CreatorPostResponse, Error = ApiError> + Send> { let context = context.clone(); @@ -161,181 +206,181 @@ impl Api for Server { Box::new(futures::failed("Generic failure".into())) } - fn editgroup_id_accept_post( + fn file_id_get( &self, - id: i32, + id: String, context: &Context, - ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "editgroup_id_accept_post({}) - X-Span-ID: {:?}", + "file_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn editgroup_id_get( + fn file_lookup_get( &self, - id: i32, + sha1: String, context: &Context, - ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "editgroup_id_get({}) - X-Span-ID: {:?}", - id, + "file_lookup_get(\"{}\") - X-Span-ID: {:?}", + sha1, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn editgroup_post( + fn file_post( &self, + body: models::FileEntity, context: &Context, - ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "editgroup_post() - X-Span-ID: {:?}", + "file_post({:?}) - X-Span-ID: {:?}", + body, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn editor_username_changelog_get( + fn work_id_get( &self, - username: String, + id: String, context: &Context, - ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}", - username, + "work_id_get(\"{}\") - X-Span-ID: {:?}", + id, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn editor_username_get( + fn work_post( &self, - username: String, + body: models::WorkEntity, context: &Context, - ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "editor_username_get(\"{}\") - X-Span-ID: {:?}", - username, + "work_post({:?}) - X-Span-ID: {:?}", + body, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn file_id_get( + fn release_id_get( &self, id: String, context: &Context, - ) -> Box<Future<Item = FileIdGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "file_id_get(\"{}\") - X-Span-ID: {:?}", + "release_id_get(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn file_lookup_get( + fn release_lookup_get( &self, - sha1: String, + doi: String, context: &Context, - ) -> Box<Future<Item = FileLookupGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "file_lookup_get(\"{}\") - X-Span-ID: {:?}", - sha1, + "release_lookup_get(\"{}\") - X-Span-ID: {:?}", + doi, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn file_post( + fn release_post( &self, - body: Option<models::FileEntity>, + body: models::ReleaseEntity, context: &Context, - ) -> Box<Future<Item = FilePostResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "file_post({:?}) - X-Span-ID: {:?}", + "release_post({:?}) - X-Span-ID: {:?}", body, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn release_id_get( + fn editgroup_id_accept_post( &self, - id: String, + id: i32, context: &Context, - ) -> Box<Future<Item = ReleaseIdGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = EditgroupIdAcceptPostResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "release_id_get(\"{}\") - X-Span-ID: {:?}", + "editgroup_id_accept_post({}) - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn release_lookup_get( + fn editgroup_id_get( &self, - doi: String, + id: i32, context: &Context, - ) -> Box<Future<Item = ReleaseLookupGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = EditgroupIdGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "release_lookup_get(\"{}\") - X-Span-ID: {:?}", - doi, + "editgroup_id_get({}) - X-Span-ID: {:?}", + id, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn release_post( + fn editgroup_post( &self, - body: Option<models::ReleaseEntity>, context: &Context, - ) -> Box<Future<Item = ReleasePostResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = EditgroupPostResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "release_post({:?}) - X-Span-ID: {:?}", - body, + "editgroup_post() - X-Span-ID: {:?}", context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn work_id_get( + fn editor_username_changelog_get( &self, - id: String, + username: String, context: &Context, - ) -> Box<Future<Item = WorkIdGetResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = EditorUsernameChangelogGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "work_id_get(\"{}\") - X-Span-ID: {:?}", - id, + "editor_username_changelog_get(\"{}\") - X-Span-ID: {:?}", + username, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) } - fn work_post( + fn editor_username_get( &self, - body: Option<models::WorkEntity>, + username: String, context: &Context, - ) -> Box<Future<Item = WorkPostResponse, Error = ApiError> + Send> { + ) -> Box<Future<Item = EditorUsernameGetResponse, Error = ApiError> + Send> { let context = context.clone(); println!( - "work_post({:?}) - X-Span-ID: {:?}", - body, + "editor_username_get(\"{}\") - X-Span-ID: {:?}", + username, context.x_span_id.unwrap_or(String::from("<none>")).clone() ); Box::new(futures::failed("Generic failure".into())) |