Skip to content

Commit

Permalink
Cont. feat: expose database connection to ActiveModelBehaviour's me…
Browse files Browse the repository at this point in the history
…thods (SeaQL#1328)

* feat: expose database connection to `ActiveModelBehaviour`'s methods (SeaQL#1145)

* Make ActiveModelTrait async

* Add tests

* refactoring

Co-authored-by: teenjuna <[email protected]>
  • Loading branch information
2 people authored and denwong47 committed Jan 20, 2023
1 parent 92660fd commit 5347dfb
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 21 deletions.
36 changes: 25 additions & 11 deletions src/entity/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,11 @@ pub trait ActiveModelTrait: Clone + Debug {
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait,
{
let am = ActiveModelBehavior::before_save(self, true)?;
let am = ActiveModelBehavior::before_save(self, db, true).await?;
let model = <Self::Entity as EntityTrait>::insert(am)
.exec_with_returning(db)
.await?;
Self::after_save(model, true)
Self::after_save(model, db, true).await
}

/// Perform the `UPDATE` operation on an ActiveModel
Expand Down Expand Up @@ -413,9 +413,9 @@ pub trait ActiveModelTrait: Clone + Debug {
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait,
{
let am = ActiveModelBehavior::before_save(self, false)?;
let am = ActiveModelBehavior::before_save(self, db, false).await?;
let model: <Self::Entity as EntityTrait>::Model = Self::Entity::update(am).exec(db).await?;
Self::after_save(model, false)
Self::after_save(model, db, false).await
}

/// Insert the model if primary key is `NotSet`, update otherwise.
Expand Down Expand Up @@ -490,10 +490,10 @@ pub trait ActiveModelTrait: Clone + Debug {
Self: ActiveModelBehavior + 'a,
C: ConnectionTrait,
{
let am = ActiveModelBehavior::before_delete(self)?;
let am = ActiveModelBehavior::before_delete(self, db).await?;
let am_clone = am.clone();
let delete_res = Self::Entity::delete(am).exec(db).await?;
ActiveModelBehavior::after_delete(am_clone)?;
ActiveModelBehavior::after_delete(am_clone, db).await?;
Ok(delete_res)
}

Expand Down Expand Up @@ -597,32 +597,46 @@ pub trait ActiveModelTrait: Clone + Debug {
/// ```
/// See module level docs [crate::entity] for a full example
#[allow(unused_variables)]
#[async_trait]
pub trait ActiveModelBehavior: ActiveModelTrait {
/// Create a new ActiveModel with default values. Also used by `Default::default()`.
fn new() -> Self {
<Self as ActiveModelTrait>::default()
}

/// Will be called before saving
fn before_save(self, insert: bool) -> Result<Self, DbErr> {
async fn before_save<C>(self, db: &C, insert: bool) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}

/// Will be called after saving
fn after_save(
async fn after_save<C>(
model: <Self::Entity as EntityTrait>::Model,
db: &C,
insert: bool,
) -> Result<<Self::Entity as EntityTrait>::Model, DbErr> {
) -> Result<<Self::Entity as EntityTrait>::Model, DbErr>
where
C: ConnectionTrait,
{
Ok(model)
}

/// Will be called before deleting
fn before_delete(self) -> Result<Self, DbErr> {
async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}

/// Will be called after deleting
fn after_delete(self) -> Result<Self, DbErr> {
async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
Ok(self)
}
}
Expand Down
23 changes: 18 additions & 5 deletions tests/common/bakery_chain/cake.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use sea_orm::entity::prelude::*;
use sea_orm::{entity::prelude::*, ConnectionTrait};

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "cake")]
Expand Down Expand Up @@ -49,6 +49,7 @@ impl Related<super::lineitem::Entity> for Entity {
}
}

#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {
fn new() -> Self {
use sea_orm::Set;
Expand All @@ -58,7 +59,10 @@ impl ActiveModelBehavior for ActiveModel {
}
}

fn before_save(self, insert: bool) -> Result<Self, DbErr> {
async fn before_save<C>(self, _db: &C, insert: bool) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
use rust_decimal_macros::dec;
if self.price.as_ref() == &dec!(0) {
Err(DbErr::Custom(format!(
Expand All @@ -70,7 +74,10 @@ impl ActiveModelBehavior for ActiveModel {
}
}

fn after_save(model: Model, insert: bool) -> Result<Model, DbErr> {
async fn after_save<C>(model: Model, _db: &C, insert: bool) -> Result<Model, DbErr>
where
C: ConnectionTrait,
{
use rust_decimal_macros::dec;
if model.price < dec!(0) {
Err(DbErr::Custom(format!(
Expand All @@ -82,7 +89,10 @@ impl ActiveModelBehavior for ActiveModel {
}
}

fn before_delete(self) -> Result<Self, DbErr> {
async fn before_delete<C>(self, _db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
if self.name.as_ref().contains("(err_on_before_delete)") {
Err(DbErr::Custom(
"[before_delete] Cannot be deleted".to_owned(),
Expand All @@ -92,7 +102,10 @@ impl ActiveModelBehavior for ActiveModel {
}
}

fn after_delete(self) -> Result<Self, DbErr> {
async fn after_delete<C>(self, _db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
if self.name.as_ref().contains("(err_on_after_delete)") {
Err(DbErr::Custom("[after_delete] Cannot be deleted".to_owned()))
} else {
Expand Down
15 changes: 15 additions & 0 deletions tests/common/features/edit_log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "edit_log")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub action: String,
pub values: Json,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}
2 changes: 2 additions & 0 deletions tests/common/features/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod applog;
pub mod byte_primary_key;
pub mod collection;
pub mod custom_active_model;
pub mod edit_log;
pub mod event_trigger;
pub mod insert_default;
pub mod json_struct;
Expand All @@ -23,6 +24,7 @@ pub use active_enum_child::Entity as ActiveEnumChild;
pub use applog::Entity as Applog;
pub use byte_primary_key::Entity as BytePrimaryKey;
pub use collection::Entity as Collection;
pub use edit_log::Entity as EditLog;
pub use event_trigger::Entity as EventTrigger;
pub use insert_default::Entity as InsertDefault;
pub use json_struct::Entity as JsonStruct;
Expand Down
61 changes: 58 additions & 3 deletions tests/common/features/repository.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use sea_orm::entity::prelude::*;
use super::edit_log;
use sea_orm::{entity::prelude::*, ConnectionTrait, Set, TryIntoModel};
use serde::Serialize;

#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize)]
#[sea_orm(table_name = "repository")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
Expand All @@ -13,4 +15,57 @@ pub struct Model {
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}
#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {
async fn before_save<C>(self, db: &C, _: bool) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
let model = self.clone().try_into_model()?;
insert_edit_log("before_save", &model, db).await?;
Ok(self)
}

async fn after_save<C>(model: Model, db: &C, _: bool) -> Result<Model, DbErr>
where
C: ConnectionTrait,
{
insert_edit_log("after_save", &model, db).await?;
Ok(model)
}

async fn before_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
let model = self.clone().try_into_model()?;
insert_edit_log("before_delete", &model, db).await?;
Ok(self)
}

async fn after_delete<C>(self, db: &C) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
let model = self.clone().try_into_model()?;
insert_edit_log("after_delete", &model, db).await?;
Ok(self)
}
}

async fn insert_edit_log<T, M, C>(action: T, model: &M, db: &C) -> Result<(), DbErr>
where
T: Into<String>,
M: Serialize,
C: ConnectionTrait,
{
edit_log::ActiveModel {
action: Set(action.into()),
values: Set(serde_json::json!(model)),
..Default::default()
}
.insert(db)
.await?;

Ok(())
}
18 changes: 18 additions & 0 deletions tests/common/features/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> {
create_insert_default_table(db).await?;
create_pi_table(db).await?;
create_uuid_fmt_table(db).await?;
create_edit_log_table(db).await?;

if DbBackend::Postgres == db_backend {
create_collection_table(db).await?;
Expand Down Expand Up @@ -480,3 +481,20 @@ pub async fn create_uuid_fmt_table(db: &DbConn) -> Result<ExecResult, DbErr> {

create_table(db, &stmt, UuidFmt).await
}

pub async fn create_edit_log_table(db: &DbConn) -> Result<ExecResult, DbErr> {
let stmt = sea_query::Table::create()
.table(edit_log::Entity)
.col(
ColumnDef::new(edit_log::Column::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(edit_log::Column::Action).string().not_null())
.col(ColumnDef::new(edit_log::Column::Values).json().not_null())
.to_owned();

create_table(db, &stmt, EditLog).await
}
53 changes: 51 additions & 2 deletions tests/string_primary_key_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod common;
pub use common::{features::*, setup::*, TestContext};
use pretty_assertions::assert_eq;
use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection};
use serde_json::json;

#[sea_orm_macros::test]
#[cfg(any(
Expand All @@ -14,13 +15,13 @@ async fn main() -> Result<(), DbErr> {
let ctx = TestContext::new("features_schema_string_primary_key_tests").await;
create_tables(&ctx.db).await?;
create_and_update_repository(&ctx.db).await?;
insert_repository(&ctx.db).await?;
insert_and_delete_repository(&ctx.db).await?;
ctx.delete().await;

Ok(())
}

pub async fn insert_repository(db: &DatabaseConnection) -> Result<(), DbErr> {
pub async fn insert_and_delete_repository(db: &DatabaseConnection) -> Result<(), DbErr> {
let repository = repository::Model {
id: "unique-id-001".to_owned(),
owner: "GC".to_owned(),
Expand All @@ -41,6 +42,54 @@ pub async fn insert_repository(db: &DatabaseConnection) -> Result<(), DbErr> {
}
);

result.delete(db).await?;

assert_eq!(
edit_log::Entity::find().all(db).await?,
[
edit_log::Model {
id: 1,
action: "before_save".into(),
values: json!({
"description": null,
"id": "unique-id-001",
"name": "G.C.",
"owner": "GC",
}),
},
edit_log::Model {
id: 2,
action: "after_save".into(),
values: json!({
"description": null,
"id": "unique-id-001",
"name": "G.C.",
"owner": "GC",
}),
},
edit_log::Model {
id: 3,
action: "before_delete".into(),
values: json!({
"description": null,
"id": "unique-id-001",
"name": "G.C.",
"owner": "GC",
}),
},
edit_log::Model {
id: 4,
action: "after_delete".into(),
values: json!({
"description": null,
"id": "unique-id-001",
"name": "G.C.",
"owner": "GC",
}),
},
]
);

Ok(())
}

Expand Down

0 comments on commit 5347dfb

Please sign in to comment.