diff --git a/examples/mysql/tests/guard_mutation_tests.rs b/examples/mysql/tests/guard_mutation_tests.rs new file mode 100644 index 00000000..65ca6e71 --- /dev/null +++ b/examples/mysql/tests/guard_mutation_tests.rs @@ -0,0 +1,169 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection}; +use seaography::{Builder, BuilderContext, FnGuard, GuardsConfig}; +use seaography_mysql_example::entities::*; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.name".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + seaography::register_entities!( + builder, + [ + actor, + address, + category, + city, + country, + customer, + film, + film_actor, + film_category, + film_text, + inventory, + language, + payment, + rental, + staff, + store, + ] + ); + builder.register_enumeration::(); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard_mutation() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate( + data: { lastUpdate: "2030-01-01 11:11:11 UTC" } + filter: { languageId: { eq: 6 } } + ) { + languageId + } + } + "#, + ) + .await, + r#" + { + "languageUpdate": [ + { + "languageId": 6 + } + ] + } + "#, + ); + + let response = schema + .execute( + r#" + mutation FilmCategoryUpdate { + filmCategoryUpdate( + data: { filmId: 1, categoryId: 1, lastUpdate: "2030-01-01 11:11:11 UTC" } + ) { + filmId + } + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); + + let response = schema + .execute( + r#" + mutation FilmCategoryDelete { + filmCategoryDelete(filter: { filmId: { eq: 2 } }) + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard_mutation() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate(data: { name: "Cantonese" }, filter: { languageId: { eq: 6 } }) { + languageId + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/mysql/tests/guard_tests.rs b/examples/mysql/tests/guard_tests.rs new file mode 100644 index 00000000..3bfa6b0c --- /dev/null +++ b/examples/mysql/tests/guard_tests.rs @@ -0,0 +1,387 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection, RelationTrait}; +use seaography::{ + Builder, BuilderContext, EntityObjectRelationBuilder, EntityObjectViaRelationBuilder, FnGuard, + GuardsConfig, +}; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.lastUpdate".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + let entity_object_relation_builder = EntityObjectRelationBuilder { context: &CONTEXT }; + let entity_object_via_relation_builder = EntityObjectViaRelationBuilder { context: &CONTEXT }; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "actor", + seaography_mysql_example::entities::film_actor::Relation::Actor.def(), + ), + entity_object_relation_builder + .get_relation::( + "film", + seaography_mysql_example::entities::film_actor::Relation::Film.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::rental::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_mysql_example::entities::rental::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_mysql_example::entities::rental::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::rental::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::( + "film", + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::staff::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_mysql_example::entities::staff::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::staff::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "selfRef", + seaography_mysql_example::entities::staff::Relation::SelfRef.def(), + ), + entity_object_relation_builder + .get_relation::( + "selfRefReverse", + seaography_mysql_example::entities::staff::Relation::SelfRef.def().rev(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::staff::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_mysql_example::entities::country::Relation::City.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("actor"), + entity_object_via_relation_builder + .get_relation::( + "category", + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_mysql_example::entities::film::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "language1", + seaography_mysql_example::entities::film::Relation::Language1.def(), + ), + entity_object_relation_builder + .get_relation::( + "language2", + seaography_mysql_example::entities::film::Relation::Language2.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("film"), + ]); + builder.register_entity::(vec![]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::city::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "country", + seaography_mysql_example::entities::city::Relation::Country.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "film", + seaography_mysql_example::entities::inventory::Relation::Film.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::inventory::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::inventory::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![]); + builder . register_entity :: < seaography_mysql_example:: entities :: film_category :: Entity > (vec ! [entity_object_relation_builder . get_relation :: < seaography_mysql_example:: entities :: film_category :: Entity , seaography_mysql_example:: entities :: category :: Entity > ("category" , seaography_mysql_example:: entities :: film_category :: Relation :: Category . def ()) , entity_object_relation_builder . get_relation :: < seaography_mysql_example:: entities :: film_category :: Entity , seaography_mysql_example:: entities :: film :: Entity > ("film" , seaography_mysql_example:: entities :: film_category :: Relation :: Film . def ())]) ; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::customer::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_mysql_example::entities::customer::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::customer::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::customer::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_mysql_example::entities::store::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::store::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_mysql_example::entities::store::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::store::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::payment::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_mysql_example::entities::payment::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::payment::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_mysql_example::entities::address::Relation::City.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_mysql_example::entities::address::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_mysql_example::entities::address::Relation::Staff.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_mysql_example::entities::address::Relation::Store.def(), + ), + ]); + builder + .register_enumeration::(); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("mysql://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + language { + nodes { + languageId + name + } + } + } + "#, + ) + .await, + r#" + { + "language": { + "nodes": [ + { + "languageId": 1, + "name": "English" + }, + { + "languageId": 2, + "name": "Italian" + }, + { + "languageId": 3, + "name": "Japanese" + }, + { + "languageId": 4, + "name": "Mandarin" + }, + { + "languageId": 5, + "name": "French" + }, + { + "languageId": 6, + "name": "German" + } + ] + } + } + "#, + ); + + let response = schema + .execute( + r#" + { + filmCategory { + nodes { + filmId + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + { + language { + nodes { + languageId + name + lastUpdate + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/postgres/tests/guard_mutation_tests.rs b/examples/postgres/tests/guard_mutation_tests.rs new file mode 100644 index 00000000..d533ed00 --- /dev/null +++ b/examples/postgres/tests/guard_mutation_tests.rs @@ -0,0 +1,168 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection}; +use seaography::{Builder, BuilderContext, FnGuard, GuardsConfig}; +use seaography_postgres_example::entities::*; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.name".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + seaography::register_entities!( + builder, + [ + actor, + address, + category, + city, + country, + customer, + film, + film_actor, + film_category, + inventory, + language, + payment, + rental, + staff, + store, + ] + ); + builder.register_enumeration::(); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard_mutation() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate( + data: { lastUpdate: "2030-01-01 11:11:11" } + filter: { languageId: { eq: 6 } } + ) { + languageId + } + } + "#, + ) + .await, + r#" + { + "languageUpdate": [ + { + "languageId": 6 + } + ] + } + "#, + ); + + let response = schema + .execute( + r#" + mutation FilmCategoryUpdate { + filmCategoryUpdate( + data: { filmId: 1, categoryId: 1, lastUpdate: "2030-01-01 11:11:11" } + ) { + filmId + } + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); + + let response = schema + .execute( + r#" + mutation FilmCategoryDelete { + filmCategoryDelete(filter: { filmId: { eq: 2 } }) + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard_mutation() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + mutation LanguageUpdate { + languageUpdate(data: { name: "Cantonese" }, filter: { languageId: { eq: 6 } }) { + languageId + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/postgres/tests/guard_tests.rs b/examples/postgres/tests/guard_tests.rs new file mode 100644 index 00000000..45ba63e4 --- /dev/null +++ b/examples/postgres/tests/guard_tests.rs @@ -0,0 +1,375 @@ +use std::collections::BTreeMap; + +use async_graphql::{dynamic::*, Response}; +use sea_orm::{Database, DatabaseConnection, RelationTrait}; +use seaography::{ + Builder, BuilderContext, EntityObjectRelationBuilder, EntityObjectViaRelationBuilder, FnGuard, + GuardsConfig, +}; + +lazy_static::lazy_static! { + static ref CONTEXT : BuilderContext = { + let context = BuilderContext::default(); + let mut entity_guards: BTreeMap = BTreeMap::new(); + entity_guards.insert("FilmCategory".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + let mut field_guards: BTreeMap = BTreeMap::new(); + field_guards.insert("Language.lastUpdate".into(), Box::new(|_ctx| { + seaography::GuardAction::Block(None) + })); + BuilderContext { + guards: GuardsConfig { + entity_guards, + field_guards, + }, + ..context + } + }; +} + +pub fn schema( + database: DatabaseConnection, + depth: Option, + complexity: Option, +) -> Result { + let mut builder = Builder::new(&CONTEXT, database.clone()); + let entity_object_relation_builder = EntityObjectRelationBuilder { context: &CONTEXT }; + let entity_object_via_relation_builder = EntityObjectViaRelationBuilder { context: &CONTEXT }; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "actor", + seaography_postgres_example::entities::film_actor::Relation::Actor.def(), + ), + entity_object_relation_builder + .get_relation::( + "film", + seaography_postgres_example::entities::film_actor::Relation::Film.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::rental::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_postgres_example::entities::rental::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_postgres_example::entities::rental::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::rental::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::( + "film", + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::staff::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_postgres_example::entities::staff::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::staff::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::staff::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_postgres_example::entities::country::Relation::City.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("actor"), + entity_object_via_relation_builder + .get_relation::( + "category", + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_postgres_example::entities::film::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "language1", + seaography_postgres_example::entities::film::Relation::Language1.def(), + ), + entity_object_relation_builder + .get_relation::( + "language2", + seaography_postgres_example::entities::film::Relation::Language2.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_via_relation_builder + .get_relation::("film"), + ]); + builder.register_entity::(vec![]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::city::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "country", + seaography_postgres_example::entities::city::Relation::Country.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "film", + seaography_postgres_example::entities::inventory::Relation::Film.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::inventory::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::inventory::Relation::Store.def(), + ), + ]); + builder . register_entity :: < seaography_postgres_example:: entities :: film_category :: Entity > (vec ! [entity_object_relation_builder . get_relation :: < seaography_postgres_example:: entities :: film_category :: Entity , seaography_postgres_example:: entities :: category :: Entity > ("category" , seaography_postgres_example:: entities :: film_category :: Relation :: Category . def ()) , entity_object_relation_builder . get_relation :: < seaography_postgres_example:: entities :: film_category :: Entity , seaography_postgres_example:: entities :: film :: Entity > ("film" , seaography_postgres_example:: entities :: film_category :: Relation :: Film . def ())]) ; + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::customer::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "payment", + seaography_postgres_example::entities::customer::Relation::Payment.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::customer::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::customer::Relation::Store.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "address", + seaography_postgres_example::entities::store::Relation::Address.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::store::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "inventory", + seaography_postgres_example::entities::store::Relation::Inventory.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::store::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::payment::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "rental", + seaography_postgres_example::entities::payment::Relation::Rental.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::payment::Relation::Staff.def(), + ), + ]); + builder.register_entity::(vec![ + entity_object_relation_builder + .get_relation::( + "city", + seaography_postgres_example::entities::address::Relation::City.def(), + ), + entity_object_relation_builder + .get_relation::( + "customer", + seaography_postgres_example::entities::address::Relation::Customer.def(), + ), + entity_object_relation_builder + .get_relation::( + "staff", + seaography_postgres_example::entities::address::Relation::Staff.def(), + ), + entity_object_relation_builder + .get_relation::( + "store", + seaography_postgres_example::entities::address::Relation::Store.def(), + ), + ]); + builder.register_enumeration::(); + let schema = builder.schema_builder(); + let schema = if let Some(depth) = depth { + schema.limit_depth(depth) + } else { + schema + }; + let schema = if let Some(complexity) = complexity { + schema.limit_complexity(complexity) + } else { + schema + }; + schema.data(database).finish() +} + +pub async fn get_schema() -> Schema { + let database = Database::connect("postgres://sea:sea@127.0.0.1/sakila") + .await + .unwrap(); + let schema = schema(database, None, None).unwrap(); + + schema +} + +pub fn assert_eq(a: Response, b: &str) { + assert_eq!( + a.data.into_json().unwrap(), + serde_json::from_str::(b).unwrap() + ) +} + +#[tokio::test] +async fn entity_guard() { + let schema = get_schema().await; + + assert_eq( + schema + .execute( + r#" + { + language(orderBy: { languageId: ASC }) { + nodes { + languageId + name + } + } + } + "#, + ) + .await, + r#" + { + "language": { + "nodes": [ + { + "languageId": 1, + "name": "English " + }, + { + "languageId": 2, + "name": "Italian " + }, + { + "languageId": 3, + "name": "Japanese " + }, + { + "languageId": 4, + "name": "Mandarin " + }, + { + "languageId": 5, + "name": "French " + }, + { + "languageId": 6, + "name": "German " + } + ] + } + } + "#, + ); + + let response = schema + .execute( + r#" + { + filmCategory { + nodes { + filmId + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); +} + +#[tokio::test] +async fn field_guard() { + let schema = get_schema().await; + + let response = schema + .execute( + r#" + { + language { + nodes { + languageId + name + lastUpdate + } + } + } + "#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Field guard triggered."); +} diff --git a/examples/sqlite/tests/guard_mutation_tests.rs b/examples/sqlite/tests/guard_mutation_tests.rs index ced4915a..e20d21f3 100644 --- a/examples/sqlite/tests/guard_mutation_tests.rs +++ b/examples/sqlite/tests/guard_mutation_tests.rs @@ -128,6 +128,20 @@ async fn entity_guard_mutation() { assert_eq!(response.errors.len(), 1); assert_eq!(response.errors[0].message, "Entity guard triggered."); + + let response = schema + .execute( + r#" + mutation FilmCategoryDelete { + filmCategoryDelete(filter: { filmId: { eq: 2 } }) + } +"#, + ) + .await; + + assert_eq!(response.errors.len(), 1); + + assert_eq!(response.errors[0].message, "Entity guard triggered."); } #[tokio::test] diff --git a/src/mutation/entity_delete_mutation.rs b/src/mutation/entity_delete_mutation.rs index a5a34995..200aad4a 100644 --- a/src/mutation/entity_delete_mutation.rs +++ b/src/mutation/entity_delete_mutation.rs @@ -5,7 +5,7 @@ use sea_orm::{ use crate::{ get_filter_conditions, BuilderContext, EntityObjectBuilder, EntityQueryFieldBuilder, - FilterInputBuilder, + FilterInputBuilder, GuardAction, }; /// The configuration structure of EntityDeleteMutationBuilder @@ -73,11 +73,25 @@ impl EntityDeleteMutationBuilder { let context = self.context; + let guard = self.context.guards.entity_guards.get(&object_name); + Field::new( self.type_name::(), TypeRef::named_nn(TypeRef::INT), move |ctx| { FieldFuture::new(async move { + let guard_flag = if let Some(guard) = guard { + (*guard)(&ctx) + } else { + GuardAction::Allow + }; + + if let GuardAction::Block(reason) = guard_flag { + return Err::, async_graphql::Error>(async_graphql::Error::new( + reason.unwrap_or("Entity guard triggered.".into()), + )); + } + let db = ctx.data::()?; let filters = ctx.args.get(&context.entity_delete_mutation.filter_field); diff --git a/src/outputs/connection_object.rs b/src/outputs/connection_object.rs index 87b77e35..642b47a7 100644 --- a/src/outputs/connection_object.rs +++ b/src/outputs/connection_object.rs @@ -110,7 +110,7 @@ impl ConnectionObjectBuilder { if let Some(value) = connection .pagination_info .as_ref() - .map(FieldValue::borrowed_any) + .map(|pagination_info| FieldValue::borrowed_any(pagination_info)) { Ok(Some(value)) } else {