diff --git a/Cargo.lock b/Cargo.lock index 5d19af0..2505613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,119 +50,6 @@ dependencies = [ "password-hash", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "atoi" version = "2.0.0" @@ -284,19 +171,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel 2.5.0", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "bumpalo" version = "3.20.3" @@ -602,12 +476,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -619,16 +487,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -754,19 +612,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-sink" version = "0.3.32" @@ -827,18 +672,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -901,12 +734,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hex" version = "0.4.3" @@ -1211,15 +1038,6 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1289,9 +1107,6 @@ name = "log" version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" -dependencies = [ - "value-bag", -] [[package]] name = "matchit" @@ -1550,23 +1365,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkcs1" version = "0.7.5" @@ -1600,20 +1398,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "potential_utf" version = "0.1.5" @@ -2105,7 +1889,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-intrusive", "futures-io", @@ -2143,7 +1927,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-intrusive", "futures-io", @@ -2511,16 +2295,11 @@ dependencies = [ [[package]] name = "textsender_auth" -version = "0.2.0" +version = "0.2.1" dependencies = [ "argon2", - "async-std", "axum", - "http-body-util", - "hyper", "josekit", - "once_cell", - "rand 0.10.1", "serde", "serde_json", "sqlx 0.9.0", @@ -2914,12 +2693,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - [[package]] name = "vcpkg" version = "0.2.15" @@ -2967,16 +2740,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.126" diff --git a/Cargo.toml b/Cargo.toml index bd594a5..0ded84c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "textsender_auth" -version = "0.2.0" +version = "0.2.1" edition = "2024" rust-version = "1.96" @@ -10,13 +10,10 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = { version = "1.0.150" } tokio = { version = "1.52.3", features = ["rt-multi-thread"] } tracing-subscriber = { version = "0.3.23" } -tower = { version = "0.5.3", features = ["full"] } tower-http = { version = "0.6.11", features = ["cors"] } -hyper = { version = "1.10.1" } sqlx = { version = "0.9.0", features = ["runtime-tokio", "tls-native-tls", "postgres", "time", "uuid"] } uuid = { version = "1.23.3", features = ["v4", "serde"] } argon2 = { version = "0.5.3", features = ["std"] } # Use the latest 0.5.x version -rand = { version = "0.10.1" } time = { version = "0.3.49", features = ["macros", "serde"] } josekit = { version = "0.10.3" } utoipa = { version = "5.5.0", features = ["axum_extras"] } @@ -24,7 +21,5 @@ utoipa-swagger-ui = { version = "9.0.2", features = ["axum"] } textsender_models = { git = "ssh://git@git.kundeng.us/phoenix/textsender_models.git", tag = "v0.4.10", features = ["config", "user"] } [dev-dependencies] -http-body-util = { version = "0.1.3" } +tower = { version = "0.5.3", features = ["full"] } url = { version = "2.5.8" } -once_cell = { version = "1.21.4" } # Useful for lazy initialization in tests/app setup -async-std = { version = "1.13.2" } diff --git a/src/callers/login.rs b/src/callers/login.rs index d9216b2..efa2454 100644 --- a/src/callers/login.rs +++ b/src/callers/login.rs @@ -120,6 +120,12 @@ pub mod response { pub message: String, pub data: Vec, } + + #[derive(Default, Deserialize, Serialize, utoipa::ToSchema)] + pub struct GetUserProfileResponse { + pub message: String, + pub data: Vec, + } } /// Endpoint for a user login @@ -796,3 +802,54 @@ pub async fn update_name_of_user( } } } + +/// Endpoint to get User Profile +#[utoipa::path( + delete, + path = super::endpoints::GET_USER_PROFILE, + params(("id" = uuid::Uuid, Path, description = "User Id")), + responses( + (status = 200, description = "Deleted", body = response::GetUserProfileResponse), + (status = 400, description = "Bad request", body = response::GetUserProfileResponse), + (status = 500, description = "Error", body = response::GetUserProfileResponse) + ) +)] +pub async fn get_user_profile( + axum::Extension(pool): axum::Extension, + axum::extract::Path(id): axum::extract::Path, +) -> ( + axum::http::StatusCode, + axum::Json, +) { + let mut response = response::GetUserProfileResponse::default(); + if id.is_nil() { + response.message = String::from("Invalid"); + return (axum::http::StatusCode::BAD_REQUEST, axum::Json(response)); + } + + match repo::user::get_with_id(&pool, &id).await { + Ok(user) => { + let user_profile = textsender_models::user::UserProfile { + user_id: user.id, + phone_number: user.phone_number, + firstname: user.firstname, + lastname: user.lastname, + last_login: user.last_login, + created: user.created, + username: user.username, + }; + response.message = String::from(super::messages::SUCCESSFUL_MESSAGE); + response.data.push(user_profile); + + (axum::http::StatusCode::OK, axum::Json(response)) + } + Err(err) => { + eprintln!("Error: {err:?}"); + response.message = String::from("Bad request"); + ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + axum::Json(response), + ) + } + } +} diff --git a/src/callers/mod.rs b/src/callers/mod.rs index 0dbf0a8..2e19209 100644 --- a/src/callers/mod.rs +++ b/src/callers/mod.rs @@ -19,4 +19,6 @@ pub mod endpoints { pub const UPDATE_PASSWORD: &str = "/api/v1/user/password/update"; /// Endpoint constant for updating user's name pub const UPDATE_USER_NAME: &str = "/api/v1/user/name/update"; + /// Endpoint constant for getting user profile + pub const GET_USER_PROFILE: &str = "/api/v1/user/profile/{id}"; } diff --git a/src/lib.rs b/src/lib.rs index d56cab5..763c3ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,9 +24,12 @@ pub mod init { paths( common_callers::endpoint::db_ping, common_callers::endpoint::root, register_caller::register_user, login_caller::user_login, + login_caller::get_user_profile, ), components(schemas(common_callers::response::TestResult, - register_responses::Response, login_responses::LoginResponse)), + register_responses::Response, login_responses::LoginResponse, + login_responses::GetUserProfileResponse, + )), tags( (name = "TextSender Auth API", description = "Auth API for TextSender API") ) @@ -39,6 +42,7 @@ pub mod init { let cors = tower_http::cors::CorsLayer::new() .allow_methods([ axum::http::Method::GET, + axum::http::Method::PATCH, axum::http::Method::POST, axum::http::Method::PUT, axum::http::Method::DELETE, @@ -76,6 +80,12 @@ pub mod init { cors.allow_origin(vec![ "http://localhost:4200".parse().unwrap(), "http://127.0.0.1:4200".parse().unwrap(), + "http://localhost:5173".parse().unwrap(), + "http://127.0.0.1:5173".parse().unwrap(), + "http://localhost:9080".parse().unwrap(), + "http://127.0.0.1:9080".parse().unwrap(), + "http://localhost:9081".parse().unwrap(), + "http://127.0.0.1:9081".parse().unwrap(), ]) } } @@ -118,6 +128,10 @@ pub mod init { callers::endpoints::UPDATE_USER_NAME, patch(callers::login::update_name_of_user), ) + .route( + callers::endpoints::GET_USER_PROFILE, + get(callers::login::get_user_profile), + ) .layer(cors::configure_cors().await) } diff --git a/tests/tests.rs b/tests/tests.rs index d1927a4..ba88e04 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -225,6 +225,29 @@ pub mod requests { Err(err) => Err(err), } } + + /// Function to call get user profile endpoint + pub async fn get_user_profile( + app: &axum::Router, + user_id: &uuid::Uuid, + ) -> Result { + let url = super::util::format_url_with_value( + textsender_auth::callers::endpoints::GET_USER_PROFILE, + user_id, + ); + match axum::http::Request::builder() + .method(axum::http::Method::GET) + .uri(url) + .header(axum::http::header::CONTENT_TYPE, "application/json") + .body(axum::body::Body::empty()) + { + Ok(req) => match app.clone().oneshot(req).await { + Ok(resp) => Ok(resp), + Err(err) => Err(axum::http::Error::from(err)), + }, + Err(err) => Err(err), + } + } } /// Test user firstname @@ -272,6 +295,11 @@ mod util { Err(err) => Err(std::io::Error::other(err.to_string())), } } + + pub fn format_url_with_value(endpoint: &str, value: &uuid::Uuid) -> String { + let last = endpoint.len() - 5; + format!("{}/{value}", &endpoint[0..last]) + } } mod flow { @@ -989,3 +1017,79 @@ async fn test_update_name_of_password() { } } } + +#[tokio::test] +async fn test_get_user_profile() { + let tm_pool = db_mgr::get_pool().await.unwrap(); + let db_name = db_mgr::generate_db_name().await; + + match db_mgr::create_database(&tm_pool, &db_name).await { + Ok(_) => { + println!("Success"); + } + Err(e) => { + assert!(false, "Error: {:?}", e.to_string()); + } + } + + let pool = db_mgr::connect_to_db(&db_name).await.unwrap(); + + db::init::migrations(&pool).await; + + let app = init::routes().await.layer(axum::Extension(pool)); + + let user_id = match flow::register_user(&app).await { + Ok(user) => match flow::login_user(&app, &user.username, TEST_PASSWORD).await { + Ok(login_result) => { + assert_eq!( + false, + login_result.access_token.is_empty(), + "Access token is empty when it should not be" + ); + login_result.user_id + } + Err(err) => { + assert!(false, "Error: {err:?}"); + uuid::Uuid::nil() + } + }, + Err(err) => { + assert!(false, "Error: {err:?}"); + uuid::Uuid::nil() + } + }; + + assert_eq!(false, user_id.is_nil(), "User Id should not be empty"); + + match requests::get_user_profile(&app, &user_id).await { + Ok(response) => { + match util::convert_response::< + textsender_auth::callers::login::response::GetUserProfileResponse, + >(response) + .await + { + Ok(response) => { + assert_eq!(false, response.data.is_empty(), "No User Profile found"); + let user_profile = &response.data[0]; + assert_eq!( + TEST_USERNAME, user_profile.username, + "Username does not match" + ); + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + } + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } + + match db_mgr::drop_database(&tm_pool, &db_name).await { + Ok(()) => {} + Err(err) => { + assert!(false, "Error: {err:?}"); + } + } +}