Skip to content

Commit

Permalink
added get user by id endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
alexpitsikoulis committed Jul 16, 2024
1 parent f47e3ae commit a9ec8ae
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 2 deletions.
53 changes: 53 additions & 0 deletions src/domain/user/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use super::{Email, Handle, User};

#[derive(Deserialize, Serialize)]
pub struct GetUserResponse {
id: Uuid,
email: Email,
handle: Handle,
name: Option<String>,
profile_photo: Option<String>,
bio: Option<String>,
}

impl GetUserResponse {
pub fn id(&self) -> Uuid {
self.id
}

pub fn email(&self) -> Email {
self.email.clone()
}

pub fn handle(&self) -> Handle {
self.handle.clone()
}

pub fn name(&self) -> Option<String> {
self.name.clone()
}

pub fn profile_photo(&self) -> Option<String> {
self.profile_photo.clone()
}

pub fn bio(&self) -> Option<String> {
self.bio.clone()
}
}

impl From<User> for GetUserResponse {
fn from(user: User) -> Self {
GetUserResponse {
id: user.id,
email: user.email,
handle: user.handle,
name: user.name,
profile_photo: user.profile_photo,
bio: user.bio,
}
}
}
2 changes: 2 additions & 0 deletions src/domain/user/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod api;
mod credentials;
mod tests;

use actix_web::HttpResponse;
pub use api::GetUserResponse;
pub use credentials::{
deserialize_handle_option, deserialize_password_option, deserilaize_email_option, Email,
EmailValidationErr, Handle, HandleValidationErr, Login, Password, PasswordValidationErr,
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub mod health_check;
pub mod middleware;
pub mod server;
pub mod user;
pub mod user;
52 changes: 52 additions & 0 deletions src/handlers/user/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use actix_web::{
web::{Data, Path},
HttpResponse,
};
use sqlx::PgPool;
use uuid::Uuid;

use crate::{domain::user::GetUserResponse, storage};

#[tracing::instrument(
name = "Getting user by ID",
skip(user_id, db_pool),
fields(
id = %user_id,
),
)]
pub async fn get_by_id(user_id: Path<Uuid>, db_pool: Data<PgPool>) -> HttpResponse {
let id = user_id.into_inner();

match storage::get_user_by_id(&db_pool, id).await {
Ok(user) => {
if user.deleted_at().is_some() {
let err = format!("user {} has been soft deleted", id);
tracing::error!(err);
HttpResponse::BadRequest().body(err)
} else {
let response: GetUserResponse = user.into();
let response_str = match serde_json::to_string(&response) {
Ok(response_str) => response_str,
Err(e) => {
let err = format!("failed to parse user struct {} to json: {:?}", id, e);
tracing::error!(err);
return HttpResponse::InternalServerError().finish();
}
};
HttpResponse::Ok().body(response_str)
}
}
Err(e) => match e {
sqlx::Error::RowNotFound => {
let err = format!("user {} not found", id);
tracing::error!(err);
HttpResponse::NotFound().body(err)
}
e => {
let err = format!("failed to get user {}: {:?}", id, e);
tracing::error!(err);
HttpResponse::InternalServerError().finish()
}
},
}
}
2 changes: 2 additions & 0 deletions src/handlers/user/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod confirm;
mod delete;
mod get;
mod login;
mod signup;
mod update;

pub use confirm::*;
pub use delete::*;
pub use get::*;
pub use login::*;
pub use signup::*;
pub use update::*;
Expand Down
2 changes: 1 addition & 1 deletion src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::{
domain::{email, user::Email},
handlers::{
health_check::{health_check, HEALTH_CHECK_PATH},
middleware::AuthMiddleware,
server, user,
},
};
Expand Down Expand Up @@ -70,6 +69,7 @@ impl App {
)
.service(
scope("/{user_id}")
.route("", get().to(user::get_by_id))
.route("", put().to(user::update))
.route("", patch().to(user::patch))
.route("", delete().to(user::soft_delete))
Expand Down
86 changes: 86 additions & 0 deletions tests/api/user/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use claim::assert_ok;
use fake::{faker::internet::en::SafeEmail, Fake};
use muttr_server::{
domain::user::{self, GetUserResponse},
handlers::user::BASE_PATH,
};
use uuid::Uuid;

use crate::utils::{
app::TestApp,
http_client::{ContentType, Header, Path},
};

#[actix::test]
async fn test_get_by_id_success() {
let mut app = TestApp::spawn().await;

for x in 1..6 {
let email = assert_ok!(
user::Email::try_from(SafeEmail().fake::<String>()),
"failed to generated valid user email"
);
let handle = format!("test.user{}", x);
let user = app
.database
.insert_user(email.as_ref(), &handle, true)
.await;

let response = app
.client
.request(
Path::GET(format!("{}/{}", BASE_PATH, user.id())),
&[Header::ContentType(ContentType::Json)],
None::<String>,
)
.await;

assert_eq!(
200,
response.status(),
"The API did not return 200 on valid get user by id request: {}",
response.text().await.unwrap_or_default(),
);

let user_res = match response.json::<GetUserResponse>().await {
Ok(json) => json,
Err(e) => panic!(
"failed to unmarshal json from api response into GetUserResponse struct: {:?}",
e
),
};

assert_eq!(user.id(), user_res.id(), "id does not match",);
assert_eq!(user.email(), user_res.email(), "email does not match",);
assert_eq!(user.handle(), user_res.handle(), "handle does not match",);
assert_eq!(user.name(), user_res.name(), "name does not match",);
assert_eq!(
user.profile_photo(),
user_res.profile_photo(),
"profile_photo does not match",
);
assert_eq!(user.bio(), user_res.bio(), "bio does not match",);
}
}

#[actix::test]
async fn test_get_by_id_failure() {
let app = TestApp::spawn().await;

let id = Uuid::new_v4();

let user_res = app
.client
.request(
Path::GET(format!("{}/{}", BASE_PATH, id)),
&[Header::ContentType(ContentType::Json)],
None::<String>,
)
.await;

assert_eq!(
404,
user_res.status(),
"The API did not return 404 when trying to GET a non-existant user id",
);
}
1 change: 1 addition & 0 deletions tests/api/user/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod confirm;
mod delete;
mod get;
mod login;
mod patch;
mod signup;
Expand Down

0 comments on commit a9ec8ae

Please sign in to comment.