diff --git a/entity/src/coaching_sessions.rs b/entity/src/coaching_sessions.rs index 30dfae0..5bf25d2 100644 --- a/entity/src/coaching_sessions.rs +++ b/entity/src/coaching_sessions.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize, ToSchema)] +#[schema(as = entity::coaching_sessions::Model)] #[sea_orm(schema_name = "refactor_platform", table_name = "coaching_sessions")] pub struct Model { #[sea_orm(primary_key)] diff --git a/entity_api/src/coaching_session.rs b/entity_api/src/coaching_session.rs index a41d19a..c26d1b5 100644 --- a/entity_api/src/coaching_session.rs +++ b/entity_api/src/coaching_session.rs @@ -1,9 +1,36 @@ use super::error::{EntityApiErrorCode, Error}; use crate::{naive_date_parse_str, uuid_parse_str}; -use entity::coaching_sessions::{self, Entity, Model}; -use sea_orm::{entity::prelude::*, DatabaseConnection}; +use entity::coaching_sessions::{self, ActiveModel, Entity, Model}; +use log::debug; +use sea_orm::{entity::prelude::*, DatabaseConnection, Set, TryIntoModel}; use std::collections::HashMap; +pub async fn create( + db: &DatabaseConnection, + coaching_session_model: Model, +) -> Result { + debug!( + "New Coaching Session Model to be inserted: {:?}", + coaching_session_model + ); + + let now = chrono::Utc::now(); + + let coaching_session_active_model: ActiveModel = ActiveModel { + coaching_relationship_id: Set(coaching_session_model.coaching_relationship_id), + date: Set(coaching_session_model.date), + timezone: Set(coaching_session_model.timezone), + created_at: Set(now.into()), + updated_at: Set(now.into()), + ..Default::default() + }; + + Ok(coaching_session_active_model + .save(db) + .await? + .try_into_model()?) +} + pub async fn find_by( db: &DatabaseConnection, params: HashMap, @@ -49,6 +76,30 @@ mod tests { use entity::Id; use sea_orm::{DatabaseBackend, MockDatabase, Transaction}; + #[tokio::test] + async fn create_returns_a_new_coaching_session_model() -> Result<(), Error> { + let now = chrono::Utc::now(); + + let coaching_session_model = Model { + id: Id::new_v4(), + coaching_relationship_id: Id::new_v4(), + date: chrono::Local::now().naive_utc(), + timezone: "Americas/Chicago".to_owned(), + created_at: now.into(), + updated_at: now.into(), + }; + + let db = MockDatabase::new(DatabaseBackend::Postgres) + .append_query_results(vec![vec![coaching_session_model.clone()]]) + .into_connection(); + + let coaching_session = create(&db, coaching_session_model.clone().into()).await?; + + assert_eq!(coaching_session.id, coaching_session_model.id); + + Ok(()) + } + #[tokio::test] async fn find_by_coaching_relationships_returns_all_records_associated_with_coaching_relationship( ) -> Result<(), Error> { diff --git a/web/src/controller/coaching_session_controller.rs b/web/src/controller/coaching_session_controller.rs index d589c7f..8ebedfd 100644 --- a/web/src/controller/coaching_session_controller.rs +++ b/web/src/controller/coaching_session_controller.rs @@ -7,6 +7,7 @@ use axum::extract::{Query, State}; use axum::http::StatusCode; use axum::response::IntoResponse; use axum::Json; +use entity::coaching_sessions::Model; use entity_api::coaching_session as CoachingSessionApi; use service::config::ApiVersion; use std::collections::HashMap; @@ -51,3 +52,43 @@ pub async fn index( coaching_sessions, ))) } + +/// POST create a new Coaching Session +#[utoipa::path( + post, + path = "/coaching_sessions", + params(ApiVersion), + request_body = entity::coaching_sessions::Model, + responses( + (status = 201, description = "Successfully Created a new Coaching Session", body = [entity::coaching_sessions::Model]), + (status= 422, description = "Unprocessable Entity"), + (status = 401, description = "Unauthorized"), + (status = 405, description = "Method not allowed") + ), + security( + ("cookie_auth" = []) + ) +)] +pub async fn create( + CompareApiVersion(_v): CompareApiVersion, + AuthenticatedUser(_user): AuthenticatedUser, + // TODO: create a new Extractor to authorize the user to access + // the data requested + State(app_state): State, + Json(coaching_sessions_model): Json, +) -> Result { + debug!( + "POST Create a new Coaching Session from: {:?}", + coaching_sessions_model + ); + + let coaching_session = + CoachingSessionApi::create(app_state.db_conn_ref(), coaching_sessions_model).await?; + + debug!("New Coaching Session: {:?}", coaching_session); + + Ok(Json(ApiResponse::new( + StatusCode::CREATED.into(), + coaching_session, + ))) +} diff --git a/web/src/controller/overarching_goal_controller.rs b/web/src/controller/overarching_goal_controller.rs index 9cb568d..d5c6149 100644 --- a/web/src/controller/overarching_goal_controller.rs +++ b/web/src/controller/overarching_goal_controller.rs @@ -30,7 +30,6 @@ use log::*; ("cookie_auth" = []) ) )] - pub async fn create( CompareApiVersion(_v): CompareApiVersion, AuthenticatedUser(user): AuthenticatedUser, diff --git a/web/src/router.rs b/web/src/router.rs index d31d4cb..47b3fcc 100644 --- a/web/src/router.rs +++ b/web/src/router.rs @@ -37,6 +37,8 @@ use utoipa_rapidoc::RapiDoc; agreement_controller::index, agreement_controller::read, agreement_controller::delete, + coaching_session_controller::index, + coaching_session_controller::create, note_controller::create, note_controller::update, note_controller::index, @@ -46,6 +48,8 @@ use utoipa_rapidoc::RapiDoc; organization_controller::create, organization_controller::update, organization_controller::delete, + organization::coaching_relationship_controller::index, + organization::coaching_relationship_controller::read, overarching_goal_controller::create, overarching_goal_controller::update, overarching_goal_controller::index, @@ -53,9 +57,6 @@ use utoipa_rapidoc::RapiDoc; overarching_goal_controller::update_status, user_session_controller::login, user_session_controller::logout, - organization::coaching_relationship_controller::index, - organization::coaching_relationship_controller::read, - coaching_session_controller::index, ), components( schemas( @@ -103,8 +104,8 @@ pub fn define_routes(app_state: AppState) -> Router { .merge(note_routes(app_state.clone())) .merge(organization_coaching_relationship_routes(app_state.clone())) .merge(overarching_goal_routes(app_state.clone())) - .merge(session_routes()) - .merge(protected_routes()) + .merge(user_session_routes()) + .merge(user_session_protected_routes()) .merge(coaching_sessions_routes(app_state.clone())) // FIXME: protect the OpenAPI web UI .merge(RapiDoc::with_openapi("/api-docs/openapi2.json", ApiDoc::openapi()).path("/rapidoc")) @@ -204,6 +205,10 @@ pub fn overarching_goal_routes(app_state: AppState) -> Router { pub fn coaching_sessions_routes(app_state: AppState) -> Router { Router::new() + .route( + "/coaching_sessions", + post(coaching_session_controller::create), + ) .route( "/coaching_sessions", get(coaching_session_controller::index), @@ -212,14 +217,14 @@ pub fn coaching_sessions_routes(app_state: AppState) -> Router { .with_state(app_state) } -pub fn protected_routes() -> Router { +pub fn user_session_protected_routes() -> Router { Router::new() .route("/protected", get(user_session_controller::protected)) .route("/logout", get(user_session_controller::logout)) .route_layer(login_required!(Backend, login_url = "/login")) } -pub fn session_routes() -> Router { +pub fn user_session_routes() -> Router { Router::new().route("/login", post(user_session_controller::login)) }