From e52a183ab7acc3143aeb0184d04b1078196e8867 Mon Sep 17 00:00:00 2001 From: Guillaume Deconinck <113656593+GuillaumeDecMeetsMore@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:09:37 +0900 Subject: [PATCH] feat: add other routes (#163) * feat: add events groups * chore: add generate type * feat: start adding infra & endpoints for groups * chore: remove unused files for now * test: add basic infra tests * feat: add api structs + "create" route * feat: add other routes * Update crates/api/src/event_group/update_event_group.rs --- .../api/src/event_group/delete_event_group.rs | 87 ++++++++++++++ crates/api/src/event_group/get_event_group.rs | 77 ++++++++++++ .../get_event_group_by_external_id.rs | 76 ++++++++++++ crates/api/src/event_group/mod.rs | 34 +++++- .../api/src/event_group/update_event_group.rs | 112 ++++++++++++++++++ 5 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 crates/api/src/event_group/delete_event_group.rs create mode 100644 crates/api/src/event_group/get_event_group.rs create mode 100644 crates/api/src/event_group/get_event_group_by_external_id.rs create mode 100644 crates/api/src/event_group/update_event_group.rs diff --git a/crates/api/src/event_group/delete_event_group.rs b/crates/api/src/event_group/delete_event_group.rs new file mode 100644 index 00000000..2a86b31d --- /dev/null +++ b/crates/api/src/event_group/delete_event_group.rs @@ -0,0 +1,87 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use nittei_api_structs::delete_event_group::*; +use nittei_domain::{event_group::EventGroup, User, ID}; +use nittei_infra::NitteiContext; + +use crate::{ + error::NitteiError, + shared::{ + auth::{account_can_modify_event, account_can_modify_user, protect_account_route}, + usecase::{execute, UseCase}, + }, +}; + +pub async fn delete_event_group_admin_controller( + http_req: HttpRequest, + path_params: web::Path, + ctx: web::Data, +) -> Result { + let account = protect_account_route(&http_req, &ctx).await?; + let e = account_can_modify_event(&account, &path_params.event_group_id, &ctx).await?; + let user = account_can_modify_user(&account, &e.user_id, &ctx).await?; + + let usecase = DeleteEventGroupUseCase { + user, + event_group_id: e.id, + }; + + execute(usecase, &ctx) + .await + .map(|event| HttpResponse::Ok().json(APIResponse::new(event))) + .map_err(NitteiError::from) +} + +#[derive(Debug)] +pub struct DeleteEventGroupUseCase { + pub user: User, + pub event_group_id: ID, +} + +#[derive(Debug)] +pub enum UseCaseError { + NotFound(ID), + StorageError, +} + +impl From for NitteiError { + fn from(e: UseCaseError) -> Self { + match e { + UseCaseError::StorageError => Self::InternalError, + UseCaseError::NotFound(event_group_id) => Self::NotFound(format!( + "The event group with id: {}, was not found.", + event_group_id + )), + } + } +} + +#[async_trait::async_trait(?Send)] +impl UseCase for DeleteEventGroupUseCase { + type Response = EventGroup; + + type Error = UseCaseError; + + const NAME: &'static str = "DeleteEvent"; + + // TODO: use only one db call + async fn execute(&mut self, ctx: &NitteiContext) -> Result { + let event_group = ctx + .repos + .event_groups + .find(&self.event_group_id) + .await + .map_err(|_| UseCaseError::StorageError)?; + let g = match event_group { + Some(g) if g.user_id == self.user.id => g, + _ => return Err(UseCaseError::NotFound(self.event_group_id.clone())), + }; + + ctx.repos + .event_groups + .delete(&g.id) + .await + .map_err(|_| UseCaseError::StorageError)?; + + Ok(g) + } +} diff --git a/crates/api/src/event_group/get_event_group.rs b/crates/api/src/event_group/get_event_group.rs new file mode 100644 index 00000000..66c57e23 --- /dev/null +++ b/crates/api/src/event_group/get_event_group.rs @@ -0,0 +1,77 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use nittei_api_structs::get_event_group::*; +use nittei_domain::{event_group::EventGroup, ID}; +use nittei_infra::NitteiContext; + +use crate::{ + error::NitteiError, + shared::{ + auth::{account_can_modify_event, protect_account_route}, + usecase::{execute, UseCase}, + }, +}; + +pub async fn get_event_group_admin_controller( + http_req: HttpRequest, + path_params: web::Path, + ctx: web::Data, +) -> Result { + let account = protect_account_route(&http_req, &ctx).await?; + let e = account_can_modify_event(&account, &path_params.event_group_id, &ctx).await?; + + let usecase = GetEventGroupUseCase { + user_id: e.user_id, + event_group_id: e.id, + }; + + execute(usecase, &ctx) + .await + .map(|event_group| HttpResponse::Ok().json(APIResponse::new(event_group))) + .map_err(NitteiError::from) +} + +#[derive(Debug)] +pub struct GetEventGroupUseCase { + pub event_group_id: ID, + pub user_id: ID, +} + +#[derive(Debug)] +pub enum UseCaseError { + InternalError, + NotFound(ID), +} + +impl From for NitteiError { + fn from(e: UseCaseError) -> Self { + match e { + UseCaseError::InternalError => Self::InternalError, + UseCaseError::NotFound(event_id) => Self::NotFound(format!( + "The event group with id: {}, was not found.", + event_id + )), + } + } +} + +#[async_trait::async_trait(?Send)] +impl UseCase for GetEventGroupUseCase { + type Response = EventGroup; + + type Error = UseCaseError; + + const NAME: &'static str = "GetEvent"; + + async fn execute(&mut self, ctx: &NitteiContext) -> Result { + let e = ctx + .repos + .event_groups + .find(&self.event_group_id) + .await + .map_err(|_| UseCaseError::InternalError)?; + match e { + Some(event_group) if event_group.user_id == self.user_id => Ok(event_group), + _ => Err(UseCaseError::NotFound(self.event_group_id.clone())), + } + } +} diff --git a/crates/api/src/event_group/get_event_group_by_external_id.rs b/crates/api/src/event_group/get_event_group_by_external_id.rs new file mode 100644 index 00000000..3b901f16 --- /dev/null +++ b/crates/api/src/event_group/get_event_group_by_external_id.rs @@ -0,0 +1,76 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use nittei_api_structs::get_event_group_by_external_id::*; +use nittei_domain::{event_group::EventGroup, ID}; +use nittei_infra::NitteiContext; + +use crate::{ + error::NitteiError, + shared::{ + auth::protect_account_route, + usecase::{execute, UseCase}, + }, +}; + +pub async fn get_event_group_by_external_id_admin_controller( + http_req: HttpRequest, + path_params: web::Path, + ctx: web::Data, +) -> Result { + let account = protect_account_route(&http_req, &ctx).await?; + + let usecase = GetEventGroupByExternalIdUseCase { + account_id: account.id, + external_id: path_params.external_id.clone(), + }; + + execute(usecase, &ctx) + .await + .map(|event| HttpResponse::Ok().json(APIResponse::new(event))) + .map_err(NitteiError::from) +} + +#[derive(Debug)] +pub struct GetEventGroupByExternalIdUseCase { + pub external_id: String, + pub account_id: ID, +} + +#[derive(Debug)] +pub enum UseCaseError { + InternalError, + NotFound(String), +} + +impl From for NitteiError { + fn from(e: UseCaseError) -> Self { + match e { + UseCaseError::InternalError => Self::InternalError, + UseCaseError::NotFound(external_id) => Self::NotFound(format!( + "The event group with external_id: {}, was not found.", + external_id + )), + } + } +} + +#[async_trait::async_trait(?Send)] +impl UseCase for GetEventGroupByExternalIdUseCase { + type Response = EventGroup; + + type Error = UseCaseError; + + const NAME: &'static str = "GetEvent"; + + async fn execute(&mut self, ctx: &NitteiContext) -> Result { + let g = ctx + .repos + .event_groups + .get_by_external_id(&self.external_id) + .await + .map_err(|_| UseCaseError::InternalError)?; + match g { + Some(event_group) if event_group.account_id == self.account_id => Ok(event_group), + _ => Err(UseCaseError::NotFound(self.external_id.clone())), + } + } +} diff --git a/crates/api/src/event_group/mod.rs b/crates/api/src/event_group/mod.rs index de0fd4c9..a8b28766 100644 --- a/crates/api/src/event_group/mod.rs +++ b/crates/api/src/event_group/mod.rs @@ -1,13 +1,45 @@ mod create_event_group; +mod delete_event_group; +mod get_event_group; +mod get_event_group_by_external_id; +mod update_event_group; use actix_web::web; use create_event_group::create_event_group_admin_controller; +use delete_event_group::delete_event_group_admin_controller; +use get_event_group::get_event_group_admin_controller; +use get_event_group_by_external_id::get_event_group_by_external_id_admin_controller; +use update_event_group::update_event_group_admin_controller; // Configure the routes for the event_group module pub fn configure_routes(cfg: &mut web::ServiceConfig) { - // Create an event for a user (admin route) + // Create an event group for a user (admin route) cfg.route( "/user/{user_id}/event_group", web::post().to(create_event_group_admin_controller), ); + + // Get a specific event group by external id + cfg.route( + "/user/event_groups/external_id/{external_id}", + web::get().to(get_event_group_by_external_id_admin_controller), + ); + + // Get a specific event group by uid (admin route) + cfg.route( + "/user/event_groups/{event_group_id}", + web::get().to(get_event_group_admin_controller), + ); + + // Update an event group by uid (admin route) + cfg.route( + "/user/event_grups/{event_group_id}", + web::put().to(update_event_group_admin_controller), + ); + + // Delete an event group by uid (admin route) + cfg.route( + "/user/event_groups/{event_group_id}", + web::delete().to(delete_event_group_admin_controller), + ); } diff --git a/crates/api/src/event_group/update_event_group.rs b/crates/api/src/event_group/update_event_group.rs new file mode 100644 index 00000000..32e12889 --- /dev/null +++ b/crates/api/src/event_group/update_event_group.rs @@ -0,0 +1,112 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use nittei_api_structs::update_event_group::*; +use nittei_domain::{event_group::EventGroup, User, ID}; +use nittei_infra::NitteiContext; + +use crate::{ + error::NitteiError, + shared::{ + auth::{account_can_modify_event, account_can_modify_user, protect_account_route}, + usecase::{execute, UseCase}, + }, +}; + +pub async fn update_event_group_admin_controller( + http_req: HttpRequest, + body: web::Json, + path_params: web::Path, + ctx: web::Data, +) -> Result { + let account = protect_account_route(&http_req, &ctx).await?; + let e = account_can_modify_event(&account, &path_params.event_group_id, &ctx).await?; + let user = account_can_modify_user(&account, &e.user_id, &ctx).await?; + + let body = body.0; + let usecase = UpdateEventGroupUseCase { + user, + event_group_id: e.id, + parent_id: body.parent_id, + external_id: body.external_id, + }; + + execute(usecase, &ctx) + .await + .map(|event| HttpResponse::Ok().json(APIResponse::new(event))) + .map_err(NitteiError::from) +} + +#[derive(Debug, Default)] +pub struct UpdateEventGroupUseCase { + pub user: User, + pub event_group_id: ID, + + pub parent_id: Option, + pub external_id: Option, +} + +#[derive(Debug)] +pub enum UseCaseError { + NotFound(String, ID), + StorageError, +} + +impl From for NitteiError { + fn from(e: UseCaseError) -> Self { + match e { + UseCaseError::NotFound(entity, event_id) => Self::NotFound(format!( + "The {} with id: {}, was not found.", + entity, event_id + )), + UseCaseError::StorageError => Self::InternalError, + } + } +} + +#[async_trait::async_trait(?Send)] +impl UseCase for UpdateEventGroupUseCase { + type Response = EventGroup; + + type Error = UseCaseError; + + const NAME: &'static str = "UpdateGroupEvent"; + + async fn execute(&mut self, ctx: &NitteiContext) -> Result { + let UpdateEventGroupUseCase { + user, + event_group_id, + parent_id, + external_id, + } = self; + + let mut g = match ctx.repos.event_groups.find(event_group_id).await { + Ok(Some(event_group)) if event_group.user_id == user.id => event_group, + Ok(_) => { + return Err(UseCaseError::NotFound( + "Calendar Event".into(), + event_group_id.clone(), + )) + } + Err(e) => { + tracing::error!("Failed to get one event {:?}", e); + return Err(UseCaseError::StorageError); + } + }; + + if parent_id.is_some() { + g.parent_id.clone_from(parent_id); + } + + if external_id.is_some() { + g.external_id.clone_from(external_id); + } + + // e.updated = ctx.sys.get_timestamp_millis(); + + ctx.repos + .event_groups + .save(&g) + .await + .map(|_| g.clone()) + .map_err(|_| UseCaseError::StorageError) + } +}