blatherskite

a toy discord-like chat app backend written for a swe class
Log | Files | Refs | README

commit 2f23276543271606865c80d5f2cc299d2f2ab850
parent d14540ffd17b7e025abaaf2d58f5f74389656f5a
Author: quantumish <freifeld.david@gmail.com>
Date:   Fri, 23 Sep 2022 13:30:06 -0700

Document more of API responses

Diffstat:
Mscuttlebutt/src/main.rs | 302++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 192 insertions(+), 110 deletions(-)

diff --git a/scuttlebutt/src/main.rs b/scuttlebutt/src/main.rs @@ -1,33 +1,40 @@ -use poem::{listener::TcpListener, Route, Server, Result, Request, web::Data}; -use poem_openapi::{param::Query, payload::{PlainText, Json}, *, auth::ApiKey}; use hmac::{Hmac, Mac}; -use log::{warn, info}; use jwt::{SignWithKey, VerifyWithKey}; -use serde::{Serialize, Deserialize}; +use log::{info, warn}; +use poem::{listener::TcpListener, web::Data, Request, Result, Route, Server}; +use poem_openapi::{ + auth::ApiKey, + param::Query, + payload::{Json, PlainText}, + *, +}; +use serde::{Deserialize, Serialize}; use sha2::Sha256; struct Api; #[derive(Object, Serialize, Deserialize)] struct User { - id: i64, + id: i64, username: String, - email: String, + email: String, } #[derive(Object)] struct Group { - id: i64, + id: i64, name: String, - members: Vec<i64>, - channels: Vec<i64>, + // The IDs of the group members + members: Vec<i64>, + // The IDs of the group's channels + channels: Vec<i64>, } #[derive(Object)] struct Channel { id: i64, name: String, - members: Vec<i64>, + members: Vec<i64>, } // type ServerKey = Hmac<Sha256>; @@ -49,151 +56,225 @@ struct Channel { #[derive(ApiResponse)] enum UserResponse { - /// Returns the user requested. - #[oai(status = 200)] - User(Json<User>), - /// Returns when there is no user associated with the ID - #[oai(status = 404)] - NotFound, - /// Recieved a bad argument when specifying the user. Returns error type, such as: - /// - found empty string for any of the arguments - /// - invalid email - #[oai(status = 400)] - BadRequest, + /// Returns the user requested. + #[oai(status = 200)] + User(Json<User>), + /// Invalid ID. Content specifies which of the IDs passed is invalid. + #[oai(status = 404)] + NotFound(PlainText<String>), +} + +#[derive(ApiResponse)] +enum CreateUserResponse { + /// Returns the user requested. + #[oai(status = 200)] + User(Json<User>), + /// Recieved a bad argument when specifying the user. Returns error type, such as: + /// - found empty string for any of the arguments + /// - invalid email + #[oai(status = 400)] + BadRequest, } #[derive(ApiResponse)] enum DeleteResponse { - /// The delete operation succeeded - #[oai(status = 200)] - Success, - /// Invalid ID - #[oai(status = 404)] - NotFound, + /// The delete operation succeeded + #[oai(status = 200)] + Success, + /// You are not authorized to perform the action + #[oai(status = 401)] + Unauthorized, + /// Invalid ID. Content specifies which of the IDs passed is invalid. + #[oai(status = 404)] + NotFound(PlainText<String>), +} + +#[derive(ApiResponse)] +enum GroupResponse { + /// Returns the group requested + #[oai(status = 200)] + Group(Json<Group>), + /// Invalid ID. + #[oai(status = 404)] + NotFound, +} + +#[derive(ApiResponse)] +enum CreateGroupResponse { + /// Returns the group requested + #[oai(status = 200)] + Group(Json<Group>), + /// Invalid parameter, such as: + /// - empty string for name + /// - bad string + #[oai(status = 400)] + BadRequest(PlainText<String>), +} + +#[derive(ApiResponse)] +enum ChannelResponse { + /// Returns the channel requested + #[oai(status = 200)] + Channel(Json<Channel>), + /// Invalid ID. Content specifies which of the IDs passed is invalid. + #[oai(status = 404)] + NotFound(PlainText<String>), +} + +#[derive(ApiResponse)] +enum CreateChannelResponse { + /// Returns the channel requested + #[oai(status = 200)] + Channel(Json<Channel>), + /// Invalid parameter, such as: + /// - empty string for name + /// - bad string + #[oai(status = 400)] + BadRequest(PlainText<String>), +} + +#[derive(ApiResponse)] +enum GenericResponse { + /// Action succeeded + #[oai(status = 200)] + Success, + /// Invalid ID. Content specifies which of the IDs passed is invalid. + #[oai(status = 404)] + NotFound(PlainText<String>), } #[OpenApi] impl Api { #[oai(path = "/user", method = "get")] - /// Gets the user with the given ID + /// Gets the user with the given ID /// /// # Example - /// + /// /// Call `/user?id=1234` to get the user with id 1234 - async fn get_user(&self, id: Query<i64>) -> UserResponse { + async fn get_user(&self, id: Query<i64>) -> UserResponse { todo!() } - #[oai(path = "/user", method = "post")] - /// Creates a new user - async fn make_user(&self, name: Query<String>, email: Query<String>, password: Query<String>) -> Result<Json<User>> { + #[oai(path = "/user", method = "post")] + /// Creates a new user + async fn make_user( + &self, + name: Query<String>, + email: Query<String>, + password: Query<String>, + ) -> CreateUserResponse { todo!() } - #[oai(path = "/user", method = "put")] - /// Updates a user's name and email - async fn update_user(&self, id: Query<i64>, name: Query<String>, email: Query<String>) -> UserResponse { + #[oai(path = "/user", method = "put")] + /// Updates a user's name and email + async fn update_user( + &self, + id: Query<i64>, + name: Query<String>, + email: Query<String>, + ) -> CreateUserResponse { todo!() } - #[oai(path = "/user", method = "delete")] - /// Deletes a user + #[oai(path = "/user", method = "delete")] + /// Deletes a user async fn delete_user(&self, id: Query<i64>) -> DeleteResponse { todo!() } - - #[oai(path = "/user/groups", method = "get")] - /// Gets all groups accessible to a user + + #[oai(path = "/user/groups", method = "get")] + /// Gets all groups accessible to a user async fn get_groups(&self, id: Query<i64>) -> Json<Vec<Group>> { todo!() } - #[oai(path = "/user/groups", method = "delete")] - /// Leaves a group accessible to the user - async fn leave_group(&self, uid: Query<i64>, gid: Query<i64>) -> Json<Vec<Group>> { - todo!() - } - - #[oai(path = "/group", method = "get")] - /// Gets the group with the given ID - async fn get_group(&self, id: Query<i64>) -> Json<Group> { + #[oai(path = "/user/groups", method = "delete")] + /// Leaves a group accessible to the user + async fn leave_group(&self, uid: Query<i64>, gid: Query<i64>) -> GenericResponse { todo!() } - #[oai(path = "/group", method = "post")] - /// Creates a new group - async fn make_group(&self, name: Query<String>) -> Json<Group> { + #[oai(path = "/group", method = "get")] + /// Gets the group with the given ID + async fn get_group(&self, id: Query<i64>) -> GroupResponse { todo!() } - #[oai(path = "/group", method = "put")] - /// Updates the name of an existing group - async fn update_group(&self, id: Query<i64>, name: Query<String>) -> Json<Group> { + #[oai(path = "/group", method = "post")] + /// Creates a new group + async fn make_group(&self, name: Query<String>) -> CreateGroupResponse { todo!() } - #[oai(path = "/group", method = "delete")] - /// Deletes a user + #[oai(path = "/group", method = "put")] + /// Updates the name of an existing group + async fn update_group(&self, id: Query<i64>, name: Query<String>) -> CreateGroupResponse { + todo!() + } + + #[oai(path = "/group", method = "delete")] + /// Deletes a user async fn delete_group(&self, id: Query<i64>) -> DeleteResponse { todo!() } - - #[oai(path = "/group/members", method = "get")] - /// Gets the members of the specified group + + #[oai(path = "/group/members", method = "get")] + /// Gets the members of the specified group async fn get_group_members(&self, id: Query<i64>) -> Json<Vec<User>> { todo!() } - #[oai(path = "/group/members", method = "put")] - /// Adds a member to an existing group - async fn add_group_member(&self, gid: Query<i64>, uid: Query<i64>) -> Json<Vec<User>> { + #[oai(path = "/group/members", method = "put")] + /// Adds a member to an existing group + async fn add_group_member(&self, gid: Query<i64>, uid: Query<i64>) -> GenericResponse { todo!() } - #[oai(path = "/group/members", method = "delete")] - /// Adds a member to an existing group - async fn remove_group_member(&self, gid: Query<i64>, uid: Query<i64>) -> Json<Vec<User>> { + #[oai(path = "/group/members", method = "delete")] + /// Removes a member to an existing group + async fn remove_group_member(&self, gid: Query<i64>, uid: Query<i64>) -> DeleteResponse { todo!() } - - #[oai(path = "/group/channels", method = "get")] - /// Gets all channels in a group that are accessible to a user + + #[oai(path = "/group/channels", method = "get")] + /// Gets all channels in a group that are accessible to a user async fn get_channels(&self, gid: Query<i64>, uid: Query<i64>) -> Json<Vec<Channel>> { todo!() } - #[oai(path = "/group/channels", method = "post")] - /// Creates a channel in a group - async fn make_channel(&self, gid: Query<i64>, name: Query<String>) -> Json<Channel> { + #[oai(path = "/group/channels", method = "post")] + /// Creates a channel in a group + async fn make_channel(&self, gid: Query<i64>, name: Query<String>) -> CreateChannelResponse { todo!() } - #[oai(path = "/channel", method = "put")] - /// Updates the name of a channel - async fn update_channel(&self, id: Query<i64>, name: Query<String>) -> Json<Channel> { + #[oai(path = "/channel", method = "put")] + /// Updates the name of a channel + async fn update_channel(&self, id: Query<i64>, name: Query<String>) -> CreateChannelResponse { todo!() } - - #[oai(path = "/channel", method = "delete")] - /// Deletes a channel + + #[oai(path = "/channel", method = "delete")] + /// Deletes a channel async fn delete_channel(&self, id: Query<i64>) -> DeleteResponse { todo!() } - #[oai(path = "/channel/members", method = "get")] - /// Gets the members that can access a channel + #[oai(path = "/channel/members", method = "get")] + /// Gets the members that can access a channel async fn get_channel_members(&self, id: Query<i64>) -> Json<Vec<User>> { todo!() } - - #[oai(path = "/channel/members", method = "put")] - /// Adds a member to a channel - async fn add_channel_member(&self, cid: Query<i64>, uid: Query<i64>) -> Json<Channel> { + + #[oai(path = "/channel/members", method = "put")] + /// Adds a member to a channel + async fn add_channel_member(&self, cid: Query<i64>, uid: Query<i64>) -> GenericResponse { todo!() } - #[oai(path = "/channel/members", method = "delete")] - /// Removes a member from a channel + #[oai(path = "/channel/members", method = "delete")] + /// Removes a member from a channel async fn remove_channel_member(&self, cid: Query<i64>, uid: Query<i64>) -> DeleteResponse { todo!() } @@ -206,18 +287,19 @@ async fn main() -> Result<(), std::io::Error> { } tracing_subscriber::fmt::init(); - let api_service = - OpenApiService::new(Api, "Scuttlebutt", "1.0") - .description("Scuttlebutt is the REST API for managing everything but sending/receiving messages \ - - which means creating/updating/deleting all of your users/groups/channels.") - .server("http://localhost:3000/api"); + let api_service = OpenApiService::new(Api, "Scuttlebutt", "1.0") + .description( + "Scuttlebutt is the REST API for managing everything but sending/receiving messages \ + - which means creating/updating/deleting all of your users/groups/channels.", + ) + .server("http://localhost:3000/api"); let ui = api_service.swagger_ui(); - // let wat = b"whee"; - // let server_key = Hmac::<Sha256>::new_from_slice(wat).expect("valid server key"); - // let server_key2 = Hmac::<Sha256>::new_from_hex - // println!("{:?}", server_key.); + // let wat = b"whee"; + // let server_key = Hmac::<Sha256>::new_from_slice(wat).expect("valid server key"); + // let server_key2 = Hmac::<Sha256>::new_from_hex + // println!("{:?}", server_key.); Server::new(TcpListener::bind("127.0.0.1:3000")) .run(Route::new().nest("/api", api_service).nest("/", ui)) .await @@ -226,18 +308,18 @@ async fn main() -> Result<(), std::io::Error> { #[cfg(test)] mod tests { use super::*; - use poem::test::TestClient; - - // fn setup() -> TestClient<Route> { - // let app = OpenApiService::new(Api, "Scuttlebutt", "1.0").server("http://localhost:3000/api"); - // TestClient::new(Route::new().nest("/api", app)) - // } - - // #[tokio::test] - // async fn sanity() { - // let cli = setup(); - // let resp = cli.get("/api/hello").send().await; - // resp.assert_status_is_ok(); - // resp.assert_text("whee").await; - // } + use poem::test::TestClient; + + // fn setup() -> TestClient<Route> { + // let app = OpenApiService::new(Api, "Scuttlebutt", "1.0").server("http://localhost:3000/api"); + // TestClient::new(Route::new().nest("/api", app)) + // } + + // #[tokio::test] + // async fn sanity() { + // let cli = setup(); + // let resp = cli.get("/api/hello").send().await; + // resp.assert_status_is_ok(); + // resp.assert_text("whee").await; + // } }