-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add index, read, update for agreements
- Loading branch information
1 parent
93db113
commit 911ca30
Showing
6 changed files
with
310 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
use super::error::{EntityApiErrorCode, Error}; | ||
use crate::uuid_parse_str; | ||
use entity::agreements::{self, ActiveModel, Entity, Model}; | ||
use entity::Id; | ||
use sea_orm::{ | ||
entity::prelude::*, | ||
ActiveValue::{Set, Unchanged}, | ||
DatabaseConnection, TryIntoModel, | ||
}; | ||
use std::collections::HashMap; | ||
|
||
use log::*; | ||
|
||
pub async fn create(db: &DatabaseConnection, agreement_model: Model) -> Result<Model, Error> { | ||
debug!("New Agreement Model to be inserted: {:?}", agreement_model); | ||
|
||
let now = chrono::Utc::now(); | ||
|
||
let agreement_active_model: ActiveModel = ActiveModel { | ||
coaching_session_id: Set(agreement_model.coaching_session_id), | ||
details: Set(agreement_model.details), | ||
user_id: Set(agreement_model.user_id), | ||
created_at: Set(now.into()), | ||
updated_at: Set(now.into()), | ||
..Default::default() | ||
}; | ||
|
||
Ok(agreement_active_model.save(db).await?.try_into_model()?) | ||
} | ||
|
||
pub async fn update(db: &DatabaseConnection, id: Id, model: Model) -> Result<Model, Error> { | ||
let result = Entity::find_by_id(id).one(db).await?; | ||
|
||
match result { | ||
Some(agreement) => { | ||
debug!("Existing Agreement model to be Updated: {:?}", agreement); | ||
|
||
let active_model: ActiveModel = ActiveModel { | ||
id: Unchanged(agreement.id), | ||
coaching_session_id: Unchanged(agreement.coaching_session_id), | ||
details: Set(model.details), | ||
user_id: Unchanged(agreement.user_id), | ||
updated_at: Set(chrono::Utc::now().into()), | ||
created_at: Unchanged(agreement.created_at), | ||
}; | ||
|
||
Ok(active_model.update(db).await?.try_into_model()?) | ||
} | ||
None => { | ||
debug!("Agreement with id {} not found", id); | ||
|
||
Err(Error { | ||
inner: None, | ||
error_code: EntityApiErrorCode::RecordNotFound, | ||
}) | ||
} | ||
} | ||
} | ||
|
||
pub async fn find_by( | ||
db: &DatabaseConnection, | ||
query_params: HashMap<String, String>, | ||
) -> Result<Vec<Model>, Error> { | ||
let mut query = Entity::find(); | ||
|
||
for (key, value) in query_params { | ||
match key.as_str() { | ||
"coaching_session_id" => { | ||
let coaching_session_id = uuid_parse_str(&value)?; | ||
|
||
query = query.filter(agreements::Column::CoachingSessionId.eq(coaching_session_id)); | ||
} | ||
_ => { | ||
return Err(Error { | ||
inner: None, | ||
error_code: EntityApiErrorCode::InvalidQueryTerm, | ||
}); | ||
} | ||
} | ||
} | ||
|
||
Ok(query.all(db).await?) | ||
} | ||
|
||
#[cfg(test)] | ||
// We need to gate seaORM's mock feature behind conditional compilation because | ||
// the feature removes the Clone trait implementation from seaORM's DatabaseConnection. | ||
// see https://github.com/SeaQL/sea-orm/issues/830 | ||
#[cfg(feature = "mock")] | ||
mod tests { | ||
use super::*; | ||
use entity::{agreements::Model, Id}; | ||
use sea_orm::{DatabaseBackend, MockDatabase, Transaction}; | ||
|
||
#[tokio::test] | ||
async fn create_returns_a_new_agreement_model() -> Result<(), Error> { | ||
let now = chrono::Utc::now(); | ||
|
||
let agreement_model = Model { | ||
id: Id::new_v4(), | ||
coaching_session_id: Id::new_v4(), | ||
details: Some("This is a agreement".to_owned()), | ||
user_id: Id::new_v4(), | ||
created_at: now.into(), | ||
updated_at: now.into(), | ||
}; | ||
|
||
let db = MockDatabase::new(DatabaseBackend::Postgres) | ||
.append_query_results(vec![vec![agreement_model.clone()]]) | ||
.into_connection(); | ||
|
||
let agreement = create(&db, agreement_model.clone().into()).await?; | ||
|
||
assert_eq!(agreement.id, agreement_model.id); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[tokio::test] | ||
async fn update_returns_an_updated_agreement_model() -> Result<(), Error> { | ||
let now = chrono::Utc::now(); | ||
|
||
let agreement_model = Model { | ||
id: Id::new_v4(), | ||
coaching_session_id: Id::new_v4(), | ||
details: Some("This is a agreement".to_owned()), | ||
user_id: Id::new_v4(), | ||
created_at: now.into(), | ||
updated_at: now.into(), | ||
}; | ||
|
||
let db = MockDatabase::new(DatabaseBackend::Postgres) | ||
.append_query_results(vec![vec![agreement_model.clone()], vec![agreement_model.clone()]]) | ||
.into_connection(); | ||
|
||
let agreement = update(&db, agreement_model.id, agreement_model.clone()).await?; | ||
|
||
assert_eq!(agreement.details, agreement_model.details); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[tokio::test] | ||
async fn find_by_returns_all_agreements_associated_with_coaching_session() -> Result<(), Error> { | ||
let db = MockDatabase::new(DatabaseBackend::Postgres).into_connection(); | ||
let mut query_params = HashMap::new(); | ||
let coaching_session_id = Id::new_v4(); | ||
|
||
query_params.insert( | ||
"coaching_session_id".to_owned(), | ||
coaching_session_id.to_string(), | ||
); | ||
|
||
let _ = find_by(&db, query_params).await; | ||
|
||
assert_eq!( | ||
db.into_transaction_log(), | ||
[Transaction::from_sql_and_values( | ||
DatabaseBackend::Postgres, | ||
r#"SELECT "agreements"."id", "agreements"."coaching_session_id", "agreements"."details", "agreements"."user_id", "agreements"."created_at", "agreements"."updated_at" FROM "refactor_platform"."agreements" WHERE "agreements"."coaching_session_id" = $1"#, | ||
[coaching_session_id.into()] | ||
)] | ||
); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use crate::controller::ApiResponse; | ||
use crate::extractors::{ | ||
authenticated_user::AuthenticatedUser, compare_api_version::CompareApiVersion, | ||
}; | ||
use crate::{AppState, Error}; | ||
use axum::extract::{Path, Query, State}; | ||
use axum::http::StatusCode; | ||
use axum::response::IntoResponse; | ||
use axum::Json; | ||
use entity::{agreements::Model, Id}; | ||
use entity_api::agreement as AgreementApi; | ||
use service::config::ApiVersion; | ||
use std::collections::HashMap; | ||
|
||
use log::*; | ||
|
||
/// POST create a new Agreement | ||
#[utoipa::path( | ||
post, | ||
path = "/agreements", | ||
params(ApiVersion), | ||
request_body = entity::agreements::Model, | ||
responses( | ||
(status = 201, description = "Successfully Created a New Agreement", body = [entity::agreements::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<AppState>, | ||
Json(agreement_model): Json<Model>, | ||
) -> Result<impl IntoResponse, Error> { | ||
debug!("POST Create a New Agreement from: {:?}", agreement_model); | ||
|
||
let agreement = AgreementApi::create(app_state.db_conn_ref(), agreement_model).await?; | ||
|
||
debug!("New Agreement: {:?}", agreement); | ||
|
||
Ok(Json(ApiResponse::new(StatusCode::CREATED.into(), agreement))) | ||
} | ||
|
||
#[utoipa::path( | ||
put, | ||
path = "/agreements/{id}", | ||
params( | ||
ApiVersion, | ||
("id" = Id, Path, description = "Id of agreement to update"), | ||
), | ||
request_body = entity::agreements::Model, | ||
responses( | ||
(status = 200, description = "Successfully Updated Agreement", body = [entity::agreements::Model]), | ||
(status = 401, description = "Unauthorized"), | ||
(status = 405, description = "Method not allowed") | ||
), | ||
security( | ||
("cookie_auth" = []) | ||
) | ||
)] | ||
pub async fn update( | ||
CompareApiVersion(_v): CompareApiVersion, | ||
AuthenticatedUser(_user): AuthenticatedUser, | ||
// TODO: create a new Extractor to authorize the user to access | ||
// the data requested | ||
State(app_state): State<AppState>, | ||
Path(id): Path<Id>, | ||
Json(agreement_model): Json<Model>, | ||
) -> Result<impl IntoResponse, Error> { | ||
debug!("PUT Update Agreement with id: {}", id); | ||
|
||
let agreement = AgreementApi::update(app_state.db_conn_ref(), id, agreement_model).await?; | ||
|
||
debug!("Updated Agreement: {:?}", agreement); | ||
|
||
Ok(Json(ApiResponse::new(StatusCode::OK.into(), agreement))) | ||
} | ||
|
||
#[utoipa::path( | ||
get, | ||
path = "/agreements", | ||
params( | ||
ApiVersion, | ||
("coaching_session_id" = Option<Id>, Query, description = "Filter by coaching_session_id") | ||
), | ||
responses( | ||
(status = 200, description = "Successfully retrieved all Agreements", body = [entity::agreements::Model]), | ||
(status = 401, description = "Unauthorized"), | ||
(status = 405, description = "Method not allowed") | ||
), | ||
security( | ||
("cookie_auth" = []) | ||
) | ||
)] | ||
pub async fn index( | ||
CompareApiVersion(_v): CompareApiVersion, | ||
AuthenticatedUser(_user): AuthenticatedUser, | ||
// TODO: create a new Extractor to authorize the user to access | ||
// the data requested | ||
State(app_state): State<AppState>, | ||
Query(params): Query<HashMap<String, String>>, | ||
) -> Result<impl IntoResponse, Error> { | ||
debug!("GET all Agreements"); | ||
debug!("Filter Params: {:?}", params); | ||
|
||
let agreements = AgreementApi::find_by(app_state.db_conn_ref(), params).await?; | ||
|
||
debug!("Found Agreements: {:?}", agreements); | ||
|
||
Ok(Json(ApiResponse::new( | ||
StatusCode::OK.into(), | ||
agreements, | ||
))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters