diff options
| -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())) | 
